fix(release): correct the v0.8.0 appcast signature to match the shipped DMG - #65
Merged
Conversation
…ed DMG The DMG attached to the v0.8.0 GitHub Release is not the one the appcast entry was signed against, so Sparkle rejects every update with "The update is improperly signed and could not be validated." The release workflow was dispatched a second time for 0.8.0 after the first release had already published. That run deleted the published release and its tag, rebuilt the DMG, and signed the new bytes — but its appcast update was force-pushed to release/v0.8.0, whose PR had already merged and closed, so the new signature never reached main. The result is a feed advertising the first build's signature over the second build's bytes. Point the entry at the DMG that is actually served. Both signatures were produced by the release key in CI; this is the one that verifies against the published artifact. Verified all 11 appcast entries against their live release assets: every EdDSA signature and length now matches the bytes GitHub serves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
Test CoverageCoverage after merging fix/appcast-08-signature into main will be
Coverage Report for Changed Files
|
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every user on 0.8.0 or earlier currently gets "The update is improperly signed and could not be validated" when Sparkle tries to update. This restores the feed.
What broke
The appcast entry for 0.8.0 advertises a signature over bytes that are not the bytes GitHub serves:
appcast.xmlonmainxeGbajj6…kKuYBA==6lGIp4X3…Se2EAw==Verified against the pinned
SUPublicEDKeyfromDistribution/Info.plist: the live entry fails Ed25519 verification over the released DMG, the replacement passes. Sparkle checks the signature before it will install, so it refuses the update — correctly. The artifact is genuine; the feed was describing a different build.How it happened
release.ymlwas dispatched a second time for 0.8.0 at2026-08-08T05:58Z, after PR #63 had already merged and published the release. git-cliff still calculated0.8.0(no releasable commits had landed since), so the run:gh release delete "$TAG" --yes --cleanup-tagbranch and removed the published release and its tag,release/v0.8.0, whose PR had already merged and closed.Step 4 is where the signature was lost: there was no open PR left to carry it to
main. The draft was then published, leaving the new DMG live under the old entry.The tag itself is fine —
v0.8.0points atb7d1e94, same asmain.The fix
Update the 0.8.0 enclosure to the signature and length of the DMG that is actually served. No re-release needed: the shipped artifact is a legitimate signed build, only the feed was stale.
Checked the whole feed rather than just the broken entry — all 11 versions now verify against their live release assets:
Merging publishes the corrected feed via
deploy-site.yml. Users recover on their next update check with no action on their part.Follow-up, not in this PR
Nothing stops this from recurring: re-dispatching
release.ymlfor an already-published version will delete it and swap the artifact again.release.ymlshould refuse to delete a release that is not a draft. Left out here deliberately — #64 rewrites that file, so the guard belongs there rather than in a conflicting hotfix.🤖 Generated with Claude Code
Screenshot
Auto-captured on
macos-26atd124d72(workflow run). Updated on every push to this PR.