v2.8.1 #35
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: Release | |
| # Native macOS release workflow. | |
| # | |
| # Current status: MANUAL | |
| # DMG artifacts are built locally on the release Mac with `make release` | |
| # (scripts/release.sh: Developer ID signing + notarization + stapling) and | |
| # uploaded to GitHub Releases with `gh release upload`. See BUILDING.md | |
| # "Building a Release". | |
| # | |
| # Future: automate with a self-hosted macOS runner or GitHub's macos-latest | |
| # (requires exporting the Developer ID certificate and notary API key as | |
| # repository secrets). | |
| # | |
| # Windows / Linux: Zerm is a native macOS Swift app (AppKit + SwiftUI). | |
| # There is no Windows or Linux build. The archived Tauri prototype that | |
| # previously built cross-platform installers is no longer maintained. | |
| on: | |
| # Manual trigger only — for running any future automated steps | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Release tag (e.g. v1.0.4)" | |
| required: true | |
| type: string | |
| # Keep the published changelog in step with GitHub Releases. | |
| # | |
| # Only `released`. Publishing a release fires `published` AND `released`, and | |
| # every asset upload fires `edited`, so the old four-type list ran this whole | |
| # workflow three times per release and raced three identical changelog branches | |
| # against each other. `deleted` was pointless here: the regenerated changelog | |
| # reads the releases list, which a deletion also changes, but a deleted release | |
| # cannot be validated by the tag job below. | |
| release: | |
| types: [released] | |
| permissions: | |
| contents: write | |
| # createCommitOnBranch needs contents; opening the changelog PR needs this. | |
| # Without it `gh pr create` fails with "Resource not accessible by integration". | |
| pull-requests: write | |
| jobs: | |
| release-notes: | |
| name: Validate release tag | |
| # Guarded because this job reads `inputs.tag`, which only exists for a manual | |
| # run. On a release event it evaluated to "", so the job failed with | |
| # "Tag not found" on every single release — the other half of why this | |
| # workflow has never gone green. | |
| if: github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| with: | |
| # Tags are not fetched by the default shallow checkout, so `git rev-parse` | |
| # below reported every tag as missing — including ones that plainly exist. | |
| fetch-depth: 0 | |
| - name: Verify tag exists | |
| run: | | |
| TAG="${{ inputs.tag }}" | |
| if git rev-parse "$TAG" >/dev/null 2>&1; then | |
| echo "Tag $TAG exists — release is valid" | |
| else | |
| echo "Tag $TAG not found. Create the tag first:" | |
| echo " git tag $TAG && git push origin $TAG" | |
| exit 1 | |
| fi | |
| - name: Check release assets | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| TAG="${{ inputs.tag }}" | |
| ASSETS=$(gh release view "$TAG" --json assets --jq '.assets[].name' 2>/dev/null || echo "") | |
| if echo "$ASSETS" | grep -qi "\.dmg"; then | |
| echo "Release $TAG has DMG assets:" | |
| echo "$ASSETS" | |
| else | |
| echo "WARNING: Release $TAG has no .dmg assets yet." | |
| echo "Upload a DMG with:" | |
| echo " gh release upload $TAG path/to/Zerm_VERSION_aarch64.dmg" | |
| fi | |
| publish-changelog: | |
| name: Regenerate site changelog | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| with: | |
| ref: Production | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: "22" | |
| - name: Regenerate derived pages | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: node scripts/build-site.mjs | |
| # This job cannot publish the changelog itself, and pretending otherwise is | |
| # what made it useless for so long. The history: | |
| # | |
| # 1. It committed as github-actions[bot] and pushed to Production. The | |
| # "Require signed commits" ruleset covers ~ALL branches with no bypass | |
| # actors, so the push was rejected — every release, silently, which is | |
| # how the site changelog got stuck at 2.6.1 while 2.7.0 shipped. | |
| # 2. Creating the commit via GraphQL createCommitOnBranch fixed the signing | |
| # (GitHub signs those server-side) and that part does work. | |
| # 3. But Production takes changes by pull request only, and the org forbids | |
| # GitHub Actions from creating pull requests: | |
| # "GitHub Actions is not permitted to create or approve pull requests" | |
| # That is an org-wide security policy. Loosening it for a changelog is a | |
| # bad trade, so this job no longer tries. | |
| # | |
| # Instead it reports drift and fails loudly. Regenerating is a one-liner a | |
| # maintainer runs with their own credentials, and `make release` does it as | |
| # part of cutting a release. | |
| - name: Check the site is in step with published releases | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| if git diff --quiet -- docs/; then | |
| echo "Site is up to date with the published releases." | |
| exit 0 | |
| fi | |
| echo "::error::The published site is out of date with the GitHub Releases." | |
| echo "Stale files:" | |
| git diff --name-only -- docs/ | sed 's/^/ /' | |
| echo "" | |
| echo "Regenerate and open a PR:" | |
| echo " node scripts/build-site.mjs && git add docs/ && git commit -m 'Regenerate site changelog from releases'" | |
| echo "" | |
| echo "Diff of what would change:" | |
| git --no-pager diff --stat -- docs/ | |
| exit 1 |