Skip to content

ci: unify workflow shape (parallel jobs, composites, weekly cargo-deny)#1

Merged
jaredLunde merged 10 commits into
mainfrom
ci/unify-workflows
May 19, 2026
Merged

ci: unify workflow shape (parallel jobs, composites, weekly cargo-deny)#1
jaredLunde merged 10 commits into
mainfrom
ci/unify-workflows

Conversation

@jaredLunde
Copy link
Copy Markdown
Contributor

Summary

  • Same parallel-job shape as beyondoss/objects#2 and beyondoss/kv#1. Five jobs (`lint`, `generate-check`, `rust-test`, `handoff-test`, `ts-test`) + a `sqlx-check` job specific to auth.
  • New `.github/actions/setup-pgrx` composite encapsulates the PG18 dev-headers install + pgrx .so build + docker cp into the postgres service. Inputs: `extension-crate`, `built-so-name`, `install-so-name` (auth's .so loses the `lib` prefix on install).
  • `generate-check` now diffs `sdk/ts/src/react/types.ts` too — react types were regenerated but the old git-diff path list omitted them, so drift in the React SDK was silent.
  • Migrates `.cargo/audit.toml`'s single `RUSTSEC-2023-0071` suppression into `deny.toml`'s `[advisories.ignore]` with reason preserved. `.cargo/audit.toml` deleted.
  • Drops `build:rs:release` (release workflow scope). Adds weekly cargo-deny.

Target: ≤8m cold.

Test plan

  • All six jobs run in parallel and pass
  • Cold wall-clock ≤8m
  • `sqlx-check` correctly verifies the offline cache against the loaded extension + applied migrations
  • `handoff-test` passes without timeout bumps introduced in this PR
  • generate-check correctly diffs the react types output

🤖 Generated with Claude Code

jaredLunde and others added 10 commits May 19, 2026 12:05
Same five-job shape that landed in objects#2 and kv#1, plus auth-specific
pgrx + sqlx + migration scaffolding factored into a vendored composite
.github/actions/setup-pgrx. Each postgres-needing job (sqlx-check,
rust-test, handoff-test, ts-test) declares its own services.postgres,
builds the .so once per job with a per-job rust-cache slot, and runs
migrate before testing.

generate-check now diffs sdk/ts/src/react/types.ts in addition to
openapi/v1.json and sdk/ts/src/types.ts — react types were being
regenerated by `mise run generate:types` but the git diff path list
didn't include them, so drift in the React SDK types went silent.

Inline dprint fmt in both generate-types scripts mirrors
objects/sdk/ts/scripts/generate-types.mjs. mise's generate:types task
already calls dprint at the end, but having the scripts dprint their own
output keeps them self-sufficient when invoked directly.

Migrates .cargo/audit.toml's single RUSTSEC-2023-0071 suppression into
deny.toml's [advisories.ignore], with the same upstream-no-fix reason
preserved. .cargo/audit.toml deleted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
\`\${{ job.services.postgres.id }}\` is not valid in job-level \`env:\` —
GitHub Actions only exposes github/needs/secrets/inputs/vars contexts
there. Caused the workflow file to be rejected at parse time with
zero jobs run.

Move POSTGRES_CONTAINER off job env, plumb it as a setup-pgrx input
instead.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitHub Actions evaluates \${{ }} inside YAML description strings even
though they're documentation, not expressions. Rephrased to plain text
so the action manifest loads.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
back_to_back_handoffs_under_load measures customer-visible availability
during 5 consecutive handoffs at 4-way concurrency. Each handoff's
graceful_shutdown sends Connection: close (or RST) to keep-alived
clients exactly once — and a real-world HTTP client retries idempotent
GETs through that.

HealthzLoop was counting the first connection-reset on each retired
keep-alive as an error, so the noise floor on a 2-core CI runner was
~2 errors per handoff × 5 handoffs ≈ 10 errors / ~900 acks = 1.11%,
barely above the test's <1% threshold. The threshold reflects the
customer contract; the test client just wasn't measuring the same
thing customers measure.

Single-retry on transient error matches reqwest's own default for
hyper connection-pool eviction in newer versions and what every
production HTTP client does for GET. Doesn't loosen what the test
asserts about handoff seamlessness — just stops conflating socket
churn with service unavailability.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The split-out shape (sqlx-check / rust-test / handoff-test / ts-test as
4 parallel jobs) was costing wall-clock instead of saving it. Each job
needs the same workspace+pgrx build, and on a 2-core ubuntu-latest the
~3m pgrx build + ~4m auth-server compile dominates each job. Parallelism
ran 4× the same compile, and rust-cache slots can't be shared across
jobs without stomping. Net result: 17m warm-cache wall clock vs auth's
old 14m monolithic CI.

Consolidate the postgres-needing jobs into one server-tests job that
runs all phases sequentially after one cargo build. Keep lint and
generate-check as parallel fast-fail jobs. Expected wall clock: ~10–12m
(better than old 14m, but the parallelism win is bounded by the slowest
job — server-tests — and the slowest job is bounded by the compile +
e2e suite cost).

kv and objects keep the split-job shape because they have no pgrx
extension and no postgres service — their per-job compiles share the
same dependency tree and the rust-cache works well across them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The consolidated single-job experiment was strictly worse (23m vs 17m
on the parallel-jobs shape) — sequential addition exceeds the
parallel-max even after eliminating duplicate compiles, because the
slowest job (ts-test) is roughly half the workload.

Real win: remove test:integration:rs from test:integration:ts's deps
in mise.toml. The dep added defensive ordering but cost ~3–6m of
duplicated compile+test on every CI run (test:integration:rs ran in
its own rust-test job AND again as a transitive dep of ts-test). Local
devs can chain manually if they want that ordering.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
verify_step_up tracked the last-used step but only rejected reuse when
last_step >= now_step — same-step replays. With TOTP::new(..., skew=1, ...)
a code legitimately used at step N is still cryptographically valid at
step N+1 (the ±1 step skew tolerance for clock drift), so it could be
replayed cleanly across a step boundary.

Surfaced as a flaky integration test on slow CI:
sessions::totp_step_up_replay_in_same_window_rejected occasionally
crossed the 30s step boundary between first and second uses (Argon2id
signup + 4 HTTP roundtrips × slow runner), then the second use was
accepted because last_step < now_step despite the code still being
within the skew window.

Fix: reject reuse whenever |now_step - last_step| <= skew. Same atomic
write of last_used_at on success; replay window is now the full
~90-second skew range that verify_code accepts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jaredLunde jaredLunde merged commit dcdb884 into main May 19, 2026
6 checks passed
@jaredLunde jaredLunde deleted the ci/unify-workflows branch May 19, 2026 21:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant