ci(nightly-next): publish the cmux NIGHTLY-identity feed from Blacksmith Linux
Leo: "JUST FUCKING USE MORE LINUX FIX THE FUCKING BUILDS" and "Just fucking compile on Linux. Just do it. Why are the minis tied up?"
What
publish-nightly-identity-legacy (the cmux NIGHTLY-identity old feed writer) moves from Blacksmith macOS to blacksmith-4vcpu-ubuntu-2404. It never needed a Mac except for one step, "Generate the old feed's appcast", which ran Sparkle's generate_appcast (a macOS binary).
- That step now uses
scripts/ci/tip_appcast_linux.py, main's Linux writer from the DEV tip feed (#25799), copied verbatim to feat-cmux-next. It writes the itemgenerate_appcastwrites for one DMG with no deltas. The macOS floor (appcast_minimum_system_version.py enforce) and thesparkle:channeltag get added the waysparkle_generate_appcast.shadded them. Then the existing ring merge and guard run as before. - The writer needs the app's Info.plist, and Linux can't mount the APFS/ULMO DMG. So
verify-nightly-identity-legacy(the Mac verify job, which stays) gets one more step after it verifies the DMG: it re-checks the tested sha256, mounts the DMG read-only, copiescmux NIGHTLY.app/Contents/Info.plistand uploads it (cmux-nightly-identity-tested-plist). The writer refuses an app whose CFBundleVersion is not the build, or whose SUPublicEDKey is not the signing key's public half. It also checks the signature with openssl before it writes anything. - runs-on is a literal Blacksmith label with no
vars.LINUX_RUNNER, because the job holds the Sparkle key and R2 credentials. Blacksmith is trusted for release secrets; owned hosts are not. No new secrets or permissions. - The jobs this PR does not touch:
sign-nightly-identity-legacy(still waits on the signing-split proof) and the runs-on routing that #24941 changes. main has no copy of this job, so nothing needs mirroring there.
Is the Sparkle signature the same?
I checked on the real published legacy DMG cmux-nightly-next-macos-nightlyid-arm64-4038033920301.dmg (sha256 c317f089…63e6):
- With one random test seed, Sparkle 2.8.1's
sign_update -pand the Linux writer's signer gave the same signature:2W1/nDIc…kaX3Bg==both times. openssl verifies it. - The live feed's
sparkle:edSignaturefor that DMG verifies with openssl against the pinnedNIGHTLY_NEXT_SPARKLE_PUBLIC_KEY. - The new test
tests/test_nightly_legacy_publish_linux.pychecks:- the RFC 8032 vector, for both the 32-byte and the 96-byte key form;
- that the item the writer produces is byte-for-byte the live feed's item for build 4038033920301 (pubDate and description aside);
- the workflow contract: Linux runs-on, no Apple tool in the job, the plist comes from the tested DMG, and the step order.
The test is wired into ci-guards release-notary, with step coverage in .github/nightly-step-coverage.json.
Mac minutes
Before, from the publish job in 7 published nightly-next runs (40351269110, 40357982361, 40370046046, 40370780830, 40372260966, 40378565252, 40380339203): 2.35, 2.98, 1.85, 1.85, 2.05, 1.82 and 1.62 Mac minutes, mean 2.07. The verify job averaged 2.5 minutes in the same runs; its new step adds about 10 s.
After: I'll add numbers here from the first nightly-next run after this lands. Expected is 0 Mac minutes for publish and about +0.15 for verify, so about 1.9 Mac minutes saved per published build.
Risk
Medium. If something goes wrong, it hits the old cmux NIGHTLY-identity feed, not cmux NEXT: the job is continue-on-error, and nightly-identity-legacy-published turns the run red when NEXT publishes and the old feed does not. The ways it could fail:
- the plist handoff;
- a Linux tool difference in the publish step (it already uses only curl, gh, python3 and shasum);
- the writer's item layout. The test pins that layout to the live feed.
The signature can't silently be wrong, because the writer verifies it with openssl against the app's own SUPublicEDKey before writing.
Tests
cmux-ci test (Linux) ran all 80 tests that read nightly.yml or the step coverage, at 86f07edb. The 9 failures all name jobs or files this PR does not change (base reds):
pick-nightly-sign-runnerstep coverage;- cmux-next-perf-budgets/cmux-next.yml routing and Xcode selection;
- chief-evals ubuntu-24.04;
- the signing-keychain literal-label list (
publish-nightly-identity-legacyis not in it); - the cmux-next.yml path filter;
- the
CI_NIGHTLY_NEXT_TRUSTED_RUNNERvar; - a missing
bashlex; - change-areas react gate.
Agent session: agent session
🤖 Generated with Claude Code
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.