feat: implement macOS manifest merging script and update CI workflow … #78
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build/Release | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| permissions: | |
| contents: write | |
| jobs: | |
| # Build every distributable in parallel. Nothing publishes here — artifacts go | |
| # to the `publish` job below, which is the only writer of the GitHub release. | |
| # | |
| # macOS is split by architecture on purpose. Notarization is ~3.5 minutes of | |
| # waiting on Apple per arch and electron-builder runs them serially, so a | |
| # combined mac job spent ~7 minutes of its ~10 doing nothing but waiting. | |
| # Running the two arches as concurrent jobs overlaps that wait. | |
| build: | |
| name: build (${{ matrix.label }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| # One target's failure shouldn't cancel the others — a macOS OOM took the | |
| # Linux and Windows builds down with it on v0.11.0, so the run reported | |
| # nothing about whether they would have succeeded. | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: macos-latest | |
| label: mac-x64 | |
| args: --mac --x64 | |
| - os: macos-latest | |
| label: mac-arm64 | |
| args: --mac --arm64 | |
| - os: ubuntu-latest | |
| label: linux | |
| args: --linux | |
| - os: windows-latest | |
| label: win | |
| args: --win | |
| steps: | |
| - name: Check out Git repository | |
| uses: actions/checkout@v4 | |
| - name: Verify tag matches package.json version | |
| shell: bash | |
| run: | | |
| TAG_VERSION="${GITHUB_REF_NAME#v}" | |
| PKG_VERSION=$(node -p "require('./package.json').version") | |
| echo "Tag version: $TAG_VERSION" | |
| echo "package.json version: $PKG_VERSION" | |
| if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then | |
| echo "::error::Tag $GITHUB_REF_NAME does not match package.json version $PKG_VERSION. Refusing to build." | |
| exit 1 | |
| fi | |
| - name: Install Node.js, NPM and Yarn | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Bundle agent server | |
| run: npm run bundle:agent | |
| - name: Build | |
| uses: samuelmeuli/action-electron-builder@v1 | |
| env: | |
| # Vite's sourcemap generation is the memory ceiling on this build: the | |
| # main chunk is ~3.2MB and its map ~12.5MB, and producing that lot | |
| # peaks around 2.8GB RSS. The macOS arm64 runner has 7GB, so Node 20 | |
| # sizes its old-space heap at roughly 2GB by default — which v0.10.2 | |
| # fit under and v0.11.0 does not. Raise it rather than shrink the | |
| # build, so releases don't fail on the next few hundred KB either. | |
| NODE_OPTIONS: --max-old-space-size=4096 | |
| # Apple Notarization | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| # Code Signing | |
| CSC_LINK: ${{ secrets.CSC_LINK }} | |
| CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} | |
| # GitHub Device Flow client IDs — public values inlined into the | |
| # Electron renderer at build time so the desktop app can do | |
| # GitHub OAuth without contacting redstring.io. The desktop App | |
| # slug is constant so it's literal here; falls back to the | |
| # default in src/services/githubDeviceFlow.js if unset. | |
| VITE_GITHUB_CLIENT_ID: ${{ secrets.VITE_GITHUB_CLIENT_ID }} | |
| VITE_GITHUB_APP_CLIENT_ID: ${{ secrets.VITE_GITHUB_APP_CLIENT_ID }} | |
| VITE_GITHUB_APP_SLUG: ${{ secrets.VITE_GITHUB_APP_SLUG }} | |
| with: | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| args: ${{ matrix.args }} | |
| # Publishing happens once, in the `publish` job. Two mac jobs writing | |
| # to the same release would race on latest-mac.yml. | |
| release: false | |
| # Only the distributables. dist-electron also holds the unpacked .app and | |
| # win-unpacked trees, which are hundreds of MB and are already contained | |
| # in the artifacts below. | |
| - name: Collect distributables | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: dist-${{ matrix.label }} | |
| if-no-files-found: error | |
| retention-days: 7 | |
| path: | | |
| dist-electron/*.dmg | |
| dist-electron/*.zip | |
| dist-electron/*.exe | |
| dist-electron/*.AppImage | |
| dist-electron/*.blockmap | |
| dist-electron/*.yml | |
| publish: | |
| name: publish release | |
| needs: build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out Git repository | |
| uses: actions/checkout@v4 | |
| - name: Install Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| # Each artifact lands in its own subdirectory, which is what keeps the two | |
| # macOS `latest-mac.yml` files from overwriting each other before the merge | |
| # below can see them both. | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Merge macOS update manifests | |
| run: | | |
| npm install js-yaml@4 --no-save --no-audit --no-fund | |
| node scripts/merge-mac-update-manifest.js artifacts release | |
| - name: Assemble release files | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p release | |
| # Everything except latest-mac.yml, which the merge step already wrote. | |
| # -n so nothing can clobber the merged manifest. | |
| find artifacts -type f ! -name 'latest-mac.yml' -exec cp -n {} release/ \; | |
| echo "Release payload:" | |
| ls -lh release/ | |
| - name: Create draft release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| TAG="${GITHUB_REF_NAME}" | |
| TITLE="${TAG#v}" | |
| # Created as a draft, matching how electron-builder's GitHub publisher | |
| # behaved: the release is reviewed and published by hand. | |
| if gh release view "$TAG" >/dev/null 2>&1; then | |
| echo "Release $TAG exists — uploading assets over it." | |
| gh release upload "$TAG" release/* --clobber | |
| else | |
| gh release create "$TAG" release/* \ | |
| --draft \ | |
| --title "$TITLE" \ | |
| --generate-notes | |
| fi | |
| echo "::notice::Draft release $TAG is ready. Review and publish it manually." |