fix(ruby): refresh standalone Cargo.lock and check the crates.io pin - #1456
fix(ruby): refresh standalone Cargo.lock and check the crates.io pin#1456ya-luotao wants to merge 7 commits into
Conversation
Cargo.toml pins microsandbox =0.6.14 from crates.io, but the committed
lock still resolved 0.6.9 for microsandbox, every microsandbox-* sibling
crate and the extension package itself. Nothing built the lock as
shipped: CI swaps it for the workspace lock and patches microsandbox to
the in-tree SDK, and rb_sys does not pass --locked, so gem installs
silently re-resolved. Re-resolve it against the registry with
cargo update -p microsandbox --precise 0.6.14
(no [patch.crates-io] active) so the lock the source gem ships agrees
with the pin and `--locked` builds succeed.
rake version_check compared the gem version, the extension package version and the exact microsandbox pin, but never the committed Cargo.lock, which is what the source gem ships and what --locked builds resolve. Collect the lock's [[package]] blocks named exactly `microsandbox`: abort when there is none (the lock was not resolved from this manifest) and when a registry-sourced one names a different version than the pin, telling the developer which cargo update to run. Blocks that carry no `source` (the path-patched lock CI's prepare step and cargo:patch_workspace produce) or a missing lock say nothing about the pin and are skipped. Document the lock's lifecycle in the README.
Every Ruby job runs scripts/ci/ruby-sdk-checkout.sh prepare, which replaces the extension's Cargo.lock with the workspace lock and patches microsandbox to sdk/rust, so the committed lock and the registry pin were never resolved as-is and the lock drifted to 0.6.9 unnoticed. Add ruby-standalone-pin, which reads the pin from the extension manifest, asks crates.io whether that version exists, and runs cargo fetch --locked unpatched. A 404 (the version-bump PR ahead of a release) skips the resolution with a notice; any other non-200 status fails. Compiling is not attempted because the published core crates' build scripts need the workspace's build/agentd without the prebuilt feature.
|
Reviews (2): Last reviewed commit: "ci(release): refresh the ruby extension ..." | Re-trigger Greptile |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 93e0db7177
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The extension's Cargo.lock cannot be refreshed at bump time because the new microsandbox crate is only published by this workflow. Extend the refresh-lockfile follow-up job to also wait for crates-publish and run cargo update -p microsandbox --precise "$VERSION" in sdk/ruby/ext/microsandbox, adding the lock to the PR it already opens for the npm lockfile. The two refreshes are independent: the job runs when either publish succeeded, the npm steps are guarded on npm-publish and the Rust steps on crates-publish, so one failed publish only drops that lockfile from the PR instead of skipping the other refresh.
93e0db7 to
53eee67
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 53eee67775
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Reviews (3): Last reviewed commit: "Merge branch 'main' into ruby-standalone..." | Re-trigger Greptile |
Merging main moved the extension's exact pin to =0.6.15 while the committed lock stayed at the 0.6.14 crate graph, which is exactly the drift this branch teaches CI to reject: rake version_check aborts and ruby-standalone-pin's `cargo fetch --locked` fails. Re-resolve with cargo update -p microsandbox --precise 0.6.15 (no [patch.crates-io] active), which pulls every exact-pinned sibling and the lock's own microsandbox-ruby entry along; microsandbox-metrics 0.6.15 adds a windows-sys edge that comes with it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T1yXAfQTLSSVEMknbj49uM
ruby-platform-gem ran `rake version_check` before cargo:patch_workspace, so the new standalone-lock assertion fired against the committed lock in a job that never uses it. On a release-bump PR that is fatal: the bump moves the Ruby manifest to a version crates.io does not carry yet and leaves the standalone lock on the previous release (scripts/bump-version.sh refreshes only Cargo.lock and crates/agentd/Cargo.lock), so the check aborts on all four platforms and tells the maintainer to run a `cargo update --precise` that cannot resolve until after publication. That blocks the PR that has to merge before the tag can be pushed. Run cargo:patch_workspace first. It moves ext/microsandbox/Cargo.lock aside before anything else, so version_check takes the documented "patched workspace may have no lock at all" skip for the crates.io half and still asserts the gem/manifest/pin lockstep that guards the patch swap, before gem:stage compiles. The unpatched crates.io assertion stays where it belongs: ruby-standalone-pin resolves `cargo fetch --locked` against the registry and already skips while the pin is unpublished. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T1yXAfQTLSSVEMknbj49uM
|
Reviews (4): Last reviewed commit: "ci(ruby): patch the workspace before the..." | Re-trigger Greptile |
TL;DR
The Ruby extension's committed
Cargo.lockstill resolvedmicrosandbox 0.6.9from crates.io whileCargo.tomlpinned the current release exactly. Refresh it, makerake version_checkreject that drift, resolve the unpatched pin in CI, re-lock it automatically after each crates.io publish, and keep the new assertion out of the one job that deliberately builds against the in-tree SDK.Description
sdk/ruby/ext/microsandbox/Cargo.lockresolved registrymicrosandbox 0.6.9(and everymicrosandbox-*sibling, and even its ownmicrosandbox-rubyentry) against an exact pin many releases ahead. Nothing caught it: every Ruby CI job runsscripts/ci/ruby-sdk-checkout.sh prepare, which swaps in the workspace lock and patchesmicrosandboxtosdk/rust, andrb_sysdoes not pass--locked, sogem installsilently re-resolved. The gemspec shipsext/**/*, so published source gems carried a lying lock.release.yml. So the refresh has to happen after publish.cargo update -p microsandbox --precise <pin>(no[patch.crates-io]active). One--precisepulls all exact-pinned sibling crates along. The branch now sits at0.6.15, refreshed after merging main.rake version_check(a dependency ofrake test) now collects the lock's[[package]]blocks named exactlymicrosandbox. No such block means the lock was not resolved from this manifest and aborts; every block with asource = "registry+..."line must name the pinned version, otherwise it aborts naming both values and thecargo updateto run. The check is skipped only for a path-patched lock (blocks present but none registry-sourced, which is what CI's prepare step andrake cargo:patch_workspaceproduce) or a missing lock.check.ymljobruby-standalone-pin(gated likeruby-source-gem, no agentd needed): reads the pin fromCargo.toml, askshttps://crates.io/api/v1/crates/microsandbox/<pin>with a User-Agent, and on 200 runscargo fetch --lockedinsdk/ruby/ext/microsandboxwithout the prepare step or any patch. On 404 (the version-bump PR before a release) it emits a notice and skips; any other status fails. Added to thesummaryaggregate job.default-features = false, features = ["net", "ssh"],microsandbox-filesystem's build script takes its non-prebuiltbranch and requires the workspace'sbuild/agentd, which does not exist in a registry checkout. That is a pre-existing problem with the source gem, independent of this lock, and is better handled in its own issue.ruby-platform-gemnow runsrake cargo:patch_workspace version_check gem:stagerather thanrake version_check cargo:patch_workspace gem:stage. That job builds the platform binary from the in-tree Rust SDK and never consumes the committed lock, so asserting the crates.io pin against it was out of place — and fatal on a release-bump PR, where the manifest names a version crates.io does not carry yet and the standalone lock is still on the previous release (scripts/bump-version.shrefreshes onlyCargo.lockandcrates/agentd/Cargo.lock). The check would abort on all four platforms, prescribing acargo update --precisethat cannot resolve until after publication, and block the very PR that has to merge before the tag can be pushed.cargo:patch_workspacemovesext/microsandbox/Cargo.lockaside before anything else, so running it first makesversion_checktake its documented "a patched workspace may have no lock at all" skip for the crates.io half while still asserting the gem/manifest/pin lockstep that guards the patch swap, beforegem:stagecompiles. The unpatched crates.io assertion stays withruby-standalone-pin, which resolves against the registry and already skips while the pin is unpublished.release.ymlrefresh-lockfilenow also waits forcrates-publish, installs a stable Rust toolchain, runscargo update -p microsandbox --precise "$VERSION"insdk/ruby/ext/microsandbox, and includes the lock in the follow-up PR it already opens forsdk/node-ts/package-lock.json(title, commit message, body andadd-pathsupdated). The two refreshes are independent: the job runs when either publish succeeded (!cancelled() && ... && (needs.npm-publish.result == 'success' || needs.crates-publish.result == 'success')), thesetup-node, npm wait andnpm installsteps are guarded onneeds.npm-publish.result == 'success', and the Rust toolchain andcargo updatesteps onneeds.crates-publish.result == 'success'. Checkout andcreate-pull-requeststay unguarded: the pinned action's README states that with no diff against the base branch no pull request is created and it exits silently. The PR body qualifies each lockfile paragraph on its publish having succeeded.sdk/ruby/README.mddocuments the lock's lifecycle.continue-on-error, which keeps a visible (yellow) signal on release PRs whose pin is not published yet at the cost of a noisier status.refresh-lockfilewaits for both publishes but requires neither on its own: it runs when at least one succeeded, and each registry's steps are guarded on that registry's publish. So a crates.io failure yields an npm-only PR, an npm failure yields a Ruby-only PR (previously the job required npm success, which would have left the Ruby lock stale), and only when both fail is the job skipped. Whichever lockfile is missing from the PR still has to be refreshed by hand (rake version_checkprints the cargo command; the npm one isnpm install --package-lock-only --ignore-scripts). The alternative, one job per registry, would open two follow-up PRs per release.cargo updatecan fix it; theruby-platform-gemreorder above andruby-standalone-pin's 404 skip keep CI green, and a local unpatchedrake version_checkis the only thing that still aborts (runrake cargo:patch_workspacefirst, as CI now does). After publication, betweencrates-publishsucceeding and the refresh PR being merged, main carries a published pin with a stale lock: thereruby-standalone-pinis legitimately red on any Ruby-touching push or PR and an unpatched localrake testaborts atversion_check. That is accurate signal and it heals when the refresh PR merges (the refresh PR itself is green), but it means merging that PR promptly matters. If a quieter window is preferred, the fetch step could be made non-blocking onpull_requestevents while the tag's refresh PR is open.scripts/bump-version.shdoes not make —sdk/ruby/lib/microsandbox/version.rb(the script touches nosdk/rubypath by name; the extension'sCargo.tomlis only reached by its genericfind ... sdk -name Cargo.tomlsweep) and the hardcoded gem version inruby-source-gem's smoke assertion, which chore(release): bump microsandbox to 0.6.15 #1449 had to fix in a follow-up commit. Folding both into the bump script would be a worthwhile separate change; this PR deliberately adds no third manual step, since the lock refresh is automated inrelease.yml.Test Plan
cd sdk/ruby/ext/microsandbox && cargo update -p microsandbox --precise 0.6.15with no patch active: allmicrosandbox-*entries and the lock's ownmicrosandbox-rubyentry 0.6.14 -> 0.6.15, plus thewindows-sysedgemicrosandbox-metrics0.6.15 adds. (The original 0.6.9 -> 0.6.14 refresh movedmsb_krun*0.1.31 -> 0.1.32 the same way.)cargo fetch --lockedandcargo metadata --lockedinsdk/ruby/ext/microsandbox: pass on the refreshed lock; fail on the previous lock withcannot update the lock file ... because --locked was passed.cargo check --locked(default features): fails from a registry checkout becausemicrosandbox-filesystembuild.rs cannot findbuild/agentd(pre-existing, see above); passes in 10m45s with theagentd-aarch64binary staged where that build script looks.cargo check --locked --features microsandbox_core/prebuiltpasses in 14m41s.cd sdk/ruby && rake version_check: passes on the refreshed lock; with the lock'smicrosandboxentry edited to an older release it aborts with:With the exact
microsandboxblock renamed (allmicrosandbox-*siblings intact) it aborts with:Release-bump rehearsal for the
ruby-platform-gemreorder:scripts/bump-version.sh 0.6.16plus the manualversion.rbbump the real release PRs carry, leaving the standalone lock on 0.6.15. The old order (rake version_check) aborts naming a--precise 0.6.16that cannot resolve, exit 1. The new order (rake cargo:patch_workspace version_check) exits 0, becausepatch_workspacehas moved the lock aside. Withversion.rbdesynced to 0.6.99 the reordered check still aborts withRuby/Cargo package version mismatch, so the lockstep guard on the patch swap survives the reorder.rake cargo:unpatch_workspacerestores the committed lock byte-identically.rake cargo:patch_workspace && rake version_check && rake cargo:unpatch_workspaceon an unbumped tree: version_check passes with the lock moved aside and with a path-only lock copied in; after unpatch the committed lock is byte-identical.cd sdk/ruby && rake testwithMSB_RUBY_INTEGRATIONunset (agentd staged as above), run against the 0.6.14 pin before the merge from main: the extension compiles against the registry crates;17 tests, 13 assertions, 0 failures, 0 errors, 7 omissions(integration tests omitted without a virtualization host). Not re-run at 0.6.15;version_checkandcargo fetch --lockedwere.The pin probe against crates.io: 0.6.15 -> 200, 0.6.99 -> 404, no User-Agent -> 403.
ruby -ryaml -e 'YAML.load_file(".github/workflows/check.yml"); YAML.load_file(".github/workflows/release.yml")'passes;actionlintis not installed locally.No Rust source changed;
cargo fmt --checkand clippy are unaffected.Generated with Claude Code
https://claude.ai/code/session_01RqjuPiGZ34s5CPfayN8JN4