From d8107a06c2068ef0e44a809e7237043b89dc018f Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Fri, 28 Aug 2026 17:54:05 -0700 Subject: [PATCH 1/2] Add Linux AppImage build target and app icon The build config only had a Windows target. Adds a linux block producing an AppImage, plus a 512x512 build/icon.png so the packaged app does not ship with the stock Electron icon. Verified by running the packaged binary: the embedded server comes up on 9095 and serves the overlay from process.resourcesPath, confirming the extraResources mapping works in a packaged build. StartupWMClass is deliberately not set via linux.desktop -- electron-builder already emits the correct value, and the nested { entry: {...} } form leaks a literal "entry=[object Object]" line into the .desktop file. Co-Authored-By: Claude Opus 5 (1M context) --- desktop-client/build/icon.png | Bin 0 -> 5960 bytes desktop-client/package.json | 10 +++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 desktop-client/build/icon.png diff --git a/desktop-client/build/icon.png b/desktop-client/build/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..92ce6fb2727d2ab0d3be5d655bcd704117756e76 GIT binary patch literal 5960 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 Date: Fri, 28 Aug 2026 17:58:19 -0700 Subject: [PATCH 2/2] Add Gitea Actions workflow to build AppImage on tag Builds the Linux AppImage on a v* tag, runs the test suite first, and attaches the artifact to a Gitea release via the API (no third-party action, so nothing needs to be fetched from github.com at runtime). Guards that the tag matches package.json version: electron-builder names the artifact from package.json, so tagging v1.0.1 without bumping the manifest would otherwise publish an AppImage named 1.0.0. Requires a RELEASE_TOKEN secret and a registered act_runner; neither is set up yet, so this is inert until both exist. Co-Authored-By: Claude Opus 5 (1M context) --- .gitea/workflows/release.yml | 72 ++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 .gitea/workflows/release.yml diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..686b721 --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,72 @@ +name: Build AppImage + +# Builds the Linux AppImage on a version tag and attaches it to a Gitea release. +# Tag must match the version in desktop-client/package.json, since electron-builder +# names the artifact from package.json (not from the tag). +on: + push: + tags: + - 'v*' + workflow_dispatch: + +jobs: + appimage: + runs-on: ubuntu-latest + defaults: + run: + working-directory: desktop-client + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Verify tag matches package.json version + if: startsWith(github.ref, 'refs/tags/v') + run: | + pkg_version=$(node -p "require('./package.json').version") + tag_version="${GITHUB_REF_NAME#v}" + if [ "$pkg_version" != "$tag_version" ]; then + echo "Tag $GITHUB_REF_NAME does not match package.json version $pkg_version." >&2 + echo "Bump package.json before tagging, or the AppImage will be misnamed." >&2 + exit 1 + fi + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm test + + - name: Build AppImage + run: npx electron-builder --linux AppImage --publish never + + - name: Attach AppImage to release + if: startsWith(github.ref, 'refs/tags/v') + env: + TOKEN: ${{ secrets.RELEASE_TOKEN }} + API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }} + run: | + set -euo pipefail + artifact=$(ls dist/*.AppImage | head -1) + echo "Publishing $artifact for $GITHUB_REF_NAME" + + # Reuse the release if the tag already has one, otherwise create it. + release_id=$(curl -sf -H "Authorization: token $TOKEN" \ + "$API/releases/tags/$GITHUB_REF_NAME" | node -p \ + "JSON.parse(require('fs').readFileSync(0,'utf8')).id" 2>/dev/null || true) + + if [ -z "${release_id:-}" ]; then + release_id=$(curl -sf -X POST -H "Authorization: token $TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"tag_name\":\"$GITHUB_REF_NAME\",\"name\":\"$GITHUB_REF_NAME\"}" \ + "$API/releases" | node -p \ + "JSON.parse(require('fs').readFileSync(0,'utf8')).id") + fi + + curl -sf -X POST -H "Authorization: token $TOKEN" \ + -F "attachment=@$artifact" \ + "$API/releases/$release_id/assets?name=$(basename "$artifact")" \ + -o /dev/null + echo "Attached $(basename "$artifact") to release $release_id"