fix: the sweep only touches what a newer key superseded, and the registry is told once - #65
Merged
Merged
Conversation
…s wait for verify
… the feed already did
…essable delimiter
The preflight only checked that the secrets came in pairs, so all of them empty passed with a warning and the release went out unsigned and uninstallable by any copy already out there. On a tag they are now required, named when missing, and checked before anything is built. A dispatch may still go unsigned: it publishes nothing. The feed guard only compared a candidate against the stable it belonged to, so rc.1 stayed on offer after rc.2 shipped. It now also loses to a tag that sorts above it and shares no base with it. Gatekeeper's verdict on the loose CLI binary is printed instead of discarded: a bare executable cannot be stapled, so the refusal is expected and worth saying.
The empty answer left no trace at all. Whether the Store named nothing, named other packages, or answered from what it last knew, the log said the same: nothing. It now records what came back and which family name was looked for, so the difference is on disk the next time this happens. And pressing the button when the Store cannot be asked no longer draws the green «nothing newer» line. That reading is what hid this for whole versions: a copy the Store never signed, a call that timed out and a refusal all landed on the same sentence as a real answer.
Only the setup was signed. What it put on disk was not, so running the command line from a terminal raised SmartScreen on a release that was signed all along. tauri build compiles and packs in one step, so the bundler has to do it: the certificate goes to the store and the sign command picks it by thumbprint, with no password on any command line. It is merged in with --config at build time and never written to the config file. Left there, every local build would try to run a signing command that is not on that machine and fail.
…d running A line continuation in the feed check had become a literal backslash-n, so curl was handed «n» as a second URL and failed every time. The guard that catches a candidate.json still being served while the feed announces none has therefore never once fired. The release gate waits fifteen minutes for CI to finish and the job it lives in was given ten, so a tag pushed while main was still building would have died on a runner timeout rather than on the message that explains why. It also read «no run yet» as «never ran», which is exactly what a tag pushed right behind its commit looks like. Stryker asks TypeScript for parseConfigFileTextToJson, which TypeScript 7 no longer exposes, and mutation testing has been failing since that upgrade landed. It only needs that call to rewrite a tsconfig for the sandbox, and this one has nothing to rewrite: every path in it is already inside.
The bundler is handed a signing command and a green job only says it did not crash. The installer is now unpacked and every binary it will put on disk has its signature read, so «what goes inside is signed» is a thing this pipeline proves rather than a thing it intends.
The gate gave main's run fifteen minutes and the last three took eleven, twelve and fourteen. A cold cache or a queued runner was all it needed to fail a release for having been a minute slow. Twenty minutes now, inside a job of thirty, and the message says twenty.
… lives A hardcoded path is one image change away from failing a release for a reason that has nothing to do with the release. It now looks in both program folders and on the PATH, and says what is missing when none of them has it.
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.
Two audits, and between them one bug that would have cost a working cache and
several that would have cost a release.
The sweep would have deleted a live cache
maincarries two npm caches for Linux-x64, not one, because a setup-nodekey holds the hash of its lockfile and nothing that says which lockfile:
c1fc7352…128 MBapp/package-lock.jsonwindow,coveragedbd76d3b…3 MBpackage-lock.jsonprose945f725e…123 MBThe grouping put all three in one family, kept the newest and deleted the other
two — including the 3 MB one the CI had read two hours earlier. setup-node has
no
restore-keysfor npm, so a missed exact key is a total miss, and the nextpush would rewrite it and delete the other: a see-saw, for ever.
The sweep now touches rust only, grouped by the key rust-cache actually
restores by — without either trailing hash. That is provably safe: GitHub
answers a prefix restore with the most recently created match, so everything
older under it is unreachable by definition. It also fixes a quieter miss: a
toolchain bump used to mint a family of its own and survive the sweep, which is
exactly the 2.8 GB generation that broke the budget in the first place.
The old
^(.*)-[0-9a-f]{8}$would have caught any future key from any actionthat happened to end that way. The new one is anchored to
v0-rust-.A fork could trigger the sweep
workflow_run'sbranchesfilter reads the branch of the run that triggeredit, and a fork's pull request can name its branch
main. The job now alsorequires the triggering run to be a
pushfrom this repository. No foreign codewas ever reachable —
workflow_runchecks out the default branch — but astranger could fire the sweep at will, which with the bug above was a way to
evict the cache.
It also declares
contents: readfor its own checkout rather than relying onthe public-repository exception, and no longer reports every delete failure as
"already gone": only the registry's own not-found message is benign, and
anything else now fails the job instead of passing green having swept nothing.
workflow_dispatchwithdry_rundefaulting true was added because aworkflow_runjob cannot be tested before it is on the default branch.The registry listing
mcp_config.commandnow carries${__dirname}/. The reference host onlysubstitutes
${...}; nothing in it resolves a bare relative path against theextension directory. Today's client happens to set the working directory, but
that is undocumented.
validatedoes not notice — so the job now asks the registry whether theversion is listed and stands down if it is.
mcp-publisheris pinned to v1.8.1 and its checksum verified. It was beingdownloaded unpinned into the one job holding
id-token: write.validatemoved to the gate, where a refusal costs a job instead of arrivingafter the release is announced.
SHA256SUMSbefore itis listed. A wrong hash publishes without complaint and leaves that version
uninstallable for ever.
carries
if-no-files-found: errorlike the other four.And three the audits asked for after that
the manifest an installer reads, 85 in the listing a searcher reads. The
manifest now takes it from
server.json, which the registry already treats ascanonical.
author all follow
${{ github.repository }}now. A rename used to break theURL silently.
release.ymlwrites three npm cachesunder
refs/heads/refs/tags/v*on every release — 384 MB for v1.20.0 — and atag ref is used once, so nothing will ever ask for them by key. They are swept
by ref. Reaching them needed a trigger the main-branch sweep does not have, so
the job also runs daily, which picks up the other known leak: a pull request's
cache written by a job still in flight after the pull request closed.