Version Packages (rc) #1886
Workflow file for this run
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: Test JS | |
| on: | |
| push: | |
| branches: | |
| - 'main' | |
| pull_request: | |
| branches: | |
| - "**" | |
| jobs: | |
| # Biome format + lint. Its own job (no DB, no credentials, node-agnostic) so it | |
| # surfaces as a distinct check and runs once rather than per Node matrix leg. | |
| # Gates on ERRORS only — warnings are allowed, so the `no-type-erasing-assertions` | |
| # plugin (at `warn`) surfaces new `as any`/`never`/`unknown` without blocking. | |
| # Tightening to fail on warnings is tracked in #625. | |
| lint: | |
| name: Lint (Biome) | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| steps: | |
| - name: Checkout Repo | |
| uses: actions/checkout@v6 | |
| - uses: pnpm/action-setup@v6.0.9 | |
| name: Install pnpm | |
| with: | |
| run_install: false | |
| - name: Install Node.js | |
| uses: actions/setup-node@v6.4.0 | |
| with: | |
| # Node 22 is the supply-chain hardening baseline every pnpm-using job | |
| # must pin (enforced by e2e/tests/supply-chain.e2e.test.ts). Lint is | |
| # runtime-agnostic, so a single pinned version is fine. | |
| node-version: 22 | |
| cache: 'pnpm' | |
| # node-pty's install hook falls back to `node-gyp rebuild` when no | |
| # linux-x64 prebuild matches; pnpm/action-setup v6 no longer ships it. | |
| - name: Install node-gyp | |
| run: npm install -g node-gyp | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Biome check (format + lint) | |
| run: pnpm run code:check | |
| run-tests: | |
| name: Run Tests (Node ${{ matrix.node-version }}) | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| strategy: | |
| matrix: | |
| node-version: [22, 24] | |
| # Postgres + EQL for the integration tests. Official EQL image — | |
| # PostgreSQL 17 with EQL pre-installed via /docker-entrypoint-initdb.d. | |
| # Pinned to eql-2.3.1 to match the EQL payload format the code emits | |
| # (protect-ffi 0.23.x); bump in lockstep with the protect-ffi upgrade. | |
| services: | |
| postgres: | |
| image: ghcr.io/cipherstash/postgres-eql:17-2.3.1 | |
| env: | |
| POSTGRES_USER: cipherstash | |
| POSTGRES_PASSWORD: password | |
| POSTGRES_DB: cipherstash | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd "pg_isready -U cipherstash -d cipherstash" | |
| --health-interval 2s | |
| --health-timeout 5s | |
| --health-retries 20 | |
| steps: | |
| - name: Checkout Repo | |
| uses: actions/checkout@v6 | |
| - uses: pnpm/action-setup@v6.0.9 | |
| name: Install pnpm | |
| with: | |
| run_install: false | |
| - name: Install Node.js | |
| uses: actions/setup-node@v6.4.0 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| cache: 'pnpm' | |
| # node-pty's install hook falls back to `node-gyp rebuild` when no | |
| # linux-x64 prebuild matches. pnpm/action-setup v6 no longer ships | |
| # node-gyp on PATH, so install it explicitly. | |
| - name: Install node-gyp | |
| run: npm install -g node-gyp | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| # Fail loudly if any live-test credential is missing, so the v3 live | |
| # matrix (and every other `describeLive` suite) can't silently skip. | |
| - name: Require CipherStash secrets | |
| uses: ./.github/actions/require-cs-secrets | |
| with: | |
| workspace-crn: ${{ vars.CS_WORKSPACE_CRN }} | |
| client-id: ${{ vars.CS_CLIENT_ID }} | |
| client-key: ${{ secrets.CS_CLIENT_KEY }} | |
| client-access-key: ${{ secrets.CS_CLIENT_ACCESS_KEY }} | |
| - name: Type tests (stack) | |
| run: pnpm --filter @cipherstash/stack run test:types | |
| # The v3 domain catalog lives in the test kit, and its | |
| # `satisfies Record<EqlV3TypeName, DomainSpec>` is what forces a new SDK | |
| # domain to be covered. That check only fires under `tsc`, so without this | |
| # step a domain added to stack would slip through untested. | |
| - name: Type tests (test-kit — enforces v3 domain coverage) | |
| run: pnpm --filter @cipherstash/test-kit run test:types | |
| # The adapter packages carry their own `.test-d.ts` type-contract guards | |
| # (the M1 client-surface / #622 erasure guards, the Supabase key-set | |
| # gating). They moved here from `@cipherstash/stack` in the #627 split but | |
| # were never wired into CI, so type-level regressions in them went | |
| # undetected — run them explicitly. | |
| - name: Type tests (stack-drizzle) | |
| run: pnpm --filter @cipherstash/stack-drizzle run test:types | |
| - name: Type tests (stack-supabase) | |
| run: pnpm --filter @cipherstash/stack-supabase run test:types | |
| # prisma-next's operator-capability gating is proven by `.test-d.ts` | |
| # `@ts-expect-error` assertions (an unsupported operator on a column | |
| # must be a compile error). Those only fire when tsc processes them, | |
| # so the package `typecheck` must run — and be required — here (#684). | |
| # Its tsconfig resolves stack subpaths to SOURCE, so no build step is | |
| # needed first. | |
| - name: Typecheck (prisma-next — enforces v3 operator-capability gating) | |
| run: pnpm --filter @cipherstash/prisma-next run typecheck | |
| - name: Lint — no hardcoded package-manager runners | |
| run: pnpm run lint:runners | |
| - name: Test — lint script self-tests | |
| run: pnpm run test:scripts | |
| - name: Create .env file in ./packages/protect/ | |
| run: | | |
| touch ./packages/protect/.env | |
| echo "CS_WORKSPACE_CRN=${{ vars.CS_WORKSPACE_CRN }}" >> ./packages/protect/.env | |
| echo "CS_CLIENT_ID=${{ vars.CS_CLIENT_ID }}" >> ./packages/protect/.env | |
| echo "CS_CLIENT_KEY=${{ secrets.CS_CLIENT_KEY }}" >> ./packages/protect/.env | |
| echo "CS_CLIENT_ACCESS_KEY=${{ secrets.CS_CLIENT_ACCESS_KEY }}" >> ./packages/protect/.env | |
| echo "DATABASE_URL=postgres://cipherstash:password@localhost:5432/cipherstash" >> ./packages/protect/.env | |
| - name: Create .env file in ./packages/stack/ | |
| run: | | |
| touch ./packages/stack/.env | |
| echo "CS_WORKSPACE_CRN=${{ vars.CS_WORKSPACE_CRN }}" >> ./packages/stack/.env | |
| echo "CS_CLIENT_ID=${{ vars.CS_CLIENT_ID }}" >> ./packages/stack/.env | |
| echo "CS_CLIENT_KEY=${{ secrets.CS_CLIENT_KEY }}" >> ./packages/stack/.env | |
| echo "CS_CLIENT_ACCESS_KEY=${{ secrets.CS_CLIENT_ACCESS_KEY }}" >> ./packages/stack/.env | |
| echo "DATABASE_URL=postgres://cipherstash:password@localhost:5432/cipherstash" >> ./packages/stack/.env | |
| - name: Create .env file in ./packages/protect-dynamodb/ | |
| run: | | |
| touch ./packages/protect-dynamodb/.env | |
| echo "CS_WORKSPACE_CRN=${{ vars.CS_WORKSPACE_CRN }}" >> ./packages/protect-dynamodb/.env | |
| echo "CS_CLIENT_ID=${{ vars.CS_CLIENT_ID }}" >> ./packages/protect-dynamodb/.env | |
| echo "CS_CLIENT_KEY=${{ secrets.CS_CLIENT_KEY }}" >> ./packages/protect-dynamodb/.env | |
| echo "CS_CLIENT_ACCESS_KEY=${{ secrets.CS_CLIENT_ACCESS_KEY }}" >> ./packages/protect-dynamodb/.env | |
| # Run TurboRepo tests | |
| - name: Run tests | |
| run: pnpm run test | |
| # CLI E2E tests drive the built `dist/bin/stash.js` through a real | |
| # pseudo-terminal via node-pty. Run via turbo so the `^build` + `build` | |
| # deps declared on the `test:e2e` task are honored. | |
| - name: Run CLI E2E tests | |
| run: pnpm exec turbo run test:e2e --filter stash | |
| e2e-tests: | |
| name: Run E2E Tests | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| # Auth-dependent suites in `e2e/` skip themselves unless these env vars | |
| # are set. We expose them at the job level so the wizard subprocess | |
| # picks them up via `process.env`. | |
| env: | |
| CS_WORKSPACE_CRN: ${{ vars.CS_WORKSPACE_CRN }} | |
| CS_CLIENT_ID: ${{ vars.CS_CLIENT_ID }} | |
| CS_CLIENT_KEY: ${{ secrets.CS_CLIENT_KEY }} | |
| CS_CLIENT_ACCESS_KEY: ${{ secrets.CS_CLIENT_ACCESS_KEY }} | |
| CS_ZEROKMS_HOST: https://ap-southeast-2.aws.zerokms.cipherstashmanaged.net | |
| CS_CTS_HOST: https://ap-southeast-2.aws.cts.cipherstashmanaged.net | |
| steps: | |
| - name: Checkout Repo | |
| uses: actions/checkout@v6 | |
| - uses: pnpm/action-setup@v6.0.9 | |
| name: Install pnpm | |
| with: | |
| run_install: false | |
| - name: Install Node.js | |
| uses: actions/setup-node@v6.4.0 | |
| with: | |
| node-version: 22 | |
| cache: 'pnpm' | |
| # node-pty's install hook falls back to `node-gyp rebuild` when no | |
| # linux-x64 prebuild matches. pnpm/action-setup v6 no longer ships | |
| # node-gyp on PATH, so install it explicitly. | |
| - name: Install node-gyp | |
| run: npm install -g node-gyp | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| # Auth-dependent `e2e/` suites skip themselves without these vars — a | |
| # silent skip would hide regressions behind a green job. Fail loudly. | |
| - name: Require CipherStash secrets | |
| uses: ./.github/actions/require-cs-secrets | |
| with: | |
| workspace-crn: ${{ vars.CS_WORKSPACE_CRN }} | |
| client-id: ${{ vars.CS_CLIENT_ID }} | |
| client-key: ${{ secrets.CS_CLIENT_KEY }} | |
| client-access-key: ${{ secrets.CS_CLIENT_ACCESS_KEY }} | |
| # Run the standalone `e2e/` workspace via turbo so the `^build` | |
| # dep on the `test:e2e` task builds cli + wizard first. CLI's own | |
| # E2E (`packages/cli/tests/e2e/**`) is covered by the `run-tests` | |
| # job above; we filter to the new workspace here to avoid duplication. | |
| - name: Run E2E tests | |
| run: pnpm exec turbo run test:e2e --filter @cipherstash/e2e | |
| # Verifies @cipherstash/stack/wasm-inline works under Deno — i.e. the | |
| # WASM build of protect-ffi 0.26+ and auth 0.40+ can round-trip an | |
| # encryption against ZeroKMS / CTS in a runtime with no native | |
| # bindings available. The deno.json deliberately omits --allow-ffi so | |
| # a silent fallback to the NAPI module is impossible. | |
| wasm-e2e-tests: | |
| name: Run WASM E2E Tests (Deno) | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| permissions: | |
| contents: read | |
| # CS_WORKSPACE_CRN is the single source of truth for workspace | |
| # identity and region — the stack /wasm-inline config requires it and | |
| # derives the AccessKeyStrategy region from it. | |
| env: | |
| CS_WORKSPACE_CRN: ${{ vars.CS_WORKSPACE_CRN }} | |
| CS_CLIENT_ID: ${{ vars.CS_CLIENT_ID }} | |
| CS_CLIENT_KEY: ${{ secrets.CS_CLIENT_KEY }} | |
| CS_CLIENT_ACCESS_KEY: ${{ secrets.CS_CLIENT_ACCESS_KEY }} | |
| steps: | |
| - name: Checkout Repo | |
| uses: actions/checkout@v6 | |
| with: | |
| persist-credentials: false | |
| - uses: pnpm/action-setup@v6.0.9 | |
| name: Install pnpm | |
| with: | |
| run_install: false | |
| - name: Install Node.js | |
| uses: actions/setup-node@v6.4.0 | |
| with: | |
| node-version: 22 | |
| cache: 'pnpm' | |
| # node-pty (a dev-dep of @cipherstash/cli used by its E2E PTY | |
| # tests) falls back to `node-gyp rebuild` when no prebuild matches | |
| # the runner, and pnpm/action-setup v6 no longer ships node-gyp on | |
| # PATH. The WASM smoke test itself uses no native modules — this | |
| # install only exists so the workspace-wide `pnpm install` step | |
| # below doesn't fail. | |
| - name: Install node-gyp | |
| run: npm install -g node-gyp | |
| - name: Install Deno | |
| uses: denoland/setup-deno@v2.0.5 | |
| with: | |
| deno-version: v2.x | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| # The Deno smoke test imports the locally-built dist/wasm-inline.js | |
| # via a file URL in e2e/wasm/deno.json — it needs a fresh build. | |
| - name: Build stack | |
| run: pnpm exec turbo run build --filter @cipherstash/stack | |
| # The e2e/wasm suites FAIL when any of the four CS_* env vars is | |
| # missing (requireEnv throws — no skip gating), so a rotated / cleared | |
| # secret can't hide a real WASM regression behind a green job. This | |
| # preflight just fails faster, before the Deno module downloads. | |
| - name: Require CipherStash secrets | |
| uses: ./.github/actions/require-cs-secrets | |
| with: | |
| workspace-crn: ${{ vars.CS_WORKSPACE_CRN }} | |
| client-id: ${{ vars.CS_CLIENT_ID }} | |
| client-key: ${{ secrets.CS_CLIENT_KEY }} | |
| client-access-key: ${{ secrets.CS_CLIENT_ACCESS_KEY }} | |
| - name: Run Deno WASM smoke test | |
| working-directory: e2e/wasm | |
| run: deno task test | |
| run-tests-bun: | |
| name: Run Tests (Bun) | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| continue-on-error: true | |
| services: | |
| postgres: | |
| image: ghcr.io/cipherstash/postgres-eql:17-2.3.1 | |
| env: | |
| POSTGRES_USER: cipherstash | |
| POSTGRES_PASSWORD: password | |
| POSTGRES_DB: cipherstash | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd "pg_isready -U cipherstash -d cipherstash" | |
| --health-interval 2s | |
| --health-timeout 5s | |
| --health-retries 20 | |
| steps: | |
| - name: Checkout Repo | |
| uses: actions/checkout@v6 | |
| - uses: oven-sh/setup-bun@v2 | |
| - uses: pnpm/action-setup@v6.0.9 | |
| name: Install pnpm | |
| with: | |
| run_install: false | |
| - name: Install Node.js | |
| uses: actions/setup-node@v6.4.0 | |
| with: | |
| node-version: 22 | |
| cache: 'pnpm' | |
| - name: Install node-gyp | |
| run: npm install -g node-gyp | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Create .env file in ./packages/protect/ | |
| run: | | |
| touch ./packages/protect/.env | |
| echo "CS_WORKSPACE_CRN=${{ vars.CS_WORKSPACE_CRN }}" >> ./packages/protect/.env | |
| echo "CS_CLIENT_ID=${{ vars.CS_CLIENT_ID }}" >> ./packages/protect/.env | |
| echo "CS_CLIENT_KEY=${{ secrets.CS_CLIENT_KEY }}" >> ./packages/protect/.env | |
| echo "CS_CLIENT_ACCESS_KEY=${{ secrets.CS_CLIENT_ACCESS_KEY }}" >> ./packages/protect/.env | |
| echo "DATABASE_URL=postgres://cipherstash:password@localhost:5432/cipherstash" >> ./packages/protect/.env | |
| - name: Create .env file in ./packages/stack/ | |
| run: | | |
| touch ./packages/stack/.env | |
| echo "CS_WORKSPACE_CRN=${{ vars.CS_WORKSPACE_CRN }}" >> ./packages/stack/.env | |
| echo "CS_CLIENT_ID=${{ vars.CS_CLIENT_ID }}" >> ./packages/stack/.env | |
| echo "CS_CLIENT_KEY=${{ secrets.CS_CLIENT_KEY }}" >> ./packages/stack/.env | |
| echo "CS_CLIENT_ACCESS_KEY=${{ secrets.CS_CLIENT_ACCESS_KEY }}" >> ./packages/stack/.env | |
| echo "DATABASE_URL=postgres://cipherstash:password@localhost:5432/cipherstash" >> ./packages/stack/.env | |
| - name: Create .env file in ./packages/protect-dynamodb/ | |
| run: | | |
| touch ./packages/protect-dynamodb/.env | |
| echo "CS_WORKSPACE_CRN=${{ vars.CS_WORKSPACE_CRN }}" >> ./packages/protect-dynamodb/.env | |
| echo "CS_CLIENT_ID=${{ vars.CS_CLIENT_ID }}" >> ./packages/protect-dynamodb/.env | |
| echo "CS_CLIENT_KEY=${{ secrets.CS_CLIENT_KEY }}" >> ./packages/protect-dynamodb/.env | |
| echo "CS_CLIENT_ACCESS_KEY=${{ secrets.CS_CLIENT_ACCESS_KEY }}" >> ./packages/protect-dynamodb/.env | |
| # Build with Node (turbo/tsup need Node), then run tests with Bun | |
| - name: Build packages | |
| run: pnpm turbo build --filter './packages/*' | |
| - name: Run tests with Bun | |
| run: | | |
| for dir in packages/schema packages/protect packages/stack packages/protect-dynamodb packages/stack-forge; do | |
| if [ -f "$dir/vitest.config.ts" ] || [ -f "$dir/package.json" ]; then | |
| echo "--- Testing $dir ---" | |
| (cd "$dir" && bunx --bun vitest run) || true | |
| fi | |
| done |