feat(data): working end-to-end data pipeline starter - #1
Conversation
Composed starter rather than one template per tool: Week 15 is synthesis, so a team ships one pipeline, not five parallel scaffolds. Runs on clone with no credentials: the default source is the Arbeitnow job board (no API key) and docker compose brings up a local Postgres. Verified end to end against a throwaway database: 175 rows ingested, dbt build green with PASS=18 ERROR=0, and a second pipeline run leaves the row count at 175 because writes are upserts. The mart plus its .yml is the contract with backend/, so the backend can write endpoints before the pipeline is finished and the single frontend trainee is not blocked. docs/mart_contract.md covers how the two pairs agree and change it. Bicep, Databricks, and Streamlit ship under optional/ because Week 15 marks them as optional extensions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The repository shipped a Node-only .gitignore, so a trainee running the data or backend starter would commit .venv, __pycache__, dbt target/, and compiled classes on day one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two defects found by actually running what the README told trainees to run. docker run --env-file .env failed: POSTGRES_HOST=localhost points at the container itself, not the host, so the pipeline could never reach the compose database. Added a pipeline service that joins the compose network and overrides the host, so 'docker compose run --rm pipeline' works. The DAG parsed but raised three deprecation warnings against the pinned Astro runtime 3.3, which is Airflow 3. Switched to airflow.sdk and the standard provider BashOperator. It now parses clean with both tasks present. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Verification update: the two unverified items are now done, and both had defectsDocker was unavailable when this PR was opened. Re-ran both checks and each one found a real bug, now fixed in b2ab975. 1.
|
Running 'astro dev start' exposed three defects that DAG parsing alone could not: the folder was not an Astro project at all, the runtime's ONBUILD step requires a packages.txt that did not exist, and the DAG imported src and read include/dbt, neither of which was reachable from the containers. - .astro/config.yaml added, and un-ignored at the repo root. Without it astro refuses to run, so ignoring it made the folder permanently broken. - packages.txt added. The Astro runtime ONBUILD-copies it and the build fails with 'packages.txt: not found' when it is missing. - docker-compose.override.yml bind-mounts ../src and ../dbt into the Airflow containers and sets PYTHONPATH, so there is one copy of each rather than a duplicate that drifts. - requirements.txt now carries the pipeline's own dependencies, which the ingest task needs to import src.pipeline. Verified with the stack running: the DAG is listed with no import errors, src.pipeline imports inside the scheduler, include/dbt is mounted, and the dbt_build task command finishes PASS=18 ERROR=0 against a real database. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Booting Airflow against the data stack exposed a service name collision. The Astro stack ships a service called postgres, so a DAG configured with POSTGRES_HOST=postgres resolved to Airflow's metadata database rather than the project's, and failed with 'database finalproject does not exist'. - Renamed the data service from postgres to db, and said why in a comment so nobody renames it back. - Gave the data stack a fixed network name, finalproject, so the Airflow stack can attach to it by name instead of going through host.docker.internal, which breaks on any machine already using port 5432. - Added airflow/.env.example. The folder previously shipped no environment file at all, so the DAG had nothing to read. Verified with a real run: DagRun state=success, both tasks. The ingest task fetched 175 live records and dbt built the mart from them, PASS=18 ERROR=0. fct_postings.ingested_at falls inside the run window, which is what proves the data came from that run rather than an earlier one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Everything is now tested, including the ingest task. One more real defect found.My earlier note said containers had no outbound DNS. That was wrong: the Docker daemon had only just started when I tested it. Retested and DNS resolves fine, so I ran the whole thing properly. The defect: the DAG was talking to Airflow's own metadata databaseWith the containers wired onto the data stack's network, the DAG failed: The Astro stack ships its own service named Fixed in 4cb4d6a:
Full end-to-end runThe run window was 21:28:18 to 21:28:27, so Verification status: complete
Nothing on the data side is unverified now. 🤖 Generated with Claude Code |
Adapted from the pr-body-check.yml already running in data-assignment-week-9, 11, and 12, with the size gate added. The template alone is not enough. GitHub only auto-fills it in the web compose form and in 'gh pr create' with no --body, so a pull request opened through the REST API or with --body, which is the path most AI tools take, silently skips it. The check is the only thing that actually enforces it. Sections are the assignment-repo set with one addition: 'Contract impact'. It makes the author state whether a mart the backend reads has changed, before merging rather than after, which is what turns the CODEOWNERS rule into a conversation. The size gate exists because an unreviewable pull request is a size problem before it is an AI problem. Two thousand lines does not get reviewed, it gets approved. The limit is a number so that a bot raises it rather than one teammate having to tell another their work is too big to read. Generated files are excluded, and an 'Oversized: <reason>' line in the description overrides it so the exception stays visible. Both re-run on description edits, so recovery needs no new commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
What I built
A working end-to-end starter for the
data/folder: fetch from a source API, validate, store, shape with dbt, publish a mart the backend reads. Plus the PR template and checks this description is now demonstrating.Why this approach
One composed starter, not one template per tool. Week 15 is synthesis: a team ships one pipeline. Five parallel scaffolds (dbt, Airflow, Databricks, Docker, Python) would make the two data trainees spend day one picking tools instead of building, and would duplicate the ingestion, config, and secrets layer five times. Databricks, Bicep, and Streamlit sit under
optional/, matching howweek_15__2_project_requirements.mdalready marks them.It runs on clone with no credentials. The default source is the Arbeitnow job board, which needs no key, and
docker compose up -d dbgives a local Postgres. A team's first hour goes into their product rather than setup.The mart is the contract with
backend/.dbt/models/marts/_fct_postings.ymldocuments every published column, anddocs/mart_contract.mdsets out which changes are safe alone (add a column) and which need agreement (rename, remove, retype). That is what lets the backend write endpoints before the pipeline is finished.Contract impact
None. This is new scaffolding in an empty folder; no existing mart or endpoint changes.
How to run
Self-check
Verified end to end, not assumed:
docker builddbt buildfrom hostdbt buildinside Airflowastro dev startFour real defects were found by running it rather than reading it: the container could not reach Postgres via
localhost; the DAG used Airflow 2 imports against a pinned Airflow 3 runtime;data/airflow/was not a valid Astro project and my own.gitignoremade it permanently unfixable; and the DAG was silently connecting to Airflow's own metadata database, because Astro ships a service also calledpostgres. All fixed, details in the comments below.Oversized: initial scaffold for an empty folder, 32 files that only make sense together. Splitting it would produce several PRs that individually do nothing.
🤖 Generated with Claude Code