From 2048697a03ae51dabf20577aa353c97124a6ae5e Mon Sep 17 00:00:00 2001 From: Asher Norland Date: Mon, 16 Feb 2026 09:07:32 -0800 Subject: [PATCH] Separate Platform Channels --- .github/workflows/build.yml | 88 ----------------------------- .github/workflows/package.yml | 83 ++++++++++++--------------- .github/workflows/release-build.yml | 68 +++++++--------------- synodic_client/updater.py | 25 +++++++- tests/unit/test_updater.py | 9 +-- 5 files changed, 84 insertions(+), 189 deletions(-) delete mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index c8067a9..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,88 +0,0 @@ -name: Build Executables - -# Reusable workflow that builds PyInstaller executables for all platforms. -# Does not handle packaging or publishing - that's the caller's responsibility. - -on: - workflow_call: - -jobs: - build-windows: - if: github.repository_owner == 'synodic' - runs-on: windows-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Install PDM - uses: pdm-project/setup-pdm@v4 - with: - python-version: "3.14" - cache: true - - - name: Install dependencies - run: pdm install -G build - - - name: Build executable - run: pdm run pyinstaller tool/pyinstaller/synodic.spec --distpath dist - - - name: Upload build artifact - uses: actions/upload-artifact@v4 - with: - name: build-windows-x64 - path: dist/synodic - - build-linux: - if: github.repository_owner == 'synodic' - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y libxcb-cursor0 libxkbcommon-x11-0 - - - name: Install PDM - uses: pdm-project/setup-pdm@v4 - with: - python-version: "3.14" - cache: true - - - name: Install dependencies - run: pdm install -G build - - - name: Build executable - run: pdm run pyinstaller tool/pyinstaller/synodic.spec --distpath dist - - - name: Upload build artifact - uses: actions/upload-artifact@v4 - with: - name: build-linux-x64 - path: dist/synodic - - build-macos: - if: github.repository_owner == 'synodic' - runs-on: macos-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Install PDM - uses: pdm-project/setup-pdm@v4 - with: - python-version: "3.14" - cache: true - - - name: Install dependencies - run: pdm install -G build - - - name: Build executable - run: pdm run pyinstaller tool/pyinstaller/synodic.spec --distpath dist - - - name: Upload build artifact - uses: actions/upload-artifact@v4 - with: - name: build-macos-x64 - path: dist/synodic diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index b1d2d78..2294149 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -1,7 +1,7 @@ -name: Package with Velopack +name: Build and Package -# Reusable workflow that packages build artifacts with Velopack. -# Expects build artifacts from the build.yml workflow. +# Reusable workflow that builds PyInstaller executables and packages them with +# Velopack on each platform. Produces one artifact per platform. on: workflow_call: @@ -11,39 +11,51 @@ on: required: true type: string channel: - description: "Velopack channel (stable or dev)" + description: "Velopack channel base (stable or dev)" required: true type: string - outputs: - artifact-name: - description: "Name of the uploaded release artifact" - value: ${{ jobs.collect-releases.outputs.artifact-name }} env: VELOPACK_APP_ID: synodic VELOPACK_APP_TITLE: Synodic Client jobs: - package: + build-and-package: + if: github.repository_owner == 'synodic' runs-on: ${{ matrix.runner }} strategy: matrix: include: - - artifact: build-windows-x64 + - runner: windows-latest main_exe: synodic.exe - runner: windows-latest - - artifact: build-linux-x64 + platform: win + - runner: ubuntu-latest main_exe: synodic - runner: ubuntu-latest - - artifact: build-macos-x64 + platform: linux + - runner: macos-latest main_exe: synodic - runner: macos-latest + platform: osx steps: - - name: Download build artifact - uses: actions/download-artifact@v4 + - name: Checkout + uses: actions/checkout@v6 + + - name: Install system dependencies + if: matrix.platform == 'linux' + run: | + sudo apt-get update + sudo apt-get install -y libxcb-cursor0 libxkbcommon-x11-0 libfuse2 + + - name: Install PDM + uses: pdm-project/setup-pdm@v4 with: - name: ${{ matrix.artifact }} - path: pack + python-version: "3.14" + cache: true + + - name: Install dependencies + run: pdm install -G build + + - name: Build executable + run: pdm run pyinstaller tool/pyinstaller/synodic.spec --distpath dist - name: Setup .NET uses: actions/setup-dotnet@v4 @@ -59,49 +71,24 @@ jobs: run: | vpk download github \ --repoUrl https://github.com/${{ github.repository }} \ - --channel ${{ inputs.channel }} \ + --channel ${{ inputs.channel }}-${{ matrix.platform }} \ --token ${{ secrets.GITHUB_TOKEN }} \ ${{ inputs.channel == 'dev' && '--pre' || '' }} - - name: Install libfuse2 for AppImage - if: contains(matrix.artifact, 'linux') - run: | - sudo apt-get update - sudo apt-get install -y libfuse2 - - name: Create Velopack package shell: bash run: | vpk pack \ --packId ${{ env.VELOPACK_APP_ID }} \ --packVersion ${{ inputs.version }} \ - --packDir pack \ + --packDir dist/synodic \ --mainExe ${{ matrix.main_exe }} \ --packTitle "${{ env.VELOPACK_APP_TITLE }}" \ - --channel ${{ inputs.channel }} \ + --channel ${{ inputs.channel }}-${{ matrix.platform }} \ --outputDir releases - name: Upload release artifacts uses: actions/upload-artifact@v4 with: - name: velopack-release-${{ matrix.artifact }} - path: releases/* - - collect-releases: - needs: package - runs-on: ubuntu-latest - outputs: - artifact-name: velopack-releases - steps: - - name: Download all release artifacts - uses: actions/download-artifact@v4 - with: - pattern: velopack-release-* - path: releases - merge-multiple: true - - - name: Upload combined release artifacts - uses: actions/upload-artifact@v4 - with: - name: velopack-releases + name: velopack-release-${{ matrix.platform }} path: releases/* diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index b27aa2d..660e626 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -73,36 +73,23 @@ jobs: echo "is-dev=false" >> $GITHUB_OUTPUT fi - build: + build-and-package: needs: get-version - uses: ./.github/workflows/build.yml - - package: - needs: [get-version, build] uses: ./.github/workflows/package.yml with: version: ${{ needs.get-version.outputs.installer-version }} channel: ${{ needs.get-version.outputs.channel }} release: - needs: [get-version, package] + needs: [get-version, build-and-package] runs-on: ubuntu-latest steps: - name: Download release artifacts uses: actions/download-artifact@v4 with: - name: velopack-releases + pattern: velopack-release-* path: releases - - - name: Setup .NET - if: needs.get-version.outputs.is-dev == 'true' - uses: actions/setup-dotnet@v4 - with: - dotnet-version: "9.0" - - - name: Install Velopack CLI - if: needs.get-version.outputs.is-dev == 'true' - run: dotnet tool install -g vpk + merge-multiple: true - name: Remove conflicting dev release assets if: needs.get-version.outputs.is-dev == 'true' @@ -118,40 +105,27 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - - name: Upload dev release via Velopack - if: needs.get-version.outputs.is-dev == 'true' - run: | - vpk upload github \ - --repoUrl https://github.com/${{ github.repository }} \ - --token ${{ secrets.GITHUB_TOKEN }} \ - --tag dev \ - --releaseName "Development Build" \ - --pre \ - --publish \ - --merge \ - --channel dev \ - --outputDir releases - - - name: Update dev release notes + - name: Create or update dev release if: needs.get-version.outputs.is-dev == 'true' - run: | - gh release edit dev --prerelease --notes "$(cat <<'EOF' - **Latest Development Build** + uses: softprops/action-gh-release@v2 + with: + tag_name: dev + name: "Development Build" + prerelease: true + make_latest: false + files: releases/* + body: | + **Latest Development Build** - Version: `${{ needs.get-version.outputs.version }}` - Commit: `${{ github.sha }}` + Version: `${{ needs.get-version.outputs.version }}` + Commit: `${{ github.sha }}` - ⚠️ This is a development build and may be unstable. + ⚠️ This is a development build and may be unstable. - ## Installation - - **Windows**: Download `synodic-Setup.exe` and run it - - **Linux**: Download the `.AppImage` file, make it executable with `chmod +x`, and run - - **macOS**: Download and extract the package - EOF - )" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GH_REPO: ${{ github.repository }} + ## Installation + - **Windows**: Download `synodic-Setup.exe` and run it + - **Linux**: Download the `.AppImage` file, make it executable with `chmod +x`, and run + - **macOS**: Download and extract the package - name: Upload to existing release if: needs.get-version.outputs.is-dev != 'true' && github.event_name == 'release' diff --git a/synodic_client/updater.py b/synodic_client/updater.py index fe51fd5..1b5438e 100644 --- a/synodic_client/updater.py +++ b/synodic_client/updater.py @@ -25,6 +25,21 @@ # Velopack automatically discovers releases from GitHub releases GITHUB_REPO_URL = 'https://github.com/synodic/synodic-client' +# Map sys.platform values to Velopack channel suffixes +_PLATFORM_SUFFIXES: dict[str, str] = { + 'win32': 'win', + 'linux': 'linux', + 'darwin': 'osx', +} + + +def _platform_suffix() -> str: + """Return the Velopack channel suffix for the current platform.""" + try: + return _PLATFORM_SUFFIXES[sys.platform] + except KeyError: + raise RuntimeError(f'Unsupported platform for updates: {sys.platform}') from None + class UpdateChannel(StrEnum): """Update channel selection.""" @@ -83,8 +98,14 @@ class UpdateConfig: @property def channel_name(self) -> str: - """Get the channel name for Velopack.""" - return 'dev' if self.channel == UpdateChannel.DEVELOPMENT else 'stable' + """Get the channel name for Velopack. + + Combines the update track (dev/stable) with a platform suffix + so each OS has its own release manifest and nupkg files. + """ + base = 'dev' if self.channel == UpdateChannel.DEVELOPMENT else 'stable' + platform_suffix = _platform_suffix() + return f'{base}-{platform_suffix}' class Updater: diff --git a/tests/unit/test_updater.py b/tests/unit/test_updater.py index 44d437c..5789113 100644 --- a/tests/unit/test_updater.py +++ b/tests/unit/test_updater.py @@ -14,6 +14,7 @@ UpdateInfo, Updater, UpdateState, + _platform_suffix, initialize_velopack, ) @@ -124,15 +125,15 @@ def test_custom_values() -> None: @staticmethod def test_channel_name_stable() -> None: - """Verify STABLE channel returns 'stable' name.""" + """Verify STABLE channel returns platform-specific 'stable' name.""" config = UpdateConfig(channel=UpdateChannel.STABLE) - assert config.channel_name == 'stable' + assert config.channel_name == f'stable-{_platform_suffix()}' @staticmethod def test_channel_name_development() -> None: - """Verify DEVELOPMENT channel returns 'dev' name.""" + """Verify DEVELOPMENT channel returns platform-specific 'dev' name.""" config = UpdateConfig(channel=UpdateChannel.DEVELOPMENT) - assert config.channel_name == 'dev' + assert config.channel_name == f'dev-{_platform_suffix()}' @pytest.fixture