Repository navigation
feat(stack-encrypt): run vendor KMS backends through a static keyset registry (CIP-3965) #163
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
| name: Tests (crates) | |
| # The stack-* crates and their three node bindings, ported from | |
| # cipherstash-suite's `test-unit.yml` (the stack crates' share of it), | |
| # `test-stack-auth.yml` and `test-stack-profile.yml` when the set was imported | |
| # to the repository root. | |
| # | |
| # This is the only workflow that runs the stack-encrypt `tests/ui` trybuild | |
| # binary: `crap-crates.yml` and `mutants.yml` both filter out `binary(ui)`. | |
| # `scripts/__tests__/crates-ci.test.mjs` pins that, and that every mise task in | |
| # the five crate `tasks.toml` files is reached from a root workflow. | |
| # | |
| # The filter keeps Markdown in: `stack-auth`'s crate docs are its README | |
| # (`#![doc = include_str!("../README.md")]`), so a README edit changes the | |
| # doctests. The suite's `!**.md` exclude would have skipped that change. | |
| on: | |
| pull_request: | |
| paths: | |
| - packages/stack-auth/** | |
| - packages/stack-profile/** | |
| - packages/stack-kms/** | |
| - packages/stack-encrypt/** | |
| - packages/stack-encrypt-derive/** | |
| - packages/stack-guest-abi/** | |
| - languages/typescript/packages/auth/** | |
| - languages/typescript/packages/profile/** | |
| - languages/typescript/packages/stack-auth-wasm/** | |
| # Root manifest, lockfile, toolchain pin and cargo config: each can | |
| # change what compiles without touching a crate. The toolchain pin in | |
| # particular changes the trybuild UI snapshots. | |
| - Cargo.toml | |
| - Cargo.lock | |
| - mise.toml | |
| - mise.test.toml | |
| - .cargo/config.toml | |
| - .config/nextest.toml | |
| - pnpm-lock.yaml | |
| - .github/workflows/tests-crates.yml | |
| push: | |
| branches: [main] | |
| paths: | |
| - packages/stack-auth/** | |
| - packages/stack-profile/** | |
| - packages/stack-kms/** | |
| - packages/stack-encrypt/** | |
| - packages/stack-encrypt-derive/** | |
| - packages/stack-guest-abi/** | |
| - languages/typescript/packages/auth/** | |
| - languages/typescript/packages/profile/** | |
| - languages/typescript/packages/stack-auth-wasm/** | |
| - Cargo.toml | |
| - Cargo.lock | |
| - mise.toml | |
| - mise.test.toml | |
| - .cargo/config.toml | |
| - .config/nextest.toml | |
| - pnpm-lock.yaml | |
| - .github/workflows/tests-crates.yml | |
| workflow_dispatch: {} | |
| permissions: | |
| contents: read | |
| defaults: | |
| run: | |
| shell: bash | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| RUST_BACKTRACE: full | |
| CARGO_TERM_COLOR: always | |
| CARGO_NET_GIT_FETCH_WITH_CLI: true | |
| NEXTEST_PROFILE: ci | |
| jobs: | |
| rust: | |
| name: fmt, clippy, nextest, doctests, rustdoc | |
| runs-on: blacksmith-16vcpu-ubuntu-2204 | |
| timeout-minutes: 60 | |
| env: | |
| # Job-level, not workflow-level: RUSTFLAGS replaces the | |
| # `[target.wasm32-unknown-unknown]` rustflags in .cargo/config.toml, | |
| # which the stack-auth-wasm build in `node-bindings` needs. | |
| RUSTFLAGS: "-D warnings" | |
| # nextest is pinned in mise.test.toml, which mise loads only in the | |
| # test environment. See the mise-action step. | |
| MISE_ENV: test | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| # At the repository root: the root mise.toml pins rust 1.94.1, which the | |
| # trybuild snapshots record. No install_args: `mise x` and `mise run` | |
| # install any tool of the toolset that is missing, uncached, so this | |
| # installs (and caches) the whole test toolset once. The cargo: tools | |
| # build from source. | |
| - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4 | |
| with: | |
| version: 2026.4.0 | |
| install: true | |
| working_directory: . | |
| cache: true | |
| # mise caches its rust install as a symlink into ~/.rustup, which the | |
| # cache does not hold. On a runner image that already carries a 1.94.1 | |
| # toolchain, a cache hit makes mise skip the components mise.toml | |
| # declares, and the lint steps below fail. A no-op when they are there. | |
| # Held by scripts/__tests__/crates-ci.test.mjs. | |
| - name: Add rustfmt and clippy to the pinned toolchain | |
| run: rustup component add rustfmt clippy | |
| - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| - name: rustfmt | |
| run: cargo fmt --all --check | |
| - name: clippy | |
| run: cargo clippy --locked --no-deps --workspace --all-targets --all-features -- -D warnings | |
| # Every test binary in the workspace, the trybuild `ui` binary included. | |
| - name: nextest | |
| run: mise x --env test -- cargo nextest run --locked --workspace --all-features | |
| - name: doctests | |
| run: mise run test:doc | |
| # Rustdoc with warnings as errors, one `doc:<crate>` task per crate. | |
| # `test:doc` runs the examples; only this sees a broken intra-doc link. | |
| - name: rustdoc | |
| run: mise run doc | |
| # The stack-encrypt examples need live credentials, so they are built | |
| # here and run in tests-golang.yml `live`. | |
| - name: Build the stack-encrypt examples | |
| run: cargo build --locked -p stack-encrypt --examples | |
| # The crates release-plz publishes from 0.1.0. `--dry-run` packages and | |
| # compiles each the way crates.io would (the three together, so | |
| # stack-encrypt resolves the other two from cargo's temporary local | |
| # registry), catching a publish-blocker on the PR instead of in | |
| # release-crates on main: missing metadata, or a runtime path dependency | |
| # with no `version`. No token. `--allow-dirty` tolerates files earlier | |
| # steps leave in the tree. Once a version is on crates.io, cargo warns | |
| # rather than fails on a dry run of it. | |
| # | |
| # A crate with a git dependency outside its dev-dependencies cannot be | |
| # published: cargo refuses to package it, and with a `version` beside the | |
| # `git` it would build against crates.io's crate of that name, not the | |
| # pinned rev. stack-kms and stack-encrypt pin vitaminc-kms to a git rev | |
| # until vitaminc releases its key-provider traits | |
| # (cipherstash/vitaminc#352). Such a crate is skipped here with a | |
| # WARNING, as `test-eql.yml` does for a stack-encrypt not yet on | |
| # crates.io: the PR cannot ship it, and release-crates fails loudly if a | |
| # version bump tries. Every other crate in the list is still verified. | |
| - name: Verify the stack-encrypt crates package cleanly for crates.io | |
| run: | | |
| crates=(stack-kms stack-encrypt-derive stack-encrypt) | |
| git_pinned=$(cargo metadata --locked --no-deps --format-version 1 | jq -r ' | |
| .packages[] | |
| | select(any(.dependencies[]; .kind != "dev" and ((.source // "") | startswith("git+")))) | |
| | .name') | |
| args=() | |
| for crate in "${crates[@]}"; do | |
| if grep -qx "$crate" <<<"$git_pinned"; then | |
| echo "::warning title=${crate} cannot be published::${crate} has a git dependency, so cargo cannot package it. The dry run skips it until that dependency is on crates.io." | |
| else | |
| args+=(-p "$crate") | |
| fi | |
| done | |
| if [ "${#args[@]}" -eq 0 ]; then | |
| echo "Every crate in the list has a git dependency; nothing to verify." | |
| exit 0 | |
| fi | |
| cargo publish --locked --dry-run --allow-dirty "${args[@]}" | |
| node-bindings: | |
| name: node bindings, napi typings, stack-auth-wasm | |
| runs-on: blacksmith-16vcpu-ubuntu-2204 | |
| timeout-minutes: 45 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| persist-credentials: false | |
| - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4 | |
| with: | |
| version: 2026.4.0 | |
| install: true | |
| working_directory: . | |
| install_args: rust aqua:wasm-bindgen/wasm-pack | |
| cache: true | |
| - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| - uses: pnpm/action-setup@v6.1.0 | |
| with: | |
| run_install: false | |
| - uses: actions/setup-node@v6.5.0 | |
| with: | |
| node-version: 22 | |
| cache: 'pnpm' | |
| # node-pty falls back to `node-gyp rebuild` on Linux during any | |
| # workspace install. See scripts/__tests__/workflow-node-gyp.test.mjs. | |
| - name: Install node-gyp | |
| run: npm install -g node-gyp | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| # Each builds the binding's cdylib, copies it next to the package, and | |
| # runs the package's vitest suite against it. | |
| - name: stack-auth node binding | |
| run: mise run test:integration:stack-auth | |
| - name: stack-profile node binding | |
| run: mise run test:integration:stack-profile | |
| # Each binding's `build:debug` is `napi build --dts native.d.ts`: napi | |
| # writes its generated typings to `native.d.ts`, which is committed, and | |
| # never to `index.d.ts`, which is hand-written. Nothing else regenerates | |
| # `native.d.ts`, so a `#[napi]` signature change that is not rebuilt and | |
| # committed would leave typings that disagree with the `.node` ABI. The | |
| # build must leave the tracked tree as it found it: `git status` also | |
| # catches an untracked file the build drops into the package. | |
| - name: Guard committed napi typings against drift | |
| run: | | |
| pnpm --filter @cipherstash/auth run build:debug | |
| pnpm --filter @cipherstash/profile run build:debug | |
| dirty=$(git status --porcelain -- languages/typescript/packages/auth languages/typescript/packages/profile) | |
| if [ -n "$dirty" ]; then | |
| echo "$dirty" | |
| git diff -- languages/typescript/packages/auth languages/typescript/packages/profile | |
| echo "::error::napi build changed the auth or profile binding — run 'pnpm --filter @cipherstash/auth --filter @cipherstash/profile run build:debug' and commit native.d.ts. index.d.ts is hand-written and napi must not write it." | |
| exit 1 | |
| fi | |
| # `test:integration:stack-auth` builds only the napi module, so the | |
| # wasm-inline Result tests self-skip there. Build the inline shim and | |
| # run them here. wasm-pack comes from the root mise.toml. | |
| - name: Build the wasm shim and run the wasm-inline Result tests | |
| run: | | |
| mise x -- pnpm --filter @cipherstash/auth run build:wasm | |
| pnpm --filter @cipherstash/auth exec vitest run __tests__/wasm-inline-result.test.ts | |
| - name: cargo check stack-auth-wasm (wasm32-unknown-unknown) | |
| run: cargo check --locked --target wasm32-unknown-unknown -p stack-auth-wasm | |
| - name: wasm-pack test stack-auth-wasm | |
| run: mise x -- wasm-pack test --node languages/typescript/packages/stack-auth-wasm |