From 220a4b6cb53f0d86a21db2bd9cf79548361ab6d3 Mon Sep 17 00:00:00 2001 From: berat-552 Date: Sat, 5 Jul 2025 17:19:41 +0100 Subject: [PATCH] refactor and add new make commands --- .dockerignore | 3 +- Makefile | 36 +++++++---------- README.md | 7 +++- scripts/build_app_exe.py | 18 +++++++++ scripts/clean_build_artifacts.py | 16 ++++++++ scripts/run-dev-unix.sh | 21 ++++++++++ run-dev.bat => scripts/run-dev-windows.bat | 0 scripts/start_dev_docker.py | 45 ++++++++++++++++++++++ scripts/start_dev_full.py | 37 ++++++++++++++++++ 9 files changed, 158 insertions(+), 25 deletions(-) create mode 100644 scripts/build_app_exe.py create mode 100644 scripts/clean_build_artifacts.py create mode 100644 scripts/run-dev-unix.sh rename run-dev.bat => scripts/run-dev-windows.bat (100%) create mode 100644 scripts/start_dev_docker.py create mode 100644 scripts/start_dev_full.py diff --git a/.dockerignore b/.dockerignore index b23567e..bf83e40 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,7 +6,8 @@ __pycache__/ .venv/ venv/ tests/ -run-dev.bat +# Ignore local development scripts (used only by Makefile) +scripts/* .DS_Store .git .gitignore diff --git a/Makefile b/Makefile index de2f05b..a65b277 100644 --- a/Makefile +++ b/Makefile @@ -12,8 +12,11 @@ run-backend: run-gui: python main.py -dev: - run-dev.bat +dev-windows: + cmd /C scripts\run-dev-windows.bat + +dev-unix: + bash ./scripts/run-dev-unix.sh test: pytest @@ -37,12 +40,7 @@ docker-run-clean: docker run --rm $(DOCKER_RUN_FLAGS) $(IMAGE_NAME) dev-docker: - @echo "Starting Docker container..." - docker run -d --rm --name $(CONTAINER_NAME) $(DOCKER_RUN_FLAGS) $(IMAGE_NAME) - @echo "Launching GUI..." - python main.py - @echo "Stopping Docker container..." - docker stop $(CONTAINER_NAME) > /dev/null || echo "⚠️ No running container found" + python scripts/start_dev_docker.py docker-stop: -@docker stop studyforge-api && echo Stopped Docker container. || echo No running container to stop. @@ -51,34 +49,26 @@ docker-remove: -@docker rm -f $(CONTAINER_NAME) && echo "Removed Docker container." || echo "No container to remove." # ==== Docker Compose Workflow ==== -.PHONY: compose-dev +.PHONY: compose-dev compose-down dev-full dev-full-down compose-dev: docker compose -f docker-compose.dev.yml up -.PHONY: compose-down - compose-down: docker compose -f docker-compose.dev.yml down - dev-full: - @echo Starting backend using Docker Compose... - cmd /C "start /B docker compose -f docker-compose.dev.yml up" - @timeout /T 3 > NUL - @echo Launching GUI (main.py)... - python main.py + python scripts/start_dev_full.py dev-full-down: @echo "Stopping backend (Docker Compose)..." docker compose -f docker-compose.dev.yml down # ==== Packaging ==== -.PHONY: package clean-dist - -package: - python -c "import os; os.environ['APP_ENV'] = 'prod'; import PyInstaller.__main__; PyInstaller.__main__.run(['--onefile', '--windowed', '--name', 'StudyForge', 'main.py'])" +.PHONY: build clean +build: + python scripts/build_app_exe.py -clean-dist: - python -c "import shutil, os; [shutil.rmtree(d, ignore_errors=True) for d in ['build', 'dist']]; [os.remove(f) for f in ['StudyForge.spec'] if os.path.exists(f)]" +clean: + python scripts/clean_build_artifacts.py \ No newline at end of file diff --git a/README.md b/README.md index f565401..fb2fe63 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,12 @@ PROD_API_URL=https://studyforge-api.onrender.com Use the batch script to launch everything: ```bash -run-dev.bat +run-dev-windows.bat # Windows +``` + +OR +```bash +run-dev-unix.sh # Unix ``` This will start the FastAPI backend in a new terminal window. diff --git a/scripts/build_app_exe.py b/scripts/build_app_exe.py new file mode 100644 index 0000000..5db0bb4 --- /dev/null +++ b/scripts/build_app_exe.py @@ -0,0 +1,18 @@ +import os +from PyInstaller.__main__ import run + + +def build_app_exe(): + os.environ["APP_ENV"] = "prod" + run([ + "--onefile", + "--windowed", + "--name", "StudyForge", + "main.py" + ]) + + +if __name__ == "__main__": + print("[Build] Packaging StudyForge as a Windows executable...") + build_app_exe() + print("[Build] Done. Check the dist/ folder.") \ No newline at end of file diff --git a/scripts/clean_build_artifacts.py b/scripts/clean_build_artifacts.py new file mode 100644 index 0000000..3b382b3 --- /dev/null +++ b/scripts/clean_build_artifacts.py @@ -0,0 +1,16 @@ +import shutil +import os + + +def clean_build_artifacts(): + for d in ["build", "dist"]: + shutil.rmtree(d, ignore_errors=True) + for f in ["StudyForge.spec"]: + if os.path.exists(f): + os.remove(f) + + print("Cleaned build artifacts") + + +if __name__ == "__main__": + clean_build_artifacts() diff --git a/scripts/run-dev-unix.sh b/scripts/run-dev-unix.sh new file mode 100644 index 0000000..9322b4b --- /dev/null +++ b/scripts/run-dev-unix.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Ensure script runs from project root regardless of where it's called +cd "$(dirname "$0")/.." || exit 1 + +if [ -f ".venv/Scripts/activate" ]; then + source .venv/Scripts/activate +elif [ -f ".venv/bin/activate" ]; then + source .venv/bin/activate +else + echo "[Error] Could not find virtual environment to activate." + exit 1 +fi + +echo "[Dev] Starting FastAPI backend..." +uvicorn backend.api:app --reload & + +sleep 2 + +echo "[Dev] Launching GUI..." +python main.py diff --git a/run-dev.bat b/scripts/run-dev-windows.bat similarity index 100% rename from run-dev.bat rename to scripts/run-dev-windows.bat diff --git a/scripts/start_dev_docker.py b/scripts/start_dev_docker.py new file mode 100644 index 0000000..eddf5a5 --- /dev/null +++ b/scripts/start_dev_docker.py @@ -0,0 +1,45 @@ +import subprocess +import sys +import os + +CONTAINER_NAME = "studyforge-api" +IMAGE_NAME = "studyforge-api" +DOCKER_RUN_FLAGS = ["-p", "8000:8000"] +ENV_VARS = ["COHERE_API_KEY"] + + +def build_env_flags(): + """Convert env vars into -e KEY=VAL flags.""" + flags = [] + for key in ENV_VARS: + val = os.getenv(key) + if val: + flags.extend(["-e", f"{key}={val}"]) + return flags + + +def start_container(): + print("[Docker] Starting container...") + cmd = [ + "docker", "run", "-d", "--rm", + "--name", CONTAINER_NAME, + *DOCKER_RUN_FLAGS, + *build_env_flags(), + IMAGE_NAME + ] + subprocess.run(cmd, check=True) + + +def stop_container(): + print("[Docker] Stopping container...") + try: + subprocess.run(["docker", "stop", CONTAINER_NAME], stdout=subprocess.DEVNULL, check=True) + except subprocess.CalledProcessError: + print("[Warning] Failed to stop container (it may not be running).") + except FileNotFoundError: + print("[Error] Docker is not installed or not found in PATH.") + + +if __name__ == "__main__": + start_container() + print(f"[Docker] Container '{CONTAINER_NAME}' is running.") \ No newline at end of file diff --git a/scripts/start_dev_full.py b/scripts/start_dev_full.py new file mode 100644 index 0000000..85e3cf6 --- /dev/null +++ b/scripts/start_dev_full.py @@ -0,0 +1,37 @@ +import subprocess +import time +import sys +import os + +DOCKER_COMPOSE_FILE = "docker-compose.dev.yml" +BACKEND_SERVICE_NAME = "studyforge-api" +GUI_ENTRY = "main.py" + + +def start_backend(): + print("[Start] Launching backend using Docker Compose...") + subprocess.Popen( + ["docker", "compose", "-f", DOCKER_COMPOSE_FILE, "up"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + shell=os.name == "nt" # only use shell=True on Windows + ) + time.sleep(3) # Allow time for the container to boot + + +def launch_gui(): + print("[Start] Launching GUI...") + subprocess.run([sys.executable, GUI_ENTRY]) + + +def stop_backend(): + print("[Stop] Stopping backend...") + subprocess.run(["docker", "compose", "-f", DOCKER_COMPOSE_FILE, "down"]) + + +if __name__ == "__main__": + try: + start_backend() + launch_gui() + finally: + stop_backend()