L01 CI/CD pipeline anatomy
Lesson 00 named the current delivery paths. This lesson teaches how to read a workflow file as a delivery graph: a trigger starts a run, jobs form the graph, steps do the work, gates decide whether the graph can advance, artifacts carry evidence forward, environments receive promoted output, and feedback tells the operator what happened.
The examples stay anchored in the lab. fos-workbench uses
.github/workflows/ci.yml to validate tutorial/docs changes and publish the
docs surface. The sibling fos shape uses pr.yml, staging.yml, and
release.yml to validate an app, promote it to Cloudflare Pages staging, and
then release production behind a GitHub environment gate.
Azure is not part of the current delivery-platform operating model. The Azure
workflow remains deployment-target validation and historical context, not the
pipeline used to ship the tutorial/docs site or sibling fos app.
Anatomy terms
Section titled “Anatomy terms”| Term | How to read it in YAML | Lab example |
|---|---|---|
| Trigger | The on: block that decides when the graph starts. | fos-workbench starts CI on pull requests and pushes to main; sibling fos starts production release on a v* tag or manual dispatch. |
| Job | A named node under jobs: with its own runner, permissions, timeout, and steps. | editor, serving, and deploy-docs are separate fos-workbench jobs. |
| Step | One action or command inside a job. | npm run typecheck, uv run ... dbt test, actions/upload-artifact@v4, and wrangler pages deploy are steps. |
| Gate | A condition that must pass before later work matters. | Tests, generated-reference drift checks, needs: [editor, serving], branch/tag filters, and the sibling fos production environment approval gate. |
| Artifact | A generated file or bundle kept for a later job, deployment, or reader. | Generated OpenAPI/dbt references, the evaluation snapshot, the serving DuckDB warehouse, the Astro docs bundle, the Observable dashboard bundle, and sibling fos SvelteKit build output. |
| Secret | A credential supplied by GitHub at runtime, not committed to the repo. | deploy-docs uses secrets.CLOUDFLARE_API_TOKEN and secrets.CLOUDFLARE_ACCOUNT_ID for Cloudflare Pages publishing. |
| Environment | A named runtime place or protected release target. | dataplatform.kodeklaus.dk, Cloudflare Pages staging for sibling fos, Cloudflare Pages production, and the GitHub production environment gate. |
| Deployment job | A job that publishes or promotes output after validation. | deploy-docs publishes docs-site/dist; sibling staging.yml and release.yml deploy the app. |
| Smoke check | A post-deploy runtime check that proves the environment behaves. | fos-workbench checks public analytical-surface routes; sibling fos checks login reachability and private API authorization boundaries. |
| Failure mode | The reason a graph stops or a release is unsafe. | Typecheck failure, reference drift, missing uploaded artifacts, failed dbt or pytest runs, missing Cloudflare credentials, a blocked production approval, or a failed smoke check. |
Read the graph, not the command wall
Section titled “Read the graph, not the command wall”Start with the YAML shape instead of the shell commands.
- Read
on:first. This tells you whether the run is a pull-request validation path, a push-to-main publish path, a tag release path, a manual run, or scheduled automation. - Read the job names next. Job names usually reveal the graph: validation jobs first, deployment and publishing jobs after them.
- Look for
needs,if, path filters, branch filters, tag filters, and GitHub environments. Those are gates. - Look for upload/download artifact steps. Those show what one job proves or produces for the next job.
- Look for credentials under
env:andsecrets.*. Those mark the boundary where CI can become CD without storing secrets in the repo. - Look for smoke checks after deploy commands. Those are feedback from the runtime environment, not just from the build.
In fos-workbench, editor and serving are validation gates. They prove
that generated references are current, TypeScript checks pass, Evalite passes,
dbt compiles and tests, Python tests pass, Dagster definitions validate, and
the streaming marker e2e still works. deploy-docs is a deployment and
publishing job. It runs only after those gates pass and only on main.
That separation matters: validation gates answer “is this change trustworthy?” Deployment and publishing jobs answer “can we put the trusted output in an environment and prove it works there?”
fos-workbench graph walk-through
Section titled “fos-workbench graph walk-through”The main file is .github/workflows/ci.yml.
| Trigger | Gate | Artifact | Environment | Feedback |
|---|---|---|---|---|
Pull request to main | editor typecheck/test/eval and serving dbt/pytest/Dagster/streaming checks | Generated references and snapshots are checked for drift or produced inside CI, but nothing is published | None | Pull request status checks show whether the change can merge |
Push to main | deploy-docs waits for needs: [editor, serving] | Generated OpenAPI/dbt references, evaluation snapshot, serving DuckDB warehouse, Astro docs bundle, Observable dashboard bundle | Cloudflare Pages docs site at dataplatform.kodeklaus.dk | Public analytical-surface route checks after deploy |
The artifact flow is the promotion clue. serving uploads generated docs
references and the serving DuckDB warehouse. editor uploads the evaluation
snapshot. deploy-docs downloads those artifacts, builds docs-site/dist,
adds the Observable dashboard bundle under docs-site/dist/dashboard, and
publishes the combined site to Cloudflare Pages.
Promotion here is not a separate release ceremony. It is the movement from
validated artifacts on a main push into the public docs environment.
Sibling fos graph walk-through
Section titled “Sibling fos graph walk-through”The sibling fos workflow shape uses the same anatomy but different
environments because it ships an application.
In that shape, promotion means moving from pull-request CI proof to Cloudflare Pages staging, then from an approved release request to production. Promotion path: Cloudflare Pages staging, then production.
| Trigger | Gate | Artifact | Environment | Feedback |
|---|---|---|---|---|
Pull request to main in pr.yml | Build, Svelte typecheck, unit tests, and advisory high-severity audit | App build evidence inside CI | None | Pull request status checks |
Push to main in staging.yml | App build and branch/path conditions | SvelteKit build output deployed by Wrangler | Cloudflare Pages staging | /login reachability and private workspace API auth failure check |
v* tag or manual dispatch in release.yml | App build plus GitHub production environment approval | SvelteKit build output promoted to production | Cloudflare Pages production | Better Auth provider boundary and private API authorization smoke checks |
This is why pr.yml is CI, while staging.yml and release.yml are CD.
pr.yml proves the app. staging.yml and release.yml place a built app into
named environments and then check runtime behavior.
Production promotion has an explicit gate. A tag or manual dispatch can request
release, but the GitHub production environment approval decides whether the
deployment may proceed.
Secrets and environments
Section titled “Secrets and environments”Secrets sit at the moment a trusted graph needs external authority. The repo can build a docs bundle without Cloudflare credentials. It cannot publish that bundle to Cloudflare Pages without credentials supplied at runtime.
That split is the platform contract:
- source code and workflow logic are reviewable in the repo;
- credentials live in GitHub Actions secrets or environment configuration;
- deployment jobs receive the credentials only when their gates allow them to run;
- smoke checks report whether the target environment accepted the change.
For fos-workbench, the visible example is deploy-docs: Cloudflare account
and API token secrets are used only in the publishing job. For sibling fos,
the staging and production deploy jobs use the same idea around separate
Cloudflare Pages environments, with production also protected by the GitHub
production environment gate.
Failure modes to name while reading
Section titled “Failure modes to name while reading”Every pipeline graph should make failure modes visible. In these repos, common failure modes are:
- trigger mismatch: a workflow does not run because the branch, tag, or path filter does not match;
- validation failure: typecheck, unit tests, Evalite, dbt, pytest, Dagster, or streaming e2e fails;
- drift failure: generated OpenAPI or docs reference output differs from what is checked in;
- artifact failure: a downstream job cannot download the references, snapshot, warehouse, or build output it expects;
- secret failure: Cloudflare credentials or environment variables are missing or invalid;
- environment gate failure: production approval is not granted;
- smoke-check failure: deployment completed, but runtime behavior does not match the expected public routes or auth boundaries.
The important delivery-platform habit is to ask which category failed. “The pipeline is red” is too vague. “The validation gate failed before artifact promotion” or “the deploy succeeded but the smoke check failed” tells the operator where to look.
Checkpoint questions
Section titled “Checkpoint questions”-
A pull request changes only tutorial prose in
fos-workbench. Which part of the graph gives feedback, and why is there no environment?Expected reasoning: the pull request trigger starts CI. The
editorandservingvalidation gates report status checks.deploy-docsdoes not run because publishing is limited to pushes tomain, so no environment is changed. -
servingpasses tests and uploads the serving DuckDB warehouse, buteditorfails the generated OpenAPI drift check. Shoulddeploy-docspublish?Expected reasoning: no.
deploy-docsneeds botheditorandserving, so one failed validation gate blocks artifact promotion to Cloudflare Pages. -
Why is the sibling
fosstaging workflow CD even if it also builds and tests the app?Expected reasoning: a workflow can contain validation steps and still be CD when its purpose is to place a built artifact into an environment. The staging workflow deploys to Cloudflare Pages staging and then smoke-checks runtime behavior.
-
A production release tag is pushed in sibling
fos, but the GitHubproductionenvironment is not approved. What kind of failure is that?Expected reasoning: it is an environment gate failure, not a build failure. The release request exists, but promotion to production is intentionally blocked until approval is granted.
-
A deploy step finishes, but the public auth smoke check says a private API returns data without authorization. Did CI or CD fail?
Expected reasoning: CD failed after deployment. The build artifact was promoted, but runtime feedback proved the environment is unsafe.