chore(ci): make staging ephemeral — provision per deploy, destroy after - #832
Merged
Conversation
github-actions
Bot
force-pushed
the
chore/ephemeral-staging-deploys
branch
from
July 19, 2026 14:22
d6f2120 to
94d4b1c
Compare
manavgup
force-pushed
the
chore/ephemeral-staging-deploys
branch
2 times, most recently
from
July 19, 2026 14:23
d6f2120 to
57b4272
Compare
This was referenced Jul 19, 2026
manavgup
added a commit
that referenced
this pull request
Jul 19, 2026
CVE-2026-52869 (HIGH, fixed in 1.27.2) was published after #833 went green, failing the Trivy scan on main's post-merge Docker build — which skipped the deploy — and on PR #832's rebased checks. Lockfile-only bump; full verify passes (2134 tests). Co-authored-by: manavgup <manavgup@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
github-actions Bot
pushed a commit
that referenced
this pull request
Jul 19, 2026
Three times in four days a freshly published advisory failed the Trivy scan on code that was green at review time (#833's two rounds, then CVE-2026-52869 against mcp blocking the post-#833 deploy and #832). A hard fail-on-publish gate freezes every in-flight merge on findings unrelated to the change being merged. Add an OPA ignore policy: HIGH vulnerabilities published within the last 7 days are ignored, leaving a window to bump the dependency in an orderly PR. After 7 days the scan fails as before, so nothing can be ignored indefinitely. CRITICAL findings are never ignored, and findings without a parseable PublishedDate fail closed. Verified locally with trivy 0.70: the 7-day window still reports old HIGH CVEs on python:3.11-slim, and a test 10-year window suppresses them — confirming the rule keys on Severity + PublishedDate. Also add .trivyignore and .github/trivy/** to the workflow's path filters so editing scan-ignore rules re-triggers the scan. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
manavgup
force-pushed
the
chore/ephemeral-staging-deploys
branch
from
July 19, 2026 14:52
57b4272 to
641ee06
Compare
manavgup
added a commit
that referenced
this pull request
Jul 19, 2026
…837) 'Postgres integration tests' is a required status check on main, but its workflow is path-filtered to src/tests/dependency paths. A PR touching none of those (workflow-only or docs-only changes) never gets the check reported and stays BLOCKED forever — this deadlocked #836 and #832 despite every triggered check passing. Add GitHub's documented mirror pattern: an identically named job that succeeds instantly, triggered by the exact inverse filter (paths-ignore mirroring the real workflow's paths). Code PRs run the real tests; non-code PRs get the green check from the mirror. This PR unblocks itself the same way. Co-authored-by: manavgup <manavgup@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
manavgup
added a commit
that referenced
this pull request
Jul 19, 2026
Three times in four days a freshly published advisory failed the Trivy scan on code that was green at review time (#833's two rounds, then CVE-2026-52869 against mcp blocking the post-#833 deploy and #832). A hard fail-on-publish gate freezes every in-flight merge on findings unrelated to the change being merged. Add an OPA ignore policy: HIGH vulnerabilities published within the last 7 days are ignored, leaving a window to bump the dependency in an orderly PR. After 7 days the scan fails as before, so nothing can be ignored indefinitely. CRITICAL findings are never ignored, and findings without a parseable PublishedDate fail closed. Verified locally with trivy 0.70: the 7-day window still reports old HIGH CVEs on python:3.11-slim, and a test 10-year window suppresses them — confirming the rule keys on Severity + PublishedDate. Also add .trivyignore and .github/trivy/** to the workflow's path filters so editing scan-ignore rules re-triggers the scan. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
manavgup
force-pushed
the
chore/ephemeral-staging-deploys
branch
from
July 19, 2026 15:39
641ee06 to
7d211d6
Compare
manavgup
added a commit
that referenced
this pull request
Jul 19, 2026
Three times in four days a freshly published advisory failed the Trivy scan on code that was green at review time (#833's two rounds, then CVE-2026-52869 against mcp blocking the post-#833 deploy and #832). A hard fail-on-publish gate freezes every in-flight merge on findings unrelated to the change being merged. Add an OPA ignore policy: HIGH vulnerabilities published within the last 7 days are ignored, leaving a window to bump the dependency in an orderly PR. After 7 days the scan fails as before, so nothing can be ignored indefinitely. CRITICAL findings are never ignored, and findings without a parseable PublishedDate fail closed. Verified locally with trivy 0.70: the 7-day window still reports old HIGH CVEs on python:3.11-slim, and a test 10-year window suppresses them — confirming the rule keys on Severity + PublishedDate. Also add .trivyignore and .github/trivy/** to the workflow's path filters so editing scan-ignore rules re-triggers the scan. Co-authored-by: manavgup <manavgup@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The staging stack (web + worker machines, Postgres, volume) ran 24/7 as a full clone of production but is only exercised for ~10 minutes per deploy as a smoke-test gate. Together with a leftover managed Upstash Redis it accounted for roughly $25/mo of the ~$61 June Fly invoice. Rework the deploy pipeline so staging only exists during a deploy: - deploy-staging provisions from a clean slate (destroy leftovers, then create app + volume + Postgres + attach + secrets). A fresh empty DB each run is fine: migrations run via the alembic release_command, the smoke-test JWT resolves users from its sub claim without a DB lookup, and all checked endpoints return valid empty collections. - new teardown-staging job destroys the staging app and database after the smoke gate, pass or fail. - staging queues on the shared self-hosted Redis at DB index 1 (production uses DB 0) instead of a dedicated Upstash instance. - staging reuses the production docling sidecar (stateless, scale-to-zero) instead of deploying its own 4GB-image copy per run; wikimind-staging-docling is no longer referenced. Part of the Fly.io cost-reduction effort. The always-on staging apps themselves are destroyed on the first deploy after this merges (the clean-slate provision step replaces them, and teardown removes them). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
github-actions
Bot
force-pushed
the
chore/ephemeral-staging-deploys
branch
from
July 19, 2026 15:53
7d211d6 to
eb22ff8
Compare
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The June Fly.io invoice was ~$61/mo. About $25/mo of that was the staging environment: a 24/7 clone of production (web + worker machines, Postgres cluster, volume) plus a leftover managed Upstash Redis — all to serve as a smoke-test gate that's actually exercised for about ten minutes per deploy.
Solution
Staging now only exists while a deploy is running. The pipeline provisions a fresh staging stack, smoke-tests it, promotes to production, and destroys staging — pass or fail.
deploy-stagingdestroys any leftovers, then creates the app, volume, and Postgres, attaches, and sets secrets. Destroy-then-create (rather than create-if-absent) is immune to partial state from a failed teardown.release_command; the smoke-test JWT resolvesuser_idfrom itssubclaim with no DB lookup (src/wikimind/api/deps.py); every checked endpoint returns a valid empty array on a fresh DB.teardown-stagingjob withif: always()destroyswikimind-staging+wikimind-staging-dbafter the smoke gate.wikimind-redisat DB index 1 (prod uses DB 0). No dedicated staging Redis. (Already live: I repointed the running staging at DB 1 and destroyed the Upstash instance — verified healthy, worker connected,background_mode: arq.)wikimind-staging-doclingis no longer referenced and will be destroyed after this merges.Changes
.github/workflows/deploy.yml— provision/teardown jobs, staging Redis URL staged by CI, staging docling deploy removed, header docs rewrittenfly.staging.toml— docling URL → prod sidecar; header notes the ephemeral modelREADME.md,docs/adr/adr-025-docling-serve-sidecar.md— deploy-flow descriptions updatedNotes
wikimind-staging-doclingmanually once this lands.deploy-stagingtimeout bumped 10 → 15 min); staging DB state never persists between runs.🤖 Generated with Claude Code