From 0b0a39ea861a62a5e90d356eb6e942e39ee28d54 Mon Sep 17 00:00:00 2001 From: Ivan Date: Sun, 30 Nov 2025 23:34:45 -0600 Subject: [PATCH] Refactor Docker setup and frontend structure - Updated Dockerfile to copy frontend files to meshchatx/public directory. - Modified .dockerignore to include meshchatx/public. - Added a new script to prepare the frontend directory. - Adjusted Vite configuration to output to the new public directory. - Updated GitHub Actions workflow to reflect changes in build process and artifact handling. --- .dockerignore | 1 + .github/workflows/build.yml | 203 +++++++++++++++----------------- Dockerfile | 15 +-- Makefile | 1 + meshchatx/meshchat.py | 13 +- package.json | 3 +- scripts/prepare_frontend_dir.py | 11 ++ scripts/test_wheel.sh | 5 +- vite.config.js | 4 +- 9 files changed, 126 insertions(+), 130 deletions(-) create mode 100644 scripts/prepare_frontend_dir.py diff --git a/.dockerignore b/.dockerignore index 7e18af3..0852523 100644 --- a/.dockerignore +++ b/.dockerignore @@ -15,6 +15,7 @@ Makefile build/ dist/ public/ +meshchatx/public/ node_modules/ __pycache__/ *.py[cod] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 17ce374..9a14123 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,102 +27,14 @@ on: default: 'true' type: boolean +permissions: + contents: read + jobs: - build_windows: - runs-on: windows-latest - if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.build_windows == 'true') - permissions: - contents: write - steps: - - name: Clone Repo - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1 - - - name: Install NodeJS - uses: actions/setup-node@f1f314fca9dfce2769ece7d933488f076716723e # v1 - with: - node-version: 22 - - - name: Install Python - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 - with: - python-version: "3.12" - - - name: Install Poetry - run: python -m pip install --upgrade pip poetry - - - name: Sync versions - run: python scripts/sync_version.py - - - name: Install Python Deps - run: python -m poetry install - - - name: Install NodeJS Deps - run: npm install - - - name: Build Electron App - run: npm run dist - - - name: Create Release - id: create_release - uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1 - with: - draft: true - allowUpdates: true - replacesArtifacts: true - omitDraftDuringUpdate: true - omitNameDuringUpdate: true - artifacts: "dist/*-win-installer.exe,dist/*-win-portable.exe" - - build_mac: - runs-on: macos-13 - if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.build_mac == 'true') - permissions: - contents: write - steps: - - name: Clone Repo - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1 - - - name: Install NodeJS - uses: actions/setup-node@f1f314fca9dfce2769ece7d933488f076716723e # v1 - with: - node-version: 18 - - - name: Install Python - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 - with: - python-version: "3.11" - - - name: Install Poetry - run: python -m pip install --upgrade pip poetry - - - name: Sync versions - run: python scripts/sync_version.py - - - name: Install Python Deps - run: python -m poetry install - - - name: Install NodeJS Deps - run: npm install - - - name: Build Electron App - run: npm run dist - - - name: Create Release - id: create_release - uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1 - with: - draft: true - allowUpdates: true - replacesArtifacts: true - omitDraftDuringUpdate: true - omitNameDuringUpdate: true - artifacts: "dist/*-mac.dmg" - - build_linux: + build_frontend: runs-on: ubuntu-latest - if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.build_linux == 'true') permissions: - contents: write + contents: read steps: - name: Clone Repo uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1 @@ -137,33 +49,108 @@ jobs: with: python-version: "3.12" + - name: Sync versions + run: python scripts/sync_version.py + + - name: Install NodeJS Deps + run: npm install + + - name: Build Frontend + run: npm run build-frontend + + - name: Upload frontend artifact + uses: actions/upload-artifact@v4 + with: + name: frontend-build + path: meshchatx/public + if-no-files-found: error + + build_desktop: + name: Build Desktop (${{ matrix.name }}) + needs: build_frontend + runs-on: ${{ matrix.os }} + if: | + github.event_name == 'push' || + ( + github.event_name == 'workflow_dispatch' && + ( + (matrix.name == 'windows' && github.event.inputs.build_windows == 'true') || + (matrix.name == 'mac' && github.event.inputs.build_mac == 'true') || + (matrix.name == 'linux' && github.event.inputs.build_linux == 'true') + ) + ) + strategy: + fail-fast: false + matrix: + include: + - name: windows + os: windows-latest + node: 22 + python: "3.12" + release_artifacts: "dist/*-win-installer.exe,dist/*-win-portable.exe" + - name: mac + os: macos-13 + node: 18 + python: "3.11" + release_artifacts: "dist/*-mac.dmg" + - name: linux + os: ubuntu-latest + node: 22 + python: "3.12" + release_artifacts: "dist/*-linux.AppImage,dist/*-linux.deb,python-dist/*.whl" + permissions: + contents: write + steps: + - name: Clone Repo + uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1 + + - name: Install NodeJS + uses: actions/setup-node@f1f314fca9dfce2769ece7d933488f076716723e # v1 + with: + node-version: ${{ matrix.node }} + + - name: Install Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: ${{ matrix.python }} + + - name: Install Poetry + run: python -m pip install --upgrade pip poetry + + - name: Sync versions + run: python scripts/sync_version.py + + - name: Install Python Deps + run: python -m poetry install + + - name: Install NodeJS Deps + run: npm install + + - name: Prepare frontend directory + run: python scripts/prepare_frontend_dir.py + + - name: Download frontend artifact + uses: actions/download-artifact@v4 + with: + name: frontend-build + path: meshchatx/public + - name: Install patchelf + if: matrix.name == 'linux' run: sudo apt-get update && sudo apt-get install -y patchelf - - name: Install Poetry - run: python -m pip install --upgrade pip poetry - - - name: Sync versions - run: python scripts/sync_version.py - - - name: Install Python Deps - run: python -m poetry install - - name: Build Python wheel + if: matrix.name == 'linux' run: | python -m poetry build -f wheel mkdir -p python-dist mv dist/*.whl python-dist/ rm -rf dist - - name: Install NodeJS Deps - run: npm install - - name: Build Electron App - run: npm run dist + run: npm run dist-prebuilt - name: Create Release - id: create_release uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1 with: draft: true @@ -171,7 +158,7 @@ jobs: replacesArtifacts: true omitDraftDuringUpdate: true omitNameDuringUpdate: true - artifacts: "dist/*-linux.AppImage,dist/*-linux.deb,python-dist/*.whl" + artifacts: ${{ matrix.release_artifacts }} build_docker: runs-on: ubuntu-latest diff --git a/Dockerfile b/Dockerfile index 70186e9..5b41afd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,9 +10,8 @@ FROM node:${NODE_VERSION}-alpine@${NODE_ALPINE_SHA256} AS build-frontend WORKDIR /src # Copy required source files -COPY *.json . -COPY *.js . -COPY src/frontend ./src/frontend +COPY package*.json vite.config.js ./ +COPY meshchatx ./meshchatx # Install NodeJS deps, exluding electron RUN npm install --omit=dev && \ @@ -34,12 +33,10 @@ RUN apk add --no-cache --virtual .build-deps \ apk del .build-deps # Copy prebuilt frontend -COPY --from=build-frontend /src/public public +COPY --from=build-frontend /src/meshchatx/public meshchatx/public # Copy other required source files -COPY *.py . -COPY src/__init__.py ./src/__init__.py -COPY src/backend ./src/backend -COPY *.json . +COPY meshchatx ./meshchatx +COPY pyproject.toml poetry.lock ./ -CMD ["python", "meshchat.py", "--host=0.0.0.0", "--reticulum-config-dir=/config/.reticulum", "--storage-dir=/config/.meshchat", "--headless"] +CMD ["python", "-m", "meshchatx.meshchat", "--host=0.0.0.0", "--reticulum-config-dir=/config/.reticulum", "--storage-dir=/config/.meshchat", "--headless"] diff --git a/Makefile b/Makefile index f329bbc..895391d 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,7 @@ clean: rm -rf build rm -rf dist rm -rf python-dist + rm -rf meshchatx/public sync-version: $(PYTHON) scripts/sync_version.py diff --git a/meshchatx/meshchat.py b/meshchatx/meshchat.py index 7f13de1..93722d2 100644 --- a/meshchatx/meshchat.py +++ b/meshchatx/meshchat.py @@ -49,20 +49,19 @@ def get_file_path(filename): datadir = os.path.dirname(sys.executable) return os.path.join(datadir, filename) - # Running from source or an installed wheel: assets live inside the meshchatx package + # Assets live inside the meshchatx package when installed from a wheel package_dir = os.path.dirname(__file__) - test_path = os.path.join(package_dir, filename) - if os.path.exists(test_path): - return test_path + package_path = os.path.join(package_dir, filename) + if os.path.exists(package_path): + return package_path - # Fall back to repo root when running directly from the source tree + # When running from the repository, fall back to the project root repo_root = os.path.dirname(package_dir) repo_path = os.path.join(repo_root, filename) if os.path.exists(repo_path): return repo_path - # Return the package path even if it does not exist so callers raise a clear error - return test_path + return package_path class ReticulumMeshChat: diff --git a/package.json b/package.json index f30edcd..9f6b59b 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "build": "npm run build-frontend && npm run build-backend", "electron-postinstall": "electron-builder install-app-deps", "electron": "npm run electron-postinstall && npm run build && electron .", - "dist": "npm run electron-postinstall && npm run build && electron-builder --publish=never" + "dist": "npm run electron-postinstall && npm run build && electron-builder --publish=never", + "dist-prebuilt": "npm run electron-postinstall && npm run build-backend && electron-builder --publish=never" }, "license": "MIT", "engines": { diff --git a/scripts/prepare_frontend_dir.py b/scripts/prepare_frontend_dir.py new file mode 100644 index 0000000..a88c1bc --- /dev/null +++ b/scripts/prepare_frontend_dir.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +from pathlib import Path +import shutil + +TARGET = Path("meshchatx") / "public" + +if TARGET.exists(): + shutil.rmtree(TARGET) + +TARGET.mkdir(parents=True, exist_ok=True) + diff --git a/scripts/test_wheel.sh b/scripts/test_wheel.sh index ea3e15c..8cc9992 100755 --- a/scripts/test_wheel.sh +++ b/scripts/test_wheel.sh @@ -28,8 +28,7 @@ print(f'meshchat module location: {meshchat.__file__}') # Check if public directory exists meshchat_dir = os.path.dirname(meshchat.__file__) -package_dir = os.path.dirname(os.path.dirname(meshchat_dir)) -public_path = os.path.join(package_dir, 'public') +public_path = os.path.join(meshchat_dir, 'public') print(f'Checking for public at: {public_path}') print(f'Exists: {os.path.exists(public_path)}') @@ -45,7 +44,7 @@ if os.path.exists(test_path): else: print('WARNING: public directory not found!') print('Checking parent directories...') - current = package_dir + current = meshchat_dir for i in range(3): test = os.path.join(current, 'public') print(f' {test}: {os.path.exists(test)}') diff --git a/vite.config.js b/vite.config.js index 9aa5a0b..695527d 100644 --- a/vite.config.js +++ b/vite.config.js @@ -14,8 +14,8 @@ export default { build: { - // we want to compile vite app to /public which is bundled and served by the python executable - outDir: path.join(__dirname, "public"), + // we want to compile vite app to meshchatx/public which is bundled and served by the python executable + outDir: path.join(__dirname, "meshchatx", "public"), emptyOutDir: true, rollupOptions: {