Repository navigation
Update Boost from inside the app - #4
Merged
Merged
Conversation
Updating meant noticing a release on GitHub, deleting the old app and installing the new one by hand, so most people stayed on whatever they first installed. Boost now uses Sparkle 2.10.0 (pinned exactly) to find a new version, show it, and install it when the user clicks Install. Nothing is checked until the user agrees: Sparkle asks on the second launch, and Check for Updates in the Boost menu and the toggle in Settings work at any time. Automatic installs are off and no system profile is sent. build.sh embeds Sparkle.framework in Contents/Frameworks and signs inside out (Sparkle's helpers, the framework, then the app) instead of --deep, for both the local identity and the ad-hoc fallback CI uses. Sparkle accepts an update between two ad-hoc builds because it trusts the EdDSA signature on the zip and only requires the new app's own code signature to be valid. That was tested end to end with two ad-hoc builds.
The app reads docs/appcast.xml from the project's GitHub Pages site. Scripts/appcast.sh adds a published release to it: it downloads the zip CI built, checks it against the SHA-256 in the release notes, checks the app inside has the right version and public key, signs it with the EdDSA key from the maintainer's Keychain, and commits the feed. Signing stays on the maintainer's Mac. As a GitHub secret the key would let anyone who got into the repo push an update to every install. docs/releasing.md has the steps.
README, SECURITY.md and the website all said Boost makes no network requests and has no update check. With in-app updates that is no longer true, so say exactly what happens: one HTTPS request for the feed, no identifiers, only after the user agrees or clicks Check for Updates, and how to turn it off. Also corrects SECURITY.md, which said release builds are notarized. They are ad-hoc signed by CI and not notarized.
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.
Boost can now update itself. When a new version is out, the app shows it with a link to the release notes and an Install Update button. One more click relaunches into the new version. Nobody has to watch GitHub, delete the old app and reinstall.
It uses Sparkle 2.10.0, pinned exactly in
Package.swift.What the user sees
SUAllowsAutomaticUpdatesis false), so the prompt has no "install automatically" checkbox and nothing is installed without a click. No system profile is sent.Build
build.shcopies Sparkle.framework intoContents/Frameworks. The binary gets an@executable_path/../Frameworksrpath throughPackage.swift.--deep. The same steps run with the local identity and with the ad-hoc fallback CI uses. The universal arm64 + x86_64 check still runs, and the framework is universal too.SUFeedURL,SUPublicEDKeyandSUAllowsAutomaticUpdates.Feed
The feed is
docs/appcast.xml, served by the existing Pages site at https://kernel-hunter.github.io/boost/appcast.xml. I picked Pages over raw.githubusercontent.com because Pages already servesdocs/from main over HTTPS, and the URL is the project's own site. That URL stays the same if the repo's layout or default branch changes later, and the file is served as XML rather than text/plain. It is empty until the first release is added.Releasing
Scripts/appcast.sh <version>runs after the release workflow has published the tag. It downloads Boost.zip and checks it against the SHA-256 in the release notes. Then it checks that the app inside has the right version and our public key, signs the zip withsign_updateusing the Keychain key, updates the item in the feed and commits. Signing stays on the maintainer's Mac, so no key goes to GitHub. Steps are indocs/releasing.md.Docs
The README, SECURITY.md and the website all said Boost makes no network requests. They now say what the check sends (one HTTPS request for the feed, no identifiers, your IP and a User-Agent with the version), when it happens and how to turn it off. The README has a short Updating note. SECURITY.md also said release builds are notarized, which was wrong, so that is fixed too.
Testing
swift build,./Scripts/test.sh(324 tests) and./Scripts/build.sh --no-installpass.codesign --verify --deep --strictpasses on the bundle and Sparkle.framework is embedded.boost.local.app.updatetest) and pointed at a localpython3 -m http.server. The feed was generated byappcast.shitself against a fake release. 1.4.99 run from a temp folder made no request on first launch. Check for Updates fetched the feed, Install Update downloaded the zip, and Install and Relaunch replaced the bundle in place. On disk it then read 1.5.0, with the same CDHash as the 1.5.0 build, and the relaunched app was running.SUEnableAutomaticChecksboth ways.Ad-hoc to ad-hoc works because of how
SUUpdateValidator.mdecides. An app update passes if the EdDSA signature checks out against the old app's key, or if the new app's code signature matches the old one. Ad-hoc builds never match each other, since their designated requirement is their own cdhash, but EdDSA passes. Sparkle then only requires the new app's own signature to be valid. The inside-out signing inbuild.shis there to keep that true.Before the first release
Merge, then tag v1.5.0. After CI publishes it, run
./Scripts/appcast.sh 1.5.0and push. People on 1.4.1 or older need to update once the old way. The Homebrew tap change (auto_updates true) is a separate PR on homebrew-boost and should only merge after the cask is on 1.5.0.