Repository navigation
ci: publish every release on Zapstore - #906
Conversation
The release workflow now ends with a zapstore job: once the GitHub release is out, it runs zsp publish --quiet zapstore.yaml, which takes the arm64-v8a APK of the latest GitHub release and the listing from the tagged zapstore.yaml, and signs it as Mostro's Zapstore publisher (npub1m0str0…), as every release so far was by hand. MostroP2P/bosstro does the same (MostroP2P/bosstro#50). - The key is ZAPSTORE_SIGN_WITH, a secret of the release environment; it reaches zsp through SIGN_WITH, never argv. - zsp is pinned (0.4.17) and checked against its sha256. - zapstore.yaml names the publisher (pubkey), so zsp refuses any other key instead of publishing a second Mostro. - zsp always takes the latest GitHub release, so the job publishes only while its tag is the latest one: a re-run of an older tag leaves Zapstore as it is, with a warning, and does not need the secret. - Nothing waits for it: a failed zapstore job leaves the GitHub release and the changelog PR as they are, and can be re-run on its own (a release already listed is a no-op). docs/RELEASING.md documents the secret, the certificate proof, which the job does not renew (it expires 2027-10-21), upgrading zsp and recovering from a failed run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wLvM1dvKHtf1wYBWAz5Ys
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Publisher identity and signing requirements zapstore.yaml, docs/RELEASING.md, test/ci/release_workflow_test.dart |
The Zapstore listing specifies its publisher key. Documentation and tests cover the signing secret, publisher key, certificate proof, and pinned zsp version and digest. |
Gated release publication .github/workflows/release.yml, test/ci/release_workflow_test.dart, CLAUDE.md, docs/RELEASING.md |
The workflow checks whether the run tag is latest before publishing. Tests and release guidance cover job ordering, changelog independence, and retry behavior. |
Priority:
Estimated code review effort: 3 (Moderate) | ~20 minutes
Change: Feature
Sequence Diagram(s)
sequenceDiagram
participant GitHubActions
participant GitHubReleases
participant zsp
participant Zapstore
GitHubActions->>GitHubReleases: Check whether the run tag is latest
GitHubActions->>zsp: Download and verify the pinned binary
GitHubActions->>zsp: Run publish with zapstore.yaml and signing key
zsp->>Zapstore: Publish the release listing
Merge Risk: 
Minimal · up to c2e09
-
The Zapstore job is ready to merge after normal checks. In an unusual release-selection case, its listing could refer to a different APK; the GitHub release remains unaffected.
Pre-merge checks: 
5
-
Full details: Description check
-
Explanation
The description follows the repository template and explains the workflow changes, affected files, security considerations, testing, limitations, and checklist status. It also clearly states that a real Actions run with the secret has not yet occurred.
Full details: Docstring Coverage
-
Explanation
No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 0 functions across 0 files. (5 skipped: 5 unsupported.)
-
Finishing Touches
-
🧪 Generate unit tests (beta)
-
- Commit to this branch
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
I’m a rabbit by the release gate,
I check the tag before I wait.
A signed listing hops along,
With verified tools to keep it strong.
Zapstore gets the latest hare,
And older tags stay where they were.
Comment @coderabbitai help to get the list of available commands.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
@coderabbitai review |
✅ Action performed
|
Linked issue
None: maintainer pull request. Every release has been put on Zapstore by hand with
zsp; MostroP2P/bosstro#50 automates the same for Bosstro.Type
ci
What changes and why
From the next release on, pushing the tag also lists it on Zapstore. A new
zapstorejob inrelease.ymlrunszsp publish --quiet zapstore.yamlonce the GitHub release is out. It signs as Mostro's Zapstore publisher (npub1m0str0…), the same key that published every version so far by hand.Affected flow
No app flow: only the release workflow (
.github/workflows/release.yml, new jobzapstoreafterpublish) andzapstore.yaml.ZAPSTORE_SIGN_WITH, a secret of thereleaseenvironment. It reaches zsp through theSIGN_WITHenvironment variable, never through argv.0.4.17, pinned and checked against the asset's sha256.zapstore.yamlnow names the publisher (pubkey), so zsp refuses another key ("pubkey mismatch") instead of publishing a second Mostro.--quietskips certificate linking, and the runner has no keystore.Blast radius
publishandchangelogare unchanged, and nothing waits forzapstore. If it fails, the GitHub release and the changelog PR are unaffected, and the job can be re-run on its own.release-dry-run.ymlandrelease-builds.ymlare untouched and still never see a secret.releaseenvironment, this job asks for its own approval afterandroid's.pubkeyinzapstore.yamlis read by zsp only, to refuse another signer. The events it publishes do not change.Before merging: add the secret
ZAPSTORE_SIGN_WITH(thensec1…ofnpub1m0str0…) under Settings → Environments →release. Without it the job fails at "Check Zapstore secret", after the GitHub release is already out.Worth knowing:
docs/RELEASING.mdasks for three protections, but today thereleaseenvironment only has thev*.*.*tag rule. There is no tag ruleset and no required reviewers. Only grunch and Catrya can push, and both are admins, so in practice only admins can release. But a new writer could push a tag on a commit outsidemainwith a modified workflow, and that run would reach the APK signing key, and with this PR the Zapstore key too. A tag ruleset limited to admins, as MostroP2P/bosstro has, closes that.Platforms tested
None: no app code changes. The workflow was exercised locally with the pinned zsp binary, as below.
Manual testing
Setup
The zsp
0.4.17linux-amd64 binary, downloaded and checked by the job's own install step.GITHUB_TOKENis set fromgh auth token.Steps
sha256sum --check --strict.SIGN_WITH=npub1m0str0… zsp publish --quiet zapstore.yaml(npub mode signs nothing and publishes nothing): exit 0 with no output, because v2.1.1 is already on Zapstore.--overwrite-release: it builds kind 32267, 30063foundation.mostro.app@2.1.1and 3063 with the arm64-v8a APK and the release certificate (abfbaa99…).zsp publish --check zapstore.yaml→{"package_id":"foundation.mostro.app"}.Not covered
A real run in Actions with the secret. The next release is the first one.
Screenshots
No visible change.
Automated tests
test/ci/release_workflow_test.dart, grouprelease zapstore job:publishsucceeded;zapstore.yamlnames the publisher;releaseenvironment with no write token, and only this job readsZAPSTORE_SIGN_WITH;flutter test test/ci/ test/licensing:+167: All tests passed!Checklist
cd rust && cargo fmt --check && cargo clippy -- -D warnings && cargo testpass locally (n/a: no Rust change)dart formaton the changed test,flutter analyzeandflutter test test/ci/ test/licensingpass locallyrust/src/api/or an.arb: regenerated code committed in the same commit (n/a)docs/RELEASING.mdandCLAUDE.mdupdatedAI assistance
Written with Claude Code: the workflow job, the tests, the docs, and the local zsp runs above. It is the same job as MostroP2P/bosstro#50, including the fixes from that PR's reviews. Look hardest at the secret's exposure in the
releaseenvironment (see Worth knowing).🤖 Generated with Claude Code
https://claude.ai/code/session_016wLvM1dvKHtf1wYBWAz5Ys
Summary by CodeRabbit