Lesson 10 · This repo's Airflow
rule.yaml & dynamic DAG generation
Why you won't find a hand-written DAG in this repo.
Your win: explain how DAGs here are generated from a YAML
registry, and why that beats hand-writing one .py per DAG.
DAGs are generated, not authored
Most tutorials show you a hand-written my_dag.py. This repo doesn't work
that way. It has a single registry, a generator, and Jinja2 templates that produce
the DAG files:
A rules.yaml entry declares: name, which
template, the schedule, tags,
failed_mentions, its jobs, and per-env deployments.
generate.py iterates dags → deployments → tenants → jobs and renders each into
a concrete DAG + manifest.1
rules.yaml) + shared
templates means: less duplication, and every DAG automatically gets the same
alerting, access-control, retries, and structure. Change the template once, all DAGs
update.
failed_mentions
is missing (generate.py:162) and if a SparkApplication name
exceeds 40 chars. And CI runs a drift check
(.github/scripts/ensure-make-generate-dags.bash): it
regenerates and fails if the committed DAGs differ — so what's deployed always matches
rules.yaml + templates. There's a second generator,
generate_lmsdwh.py, for the warehouse DAGs.
make generate-dags (with AIRFLOW_ENV) writes the files; CI
rsyncs them to a GCS bucket that Airflow mounts read-only via gcsfuse
(DAGS_FOLDER=/opt/airflow/dags_gcs). gitSync is off. You edit
rules.yaml/templates, not the generated .py.
The airflow-job skill + Astronomer on dynamic DAGs
The repo's own how-to for adding a job, and the general pattern of generating DAGs from config.
→ .claude/skills/airflow-job/SKILL.md · repo-airflow-map.md
→ astronomer.io/docs/learn — Dynamically generating DAGs
Check yourself (from memory)
Q1. In this repo, DAGs are…
rules.yaml + generate.py +
Jinja2 templates produce the .py files. You edit the config, not the output.
Q2. The main benefit of generating DAGs is…
Q3. The CI "lint" for DAGs checks that…
rules.yaml + templates.
airflow/dags/rules.yaml lists each DAG (name, template, schedule, jobs,
failed_mentions, deployments); generate.py + Jinja2 templates render the DAG
.py files + per-job K8s manifests into airflow/dags/{org}/{env}/.
Why: many tenants × envs × jobs → one config source beats hundreds of near-identical files
and guarantees consistent alerting/access-control. The generator validates
(failed_mentions required; SparkApp name ≤40), and a CI drift check
(ensure-make-generate-dags.bash) fails if regenerating changes the tree.
make generate-dags → GCS → gcsfuse.rules.yaml entry through to its generated
.py, field by field? Ask me.
1. Repo: repo-airflow-map.md; .claude/skills/airflow-job/SKILL.md.