ci: add the release pipelines for the stack-* crates and @cipherstash/auth, inert - #1002
Conversation
|
b4efd51 to
d669946
Compare
bceb0bc to
1ad1055
Compare
d669946 to
db6b3b9
Compare
Mutation testing (cargo-mutants,
|
classify() gains an `auth` output keyed on the `@cipherstash/auth` prefix, and `js` no longer counts the seven auth packages. Without the branch an unpublished auth version reads as `js`, and `changeset publish` would pack the platform workspaces with no binary in them. The gate prints and writes `auth=` beside `ffi=` and `js=`. A process test pins the three flags in GITHUB_OUTPUT, because a flag the gate never writes reads as "not in scope" in the workflow. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PaY5xYydZUWhv8Nex9Sw8a
release-plz.yml gains a `crates-armed` switch and a `release-crates` job that runs `release-plz release` on the root workspace, with `manifest_path: Cargo.toml` and `config: release-plz.toml`. It is its own job because a step in `release` would inherit the EQL gate. A crates.io preflight decides whether the GPG key is imported, as the EQL job does. The push filter adds the two crates and the root release-plz inputs. scripts/eql-pipeline-armed.mjs learns a `crates` line, keyed on the `@cipherstash/auth` entry of FROZEN_PUBLISHERS: no crate is in that npm map, and PR E deletes the auth entries when it moves both registries. While the entry is there the switch prints armed=false and the job is skipped. The root release-plz.toml puts stack-auth and stack-profile in one version group and sets `release = false` on the seven other members. cliff.toml is cipherstash-suite main's, unchanged. A new test holds the config to the workspace members and to the job's inputs. Registered in OIDC_JOBS and PERMISSIVE_NEEDS. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PaY5xYydZUWhv8Nex9Sw8a
- _build-auth-artifacts.yml: a reusable six-target napi matrix and a wasm job. Each target runs `napi build --platform --release --target ... --strip --dts native.d.ts --js false` (without `--js false` napi writes its own loader over the frozen index.js), then `git diff --exit-code` on the package, and `npm pack`s its platform package. linux-arm64-gnu runs on GitHub's ubuntu-24.04-arm. The wrapper is packed with pnpm, which rewrites its `workspace:*` platform peers, and the job checks the six peers equal the wrapper's version. Rust is the root mise 1.94.1; nothing restores a cache. - auth-preflight.yml: a workflow_dispatch dry run that builds the seven tarballs, checks each non-host binary with `file` and `readelf`, and smoke-installs the linux-x64-gnu pair. No id-token, no secret, no registry-url. - release.yml: `auth-artifacts` and `publish-auth` between `gate` and `release`, on the gate's new `auth` output. They publish platforms first and the wrapper last with --provenance, then tag and make a GitHub release; `release` waits for publish-auth to succeed whenever auth was in scope. - .changeset/config.json: a fixed group of the seven auth packages. - The seven auth manifests get `repository` with `directory`, which npm requires for a provenance publish. The gate hashes the 15 listed files, not package.json, so the freeze still passes. Inert: the auth packages are in FROZEN_PUBLISHERS, so any unpublished auth version fails the gate before `auth-artifacts` can run. Guards: auth-repository-urls and auth-build-artifacts tests; a derived lint-release-scope check that every reusable workflow release.yml calls, and every other caller of one, is linted. Registered in lint-no-workflow-caching TARGETS, tests-supply-chain paths, OIDC_JOBS, PERMISSIVE_NEEDS and EXPECTED_DISPATCHABLE. The frozen reason and the lint-no-auth-changeset message no longer say release.yml cannot build the binaries. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PaY5xYydZUWhv8Nex9Sw8a
db6b3b9 to
2a38fb4
Compare
freshtonic
left a comment
There was a problem hiding this comment.
I read the three commits in order. Each publishing job stays switched off until the FROZEN_PUBLISHERS entries for @cipherstash/auth are removed in PR E:
classify()now sends an unpublished auth version toauth, not tojs. Sochangeset publishcan no longer pack the platform workspaces with no binary in them.releasekeeps the publish-ffi rule for auth: ifauth=true,publish-authmust have succeeded. A skippedauth-artifactscannot letchangeset publishcontinue.crates-armeduses the sameFROZEN_PUBLISHERSmap througheql-pipeline-armed.mjs crates. Thus one deletion in PR E arms both the npm jobs and the crates.io job. The EQL readers stay unchanged.- The auth fixed group,
repository.directory, the caching-lint targets and the actionlint scope all agree with the new files.
I have one non-blocking point, in an inline comment. The six-platform build has not run yet, because auth-preflight.yml is dispatch-only. Its musl leg also differs from the proven FFI recipe. Please run the preflight before PR E arms the pipeline.
The missing changeset is correct: this PR changes only CI and repository tooling, and changesets for the auth packages stay blocked until PR E.
| working-directory: languages/typescript/packages/auth | ||
| env: | ||
| TARGET: ${{ matrix.target }} | ||
| run: mise x -- pnpm exec napi build --platform --release --target "$TARGET" --strip --dts native.d.ts --js false |
There was a problem hiding this comment.
Non-blocking. The linux-x64-musl leg installs musl-tools, but it does not set a musl linker or RUSTFLAGS="-C target-feature=-crt-static". _build-ffi-artifacts.yml sets both for its musl leg. The musl target defaults to crt-static, and rustc drops a cdylib under crt-static. With -crt-static and no CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER, the final link may use the host glibc cc. That can give a .node file that needs libc.so.6.
napi CLI v2 may add -crt-static for musl targets itself, so this could be fine. No CI run has built this matrix yet, though. The readelf check in auth-preflight.yml will catch a glibc-linked binary. But release.yml does not run that check, so the only guard is a person who dispatches the preflight. Please run auth-preflight.yml once before PR E, or use the FFI leg's linker setup here. Both are acceptable.
This PR adds the jobs that will publish
stack-authandstack-profileto crates.io, and@cipherstash/authwith its six platform packages to npm. Each platform package holds the native binary for one platform, such asdarwin-arm64. Every new job stays switched off, which the title calls "inert". PR E switches them on.This is PR D of 6 in the stack crates import. The import moves six Rust crates, their bindings and the Go module here from the private
cipherstash/cipherstash-suiterepository, with their history. The stack crates import plan in Linear lists every step, and Linear issue CIP-4274 tracks the work.This PR is stacked on PR C, #1003. Its base branch is PR C's branch, so the diff shows only this PR's own changes. It stays a draft until the freeze on Friday 2 October 2026, Pacific time. The freeze is the window in which the six PRs merge in order: #1000, #1001, #1003, #1002, #1009, then #1010. PR D is #1002, so it merges after PR C, #1003.
Read the three commits in order, starting with the release gate
Read each commit in full. Start with commit 1, because the jobs in commit 3 run on the
authoutput that it adds. Then compare the table under "Nothing in this PR can publish" with the job conditions in the diff.Each commit makes one change
d9af16dfmakes the release gate report a separateauthoutput. The release gate,scripts/release-gate.mjs, runs on every push tomainbefore anything publishes to npm. It writes flags such asffi=trueandjs=truethat tellrelease.ymlwhich publish jobs to run. Without anauthflag, a new auth version would count asjs. The npm publish step,changeset publish, would then pack the platform packages with no binary in them.4c6a49c0adds arelease-cratesjob torelease-plz.yml. release-plz is the tool that publishes Rust crates to crates.io. The job runs only when a newcrates-armedjob reports that the crates may publish. A rootrelease-plz.tomlgivesstack-authandstack-profileone shared version, and stops the other seven workspace members publishing.2a38fb4eadds the jobs that build and publish@cipherstash/auth, and the settings they need:_build-auth-artifacts.ymlbuilds the native binding for six platforms with napi, plus the WebAssembly wrapper. napi is the tool that builds Rust code into a Node.js native module.release.ymlgains anauth-artifactsjob and apublish-authjob. They publish the six platform packages first and the wrapper last.auth-preflight.ymlis a dry run that someone starts by hand. It builds all seven packages and test-installs the wrapper with itslinux-x64-gnupackage, but it cannot publish..changeset/config.jsongains a fixed group, a setting that keeps the seven auth packages on one shared version.repository.directory, thepackage.jsonfield that names the package's folder. npm needs it for a provenance publish, which records the workflow that built the package. A new test checks it.Nothing in this PR can publish
Each publishing job waits on a condition that stays false until PR E, and the dry run has no credentials. In the table, a frozen package is one that the release gate fails on if its version changes.
release-cratesinrelease-plz.ymlcrates-armedreports true. That needs the release gate's freeze on@cipherstash/authremoved, which PR E does.auth-artifactsandpublish-authinrelease.ymlauth=true. The auth packages are still frozen, so a version bump of one makes the gate fail first.auth-preflight.ymlcrates-armedreads the gate's@cipherstash/authentry, because no crate has an entry of its own there. PR E removes the auth entry, which switches on the npm jobs and the crates.io job together.The release workflows run only on pushes to
mainor when someone starts them by hand. Pushing this branch starts no release.The checks pass locally and in CI
index.jsunchanged. napi writes its own loader over that file unless the build passes--js false.workspace:*range with a real version when it packs.EXPECTED_DISPATCHABLEentry. That registration was already known to check nothing, so the survivor confirms it.Merge this PR with a merge commit after PR C
Merge it with a merge commit, which keeps every commit, and never with a squash or a rebase. PR E is built on these exact commits. A squash or a rebase would leave PR E based on commits that are not in
main.🤖 Generated with Claude Code
https://claude.ai/code/session_01PaY5xYydZUWhv8Nex9Sw8a