Reference configs

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

.alint.yml
- 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.yaml 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.yaml, a pyproject.toml, README, LICENSE and NOTICE, with src/, tests/ and docs/ next to them. The config checks that layout and each provider.yaml without importing anything. Airflow's regex hooks become content rules, its import checks import_gate rules and its sync hooks cross_file rules; Ruff, codespell and the other linters run from command rules. It extends seven bundled rulesets, from oss-baseline to apache/governance.

.alint.yml
- id: airflow-shared-no-airflow-imports
  kind: import_gate
  paths: "shared/**/src/**/*.py"
  language: python
  forbid: '^airflow($|\.)'
  allow:
    - "shared/**/tests/**"
  level: error

The shared/ distributions are standalone libraries, so their source may not import airflow. Three more import_gate rules carry Airflow's hooks on where BaseOperator and the session helpers are imported from.

.alint.yml
- 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: error

One of the hooks that keep two files in step: the version in airflow-core/pyproject.toml must match __version__ 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 ProvidersManager loads.