Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 0 additions & 88 deletions .github/workflows/build.yml

This file was deleted.

83 changes: 35 additions & 48 deletions .github/workflows/package.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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
Expand All @@ -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/*
68 changes: 21 additions & 47 deletions .github/workflows/release-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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'
Expand Down
25 changes: 23 additions & 2 deletions synodic_client/updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down Expand Up @@ -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:
Expand Down
9 changes: 5 additions & 4 deletions tests/unit/test_updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
UpdateInfo,
Updater,
UpdateState,
_platform_suffix,
initialize_velopack,
)

Expand Down Expand Up @@ -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
Expand Down
Loading