Skip to content

Verify public appcast publication after releases#896

Merged
SeoFood merged 1 commit into
mainfrom
seofood/verify-appcast-publication
Jul 14, 2026
Merged

Verify public appcast publication after releases#896
SeoFood merged 1 commit into
mainfrom
seofood/verify-appcast-publication

Conversation

@SeoFood

@SeoFood SeoFood commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • wait for the canonical public Sparkle appcast after every release
  • verify the exact version, build, channel, ZIP URL, EdDSA signature, archive length, and effective CDN cache bound
  • document the GitHub Pages publication contract and add regression coverage for stale-to-new transitions

Issue context

The release workflow updated appcast.xml on gh-pages but completed without checking what Sparkle clients could actually fetch. The canonical endpoint currently advertises Cache-Control: max-age=600, and cache-busting query parameters do not bypass the effective CDN cache.

This change keeps the canonical feed URL and adds a release-layer publication gate. After the gh-pages push, the workflow polls the unmodified public URL for up to 900 seconds. It succeeds only when the response cache lifetime is at most 600 seconds and the feed contains the exact release metadata and signed ZIP information. This also prevents a rerun of an existing tag from accepting an appcast that still references the previous ZIP signature.

Closes #895

Test plan

  • python3 scripts/test_verify_appcast_publication.py
  • actionlint -shellcheck= .github/workflows/release.yml
  • scripts/pr-preflight.sh
  • python3 scripts/verify_appcast_publication.py --url https://typewhisper.github.io/typewhisper-mac/appcast.xml --version 1.6.0-daily.20260714 --build-version 961 --channel daily --download-url https://github.com/TypeWhisper/typewhisper-mac/releases/download/v1.6.0-daily.20260714/TypeWhisper-v1.6.0-daily.20260714.zip --signature 6lhEaHcsakLAzlLI6zISC8A6U1pf3XINWca8OcfOFKpsQjdo3yOgfQhj+WchthR7KsfzX7hIVuyza6x/lgdiDg== --length 13736660 --maximum-cache-age-seconds 600 --timeout-seconds 0 --poll-interval-seconds 1

The live verification observed the expected daily entry and matching signed ZIP metadata with Cache-Control: max-age=600 through the canonical URL without a query parameter.

Summary by CodeRabbit

  • Release Reliability

    • Release publishing now verifies that the public Sparkle update feed contains the correct version, build, download details, signature, and archive size.
    • Verification accounts for caching and retries until the update is publicly available or the timeout is reached.
    • Releases fail verification if the feed is stale, mismatched, or unavailable.
  • Documentation

    • Added release-readiness guidance and checklist coverage for appcast publication verification.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: df5d6af0-b051-4e6b-a802-30f94af94e56

📥 Commits

Reviewing files that changed from the base of the PR and between 6be13ca and c3875d9.

📒 Files selected for processing (6)
  • .github/workflows/release.yml
  • docs/release-checklist.md
  • docs/release-readiness.md
  • scripts/pr-preflight.sh
  • scripts/test_verify_appcast_publication.py
  • scripts/verify_appcast_publication.py

📝 Walkthrough

Walkthrough

The release workflow now polls the canonical GitHub Pages appcast after publication, validating release metadata and cache age. A standalone verifier, unit tests, preflight integration, and release-readiness documentation were added.

Changes

Appcast publication verification

Layer / File(s) Summary
Appcast verification engine
scripts/verify_appcast_publication.py
Defines publication expectations, validates Sparkle XML and cache headers, polls the hosted appcast, and returns CLI status codes.
Verification and polling tests
scripts/test_verify_appcast_publication.py
Tests cache parsing, metadata matching and rejection, cache-age limits, and stale-to-current polling behavior.
Release workflow and readiness documentation
.github/workflows/release.yml, scripts/pr-preflight.sh, docs/release-checklist.md, docs/release-readiness.md
Runs publication verification after deploying the appcast, includes it in preflight checks, and documents the publication gate and timing requirements.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ReleaseWorkflow
  participant Verifier
  participant GitHubPages
  ReleaseWorkflow->>Verifier: Invoke with version, build, channel, URL, signature, and length
  Verifier->>GitHubPages: Poll canonical appcast URL
  GitHubPages-->>Verifier: Return feed and cache headers
  Verifier-->>ReleaseWorkflow: Report publication success or timeout
Loading

Possibly related PRs

Poem

I’m a rabbit who checks every feed,
For the right build, signature, and speed.
Fresh from the Pages gate,
No stale hops to await—
The release now springs with the need! 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: verifying public appcast publication after releases.
Description check ✅ Passed The description includes the required Summary and Test Plan sections and is specific about the change.
Linked Issues check ✅ Passed The PR satisfies #895 by documenting cache behavior, polling the canonical URL, verifying headers, and recording a stale-to-new transition.
Out of Scope Changes check ✅ Passed The changes stay focused on appcast publication verification, related docs, and tests with no obvious unrelated additions.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch seofood/verify-appcast-publication

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread scripts/verify_appcast_publication.py Fixed
Comment thread scripts/verify_appcast_publication.py Fixed
@SeoFood SeoFood force-pushed the seofood/verify-appcast-publication branch from fc5ea05 to 5122306 Compare July 14, 2026 18:53
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Caution

Review failed

An error occurred during the review process. Please try again later.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch seofood/verify-appcast-publication

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@SeoFood SeoFood force-pushed the seofood/verify-appcast-publication branch from 5122306 to c3875d9 Compare July 14, 2026 20:05
@SeoFood SeoFood marked this pull request as ready for review July 14, 2026 20:17
@SeoFood SeoFood merged commit 80667c2 into main Jul 14, 2026
14 checks passed
@SeoFood SeoFood deleted the seofood/verify-appcast-publication branch July 14, 2026 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ensure Sparkle appcast freshness at the hosting layer

2 participants