Skip to content

ci: adopt the gt-managed pipeline - #23

Closed
pedromvgomes wants to merge 5 commits into
mainfrom
chore/gt-pipeline
Closed

ci: adopt the gt-managed pipeline#23
pedromvgomes wants to merge 5 commits into
mainfrom
chore/gt-pipeline

Conversation

@pedromvgomes

@pedromvgomes pedromvgomes commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Fifth repository onto gt's pipeline, and the first to use tag-triggered delivery. Do not merge yet — see below.

What moved

Was Now
validate-topology.ymlvalidate ci-build.yml
ci.ymltest (type-check + tests) ci-test.yml
deploy.ymldeploy cd-deploy.yml, now tag-triggered
deploy.ymltest dropped — see attestation
deploy.ymlpaths-ignore dropped — see below

The behavioural change, stated plainly

This repository used to ship on every merge to main. It now ships when you tag. That is the point of the migration — it is the same delivery model as every other repo — but it is a real change: a fix reaches production when tagged, not when merged.

For a status page that matters during an incident, so workflow_dispatch stays on cd-orchestration as the immediate path.

v0.1.0 has been tagged at c1a9b45, the source currently running on Cloudflare, so the first tagged release has a predecessor rather than starting from nothing.

What survives untouched

The production environment, the D1 migrations, the worker secret push and wrangler deploy all move across as they were. Any protection rules on the environment keep applying, because environment: is declared on the job inside cd-deploy.yml.

Two things deliberately dropped

deploy.yml's test job. It re-ran the same type-check and suite ci-test already ran on the PR. cd-orchestration verifies the tagged tree carries a passing gt/validated-tree attestation instead — proving that exact tree passed, rather than proving it again on a fresh runner.

paths-ignore. topology.yaml is still fetched at runtime, so those changes genuinely need no deploy — but tagging is now the decision to ship. Keeping both would be two mechanisms answering one question. If wasted deploys turn out to matter, cd-preflight is where that belongs.

Two gt changes this repository forced

Both the same root cause: a called workflow can only narrow what its caller grants, so a stage can never ask a permission back.

  • CI stages now get packages: readpnpm install resolves @wardnet/* from GitHub Packages.
  • CD stages now get it too, for the same install before wrangler deploy.

Expected to fail until gt v1.0.0 ships

Callers pin reusable-*.yml@v1, which does not exist yet — the workflow fails to resolve with zero jobs started.

Before merging

Branch protection must move to exactly one required check, ci-gate, in the same window as the merge. gt repo settings apply does this.

Moves this repository onto gt's centrally-owned CI orchestration. gt owns the
entry point and the shared stages; the ci-* files hold this repo's own work and
gt never touches them again.

validate-topology.yml's schema check becomes ci-build, and ci.yml's job becomes
ci-test. That job keeps type-check and tests together deliberately: splitting
them across two stages would mean installing the workspace twice, and this
repository already made the opposite trade.

Both stages need packages: read to resolve @wardnet/* from GitHub Packages
during install. A called workflow can only narrow what its caller grants, so
that permission now comes from the orchestrator — without it the install would
fail to authenticate. Migrating this repository is what surfaced it.

The concurrency group ci.yml declared is now in the orchestrator, with one
difference: superseded pull-request runs are still cancelled, but
default-branch runs are not, because cancelling one would discard the
validated-tree attestation it was about to record.

deploy.yml is untouched and CD stays disabled. Deployment here happens on merge
to main, whereas gt's delivery pipeline is tag-triggered — forcing this repo
into that shape would change when it ships, which is not a governance decision.

Branch protection needs exactly one check after this: `ci-gate`.
Completes the pipeline adoption by moving delivery onto gt's tag-triggered CD,
so this repository ships the same way as every other one.

deploy.yml becomes cd-deploy.yml, keeping the production environment, the D1
migrations, the worker secret push and the wrangler deploy exactly as they
were. Two things around it are deliberately dropped:

Its test job, which re-ran the same type-check and suite ci-test already ran on
the pull request. cd-orchestration verifies the tagged tree carries a passing
gt/validated-tree attestation instead — proving that exact tree passed, rather
than proving it again on a fresh runner.

The paths-ignore that skipped deploys for topology.yaml, docs and markdown.
topology.yaml is still fetched at runtime so those changes still need no
deploy, but tagging is now the decision to ship, and inferring that from paths
alongside an explicit tag would be two mechanisms answering one question.

The trade this makes is real: a fix now reaches production when it is tagged
rather than when it is merged. For a status page that matters during an
incident, so workflow_dispatch remains on cd-orchestration as the immediate
path.

v0.1.0 has been tagged at c1a9b45, the source currently running on Cloudflare,
so the first tagged release has a predecessor rather than starting from
nothing.
cd-verify was a stub in every repository; this gives it something real to do,
and delivery gets a check it did not have before rather than just moving where
the old one lived.

Two endpoints are probed, and they fail differently on purpose. The page root
exercises the edge assets. /api/status exercises the worker and its D1 read —
so a migration that broke the schema returns 500 there while the page still
returns 200. Checking only the root would miss precisely the failure a deploy
applying migrations is most likely to introduce.

Two consecutive passes are required. A single success can be a lucky hit on an
edge node still serving the previous version, so a failure between two passes
resets the count rather than decrementing it: the claim being made is that the
deploy is consistently healthy, not that it answered once.

Requests bypass cache for the reason 6e0e383 records for the probes themselves
— a cached 200 from before the deploy says nothing about the deploy.

Verified against the live site before committing: two consecutive healthy
responses pass, an unreachable host reports 000 and fails, and a path that
returns the SPA shell for /api/status fails on the JSON check rather than
passing on its 200. That last case is why the body is validated and not just
the status code.

What it does not prove is global propagation — nothing reachable from a single
runner can. It proves the deploy is not obviously broken.
gt grouped CD runs by ref; wardnet-design-system showed why that is wrong —
a release pushing one tag per package would have its runs racing to publish the
same versions. Over-serialising costs latency, under-serialising corrupts a
release.
bulwark no longer takes tests-mode or go-report as action inputs; which side
produces coverage is coverage.source in .bulwark.yml. Without this the removed
inputs would be silently ignored and bulwark would re-run the suite ci-test
already ran.
@pedromvgomes

Copy link
Copy Markdown
Contributor Author

Closing in favour of #26, which is the branch that carried on.

Tip of this branch at close: 4dd40d44c52409bb0b0f53c5d39e72073b4717fd (chore/gt-pipeline). Nothing here is lost — #26 supersedes it and goes further: it moves ci.yml's jobs into the gt ci-* stages and deletes ci.yml, which this branch did not do, and it is synced against gt 1.3.0 rather than the 1.0.0 policy both branches were originally rendered from.

Recorded here rather than just deleting the branch so the SHA stays reachable if anyone needs to diff the two approaches later.

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