Releasing¶
Releases are driven entirely by .github/workflows/release.yml on pushes to main, gated by release-please parsing Conventional Commits.
release-please¶
The release-please job mints a short-lived, repo-scoped GitHub App installation token from an internal OpenBao broker (not a durable PAT) and runs googleapis/release-please-action. Its config, release-please-config.json, uses release-type: simple with include-component-in-tag:
false, and lists three extra-files kept in sync with the bumped version alongside CHANGELOG.md:
grot-track-extension/package.json($.version, JSON path bump)grot-track-extension/wxt.config.ts(generic — themanifest.versionstring)project.yml(generic — theMARKETING_VERSIONbuild setting)
release-please opens a release PR that accumulates changes until merged; merging it tags the release and triggers the rest of the pipeline (release_created: true). The changelog is grouped into Features, Bug Fixes, Miscellaneous, Documentation, Refactoring, Performance and Tests sections per changelog-sections in the config, and is rendered on this site at Changelog.
What runs on a release¶
All of the following jobs are gated on needs.release-please.outputs.release_created:
test-gate— runs the fullGrotTrackTestssuite (macos-26runner) before anything is built for shipping. A release does not proceed past this on a failing test.build-release— archivesGrotTrackwithxcodebuild archive, re-signs the embedded Sparkle framework's XPC services,Autoupdate,Updater.appand the framework itself (innermost-first) with theDeveloper ID Applicationidentity, then re-signs the whole app with its entitlements, verifies withcodesign --verify --deep --strict, notarizes viaxcrun notarytool submit --wait, and staples the ticket. It also builds the Chrome extension zip and uploads bothGrotTrack.zipand the extension zip as GitHub Release assets viasoftprops/action-gh-release.update-appcastandpublish-extensionrun in parallel oncebuild-releasefinishes.
Auto-update (Sparkle)¶
GrotTrack uses the Sparkle framework for in-app updates. Info.plist points SUFeedURL at https://rknightion.github.io/grotTrack/appcast.xml, published via GitHub Pages, with SUPublicEDKey pinning the EdDSA public key Sparkle uses to verify update signatures.
The update-appcast job:
- Downloads the just-published
GrotTrack.ziprelease asset. - Fetches Sparkle's
sign_updatetool (pinned version2.6.4) and signs the zip with the private EdDSA key held in theSPARKLE_EDDSA_KEYsecret, extracting the signature and length fromsign_update's output. - Downloads the currently-published
appcast.xml(if any) and runsscripts/update-appcast.shwith the version, signature and length to append the new release entry. - Publishes the updated
_site/appcast.xmlto GitHub Pages viaactions/deploy-pages.
Sparkle on a running GrotTrack instance polls that feed and verifies each update against SUPublicEDKey before installing it — an update whose signature does not verify is rejected client-side regardless of where it was fetched from.
Chrome Web Store publishing¶
The publish-extension job runs chrome-webstore-upload-cli@3 twice — upload then publish — against the zip produced by npx wxt zip, authenticating with four repository secrets: CHROME_EXTENSION_ID, CHROME_CLIENT_ID, CHROME_CLIENT_SECRET, CHROME_REFRESH_TOKEN. This only works for versions after the first submission — Chrome Web Store requires a manual first upload and review before CI can publish subsequent versions automatically. If any of the four secrets is missing, this job fails independently of build-release, so a broken Chrome Web Store credential does not block the macOS release. The full manual first-submission walkthrough, listing content, permission-justification text and OAuth credential setup live in chromestore.md at the repo root.
Chrome Web Store versions must strictly increase and follow the 1–4 dot-separated integer format (no pre-release suffixes) — this is why the extension's version is kept in lockstep with the app's via the extra-files list above rather than versioned independently.
Version numbering¶
There is one version number for the whole project, driven by release-please and mirrored into:
project.yml—MARKETING_VERSION(the macOS app's version)grot-track-extension/package.jsonandwxt.config.ts— the extension's versionCHANGELOG.mdand the Git tag (vX.Y.Z,include-component-in-tag: false)
.release-please-manifest.json tracks the last-released version release-please has recorded.