From b0d878daf201fb01633115b52b16a04f58976c98 Mon Sep 17 00:00:00 2001 From: rahulharpal1603 Date: Sat, 30 Aug 2025 12:11:08 +0530 Subject: [PATCH 1/2] Update build pipeline to support the microservice. --- .github/workflows/build-and-release.yml | 161 ++++++++++++++++++++++-- .github/workflows/pr-check-tests.yml | 12 +- backend/app/utils/microservice.py | 77 +++++++++++- sync-microservice/main.py | 6 + 4 files changed, 244 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index ddf0c69bb..896c1528d 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -134,37 +134,176 @@ jobs: name: PictoPy-MacOS path: backend/dist/PictoPy_Server/PictoPy-MacOS.zip + build-sync-microservice-windows: + runs-on: windows-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: 3.12 + + - name: Install dependencies + run: | + cd sync-microservice + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pyinstaller + + - name: Build executable with PyInstaller + run: | + cd sync-microservice + pyinstaller main.py --name PictoPy_Sync --onedir --distpath dist + + - name: Copy app folder + run: | + cd sync-microservice + robocopy app dist\PictoPy_Sync\app /e + if ($LASTEXITCODE -le 1) { exit 0 } + + - name: Create ZIP package + run: | + cd sync-microservice/dist/PictoPy_Sync + tar -a -c -f PictoPy-Sync-Windows.zip . + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: PictoPy-Sync-Windows + path: sync-microservice/dist/PictoPy_Sync/PictoPy-Sync-Windows.zip + + build-sync-microservice-ubuntu: + runs-on: ubuntu-22.04 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: 3.12 + + - name: Install dependencies + run: | + cd sync-microservice + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pyinstaller + + - name: Build executable with PyInstaller + run: | + cd sync-microservice + pyinstaller main.py --name PictoPy_Sync --onedir --distpath dist + + - name: Copy app folder + run: | + cd sync-microservice + mkdir -p dist/PictoPy_Sync/app + cp -r app/* dist/PictoPy_Sync/app/ + + - name: Create ZIP package + run: | + cd sync-microservice/dist/PictoPy_Sync + zip -r PictoPy-Sync-Ubuntu.zip . + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: PictoPy-Sync-Ubuntu + path: sync-microservice/dist/PictoPy_Sync/PictoPy-Sync-Ubuntu.zip + + build-sync-microservice-macos: + runs-on: macos-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: 3.12 + + - name: Install dependencies + run: | + cd sync-microservice + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pyinstaller + + - name: Build executable with PyInstaller + run: | + cd sync-microservice + pyinstaller main.py --name PictoPy_Sync --onedir --distpath dist + + - name: Copy app folder + run: | + cd sync-microservice + mkdir -p dist/PictoPy_Sync/app + cp -r app/* dist/PictoPy_Sync/app/ + + - name: Create ZIP package + run: | + cd sync-microservice/dist/PictoPy_Sync + zip -r PictoPy-Sync-MacOS.zip . + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: PictoPy-Sync-MacOS + path: sync-microservice/dist/PictoPy_Sync/PictoPy-Sync-MacOS.zip + publish-tauri: permissions: contents: write - needs: [build-server-windows, build-server-ubuntu, build-server-macos] + needs: + [ + build-server-windows, + build-server-ubuntu, + build-server-macos, + build-sync-microservice-windows, + build-sync-microservice-ubuntu, + build-sync-microservice-macos, + ] strategy: fail-fast: false matrix: include: - platform: "macos-latest" args: "--target aarch64-apple-darwin" - artifact: "PictoPy-MacOS" + server-artifact: "PictoPy-MacOS" + sync-artifact: "PictoPy-Sync-MacOS" - platform: "ubuntu-22.04" args: "" - artifact: "PictoPy-Ubuntu" + server-artifact: "PictoPy-Ubuntu" + sync-artifact: "PictoPy-Sync-Ubuntu" - platform: "windows-latest" args: "" - artifact: "PictoPy-Windows" + server-artifact: "PictoPy-Windows" + sync-artifact: "PictoPy-Sync-Windows" runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v4 - - name: Create dist directory + - name: Create dist directories shell: bash - run: mkdir -p backend/dist + run: | + mkdir -p backend/dist + mkdir -p sync-microservice/dist - name: Download and extract server artifact uses: actions/download-artifact@v4 with: - name: ${{ matrix.artifact }} + name: ${{ matrix.server-artifact }} path: backend/dist + - name: Download and extract sync microservice artifact + uses: actions/download-artifact@v4 + with: + name: ${{ matrix.sync-artifact }} + path: sync-microservice/dist + - name: Extract server files shell: bash run: | @@ -173,6 +312,14 @@ jobs: rm *.zip ls -l + - name: Extract sync microservice files + shell: bash + run: | + cd sync-microservice/dist + unzip -o *.zip + rm *.zip + ls -l + - name: Setup Node uses: actions/setup-node@v4 with: diff --git a/.github/workflows/pr-check-tests.yml b/.github/workflows/pr-check-tests.yml index 86035076f..f6d625806 100644 --- a/.github/workflows/pr-check-tests.yml +++ b/.github/workflows/pr-check-tests.yml @@ -70,12 +70,22 @@ jobs: with: python-version: "3.11" - - name: Install Python Dependencies & Run Tests + - name: Build check(Main backend) run: | cd backend python -m pip install --upgrade pip pip install -r requirements.txt pyinstaller main.py --name PictoPy_Server --onedir --distpath dist + + - name: Build check(Sync Microservice) + run: | + cd sync-microservice + pip install -r requirements.txt + pyinstaller main.py --name PictoPy_Sync_Microservice --onedir --distpath dist + + - name: Run Tests + run: | + cd backend pytest # Tauri Test Job diff --git a/backend/app/utils/microservice.py b/backend/app/utils/microservice.py index 995ef5b3f..71d5ea22d 100644 --- a/backend/app/utils/microservice.py +++ b/backend/app/utils/microservice.py @@ -16,6 +16,9 @@ def microservice_util_start_sync_service( """ Start the sync microservice with automatic virtual environment management. + When running as a frozen executable (PyInstaller), it will use the bundled + PictoPy_Sync executable. Otherwise, it uses the development setup with venv. + Args: sync_service_path: Path to the sync microservice directory. If None, defaults to 'sync-microservice' relative to project root. @@ -23,6 +26,74 @@ def microservice_util_start_sync_service( Returns: bool: True if service started successfully, False otherwise. """ + try: + # Check if running as a frozen executable (PyInstaller) + if getattr(sys, "frozen", False): + logger.info("Running as frozen executable, using bundled sync microservice...") + return _start_frozen_sync_service() + + # Development mode - use virtual environment setup + logger.info("Running in development mode, using virtual environment...") + return _start_dev_sync_service(sync_service_path) + + except Exception as e: + logger.error(f"Error starting sync microservice: {e}") + return False + + +def _start_frozen_sync_service() -> bool: + """ + Start the sync microservice when running as a frozen executable. + The sync microservice executable should be in the PictoPy_Sync folder. + """ + try: + # Get the directory where the current executable is located + if getattr(sys, "frozen", False): + # When frozen, sys.executable points to the main executable + app_dir = Path(sys.executable).parent + else: + # Fallback (shouldn't happen in this function) + app_dir = Path(__file__).parent.parent.parent.parent + + # Look for the PictoPy_Sync directory and executable + sync_dir = app_dir / "PictoPy_Sync" + + # Determine executable name based on platform + system = platform.system().lower() + if system == "windows": + sync_executable = sync_dir / "PictoPy_Sync.exe" + else: + sync_executable = sync_dir / "PictoPy_Sync" + + if not sync_executable.exists(): + logger.error(f"Sync microservice executable not found at: {sync_executable}") + return False + + logger.info(f"Starting sync microservice from: {sync_executable}") + + # Start the sync microservice executable + process = subprocess.Popen( + [str(sync_executable)], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + cwd=str(sync_dir), # Set working directory to sync service directory + ) + + logger.info(f"Sync microservice started with PID: {process.pid}") + logger.info("Service should be available at http://localhost:8001") + + return True + + except Exception as e: + logger.error(f"Error starting frozen sync microservice: {e}") + return False + + +def _start_dev_sync_service(sync_service_path: Optional[str] = None) -> bool: + """ + Start the sync microservice in development mode using virtual environment. + """ try: # Determine the sync service path if sync_service_path is None: @@ -65,7 +136,7 @@ def microservice_util_start_sync_service( return _start_fastapi_service(python_executable, sync_service_path) except Exception as e: - logger.error(f"Error starting sync microservice: {e}") + logger.error(f"Error starting dev sync microservice: {e}") return False @@ -161,9 +232,7 @@ def _start_fastapi_service(python_executable: Path, service_path: Path) -> bool: cmd = [str(python_executable), "-m", "fastapi", "dev", "--port", "8001"] # Start the process (non-blocking) - process = subprocess.Popen( - cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True - ) + process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) # Restore original working directory os.chdir(original_cwd) diff --git a/sync-microservice/main.py b/sync-microservice/main.py index 5f5477c50..52b21b53f 100644 --- a/sync-microservice/main.py +++ b/sync-microservice/main.py @@ -1,4 +1,5 @@ from fastapi import FastAPI +from uvicorn import Config, Server from app.core.lifespan import lifespan from app.routes import health, watcher, folders @@ -14,3 +15,8 @@ app.include_router(health.router, prefix="/api/v1") app.include_router(watcher.router, prefix="/api/v1") app.include_router(folders.router, prefix="/api/v1") + +if __name__ == "__main__": + config = Config(app=app, host="0.0.0.0", port=8001, log_config=None) + server = Server(config) + server.run() From 49c4d80a67fc64356c82346d31c7ee18680ccf38 Mon Sep 17 00:00:00 2001 From: rahulharpal1603 Date: Sat, 30 Aug 2025 12:18:18 +0530 Subject: [PATCH 2/2] Fix lint errors --- backend/app/utils/microservice.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/backend/app/utils/microservice.py b/backend/app/utils/microservice.py index 71d5ea22d..6e47b157c 100644 --- a/backend/app/utils/microservice.py +++ b/backend/app/utils/microservice.py @@ -29,7 +29,9 @@ def microservice_util_start_sync_service( try: # Check if running as a frozen executable (PyInstaller) if getattr(sys, "frozen", False): - logger.info("Running as frozen executable, using bundled sync microservice...") + logger.info( + "Running as frozen executable, using bundled sync microservice..." + ) return _start_frozen_sync_service() # Development mode - use virtual environment setup @@ -66,7 +68,9 @@ def _start_frozen_sync_service() -> bool: sync_executable = sync_dir / "PictoPy_Sync" if not sync_executable.exists(): - logger.error(f"Sync microservice executable not found at: {sync_executable}") + logger.error( + f"Sync microservice executable not found at: {sync_executable}" + ) return False logger.info(f"Starting sync microservice from: {sync_executable}") @@ -232,7 +236,9 @@ def _start_fastapi_service(python_executable: Path, service_path: Path) -> bool: cmd = [str(python_executable), "-m", "fastapi", "dev", "--port", "8001"] # Start the process (non-blocking) - process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + process = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True + ) # Restore original working directory os.chdir(original_cwd)