apache/airflow: the provider layout, checked without imports
Airflow checks its 101 provider packages with a 1,085-line script that runs in its breeze container and imports every one. Four rules check the same layout by reading the tree, and on the run every provider passed. This config was written to test alint; Airflow doesn't use alint.
What alint found
- id: airflow-provider-required-meta-files
kind: for_each_file
select: "providers/**/provider.yaml"
require:
- { kind: file_exists, paths: "{dir}/pyproject.toml" }
- { kind: file_exists, paths: "{dir}/README.rst" }
- { kind: file_exists, paths: "{dir}/LICENSE" }
- { kind: file_exists, paths: "{dir}/NOTICE" }
- { kind: dir_exists, paths: "{dir}/src" }
- { kind: dir_exists, paths: "{dir}/tests" }
- { kind: dir_exists, paths: "{dir}/docs" }
level: error✓ passed airflow-provider-required-meta-files
All 101 provider packages have the full layout, and three more rules found every provider naming its package and state correctly.
The bundled GitHub Actions rules (alint 0.16.1, on the workflows as of 7 May 2026) flag 5 workflows that grant contents: write at the workflow level, for backports, milestone tags and upgrade checks.
The config
Every provider package has the same layout: a provider, a pyproject, README, LICENSE and NOTICE, with src/, tests/ and docs/ next to them. The config checks that layout and each provider without importing anything. Airflow's regex hooks become content rules, its import checks import_ rules and its sync hooks cross_ rules; Ruff, codespell and the other linters run from command rules. It extends seven bundled rulesets, from oss-baseline to apache/.
- id: airflow-shared-no-airflow-imports
kind: import_gate
paths: "shared/**/src/**/*.py"
language: python
forbid: '^airflow($|\.)'
allow:
- "shared/**/tests/**"
level: errorThe shared/ distributions are standalone libraries, so their source may not import airflow. Three more import_ rules carry Airflow's hooks on where BaseOperator and the session helpers are imported from.
- id: airflow-version-consistency
kind: cross_file_value_equals
source:
file: airflow-core/pyproject.toml
extract: { toml: "$.project.version" }
targets:
- file: airflow-core/src/airflow/__init__.py
extract: { regex: '__version__\s*=\s*"([^"]+)"' }
normalize: trim
allow_missing_target: true
level: errorOne of the hooks that keep two files in step: the version in airflow-core/ must match _ in the package.
Speed
In the May run, on the full Airflow checkout, the rules that don't call other tools took 227 ms. By the inventory's estimate the provider script takes 30 to 60 seconds from cold; it wasn't timed, since it needs the breeze setup.
What stays outside alint
What needs Airflow's own code stays with Airflow:
- the 22 hooks that regenerate or update files, and 25 checks specific to Airflow's domain;
- the 9 hooks that walk the Python syntax tree, such as the check on operators' templated fields;
- the rest of the provider script: importing each provider and checking what
ProvidersManagerloads.