mirror of
https://github.com/apache/airflow.git
synced 2026-11-04 07:12:48 +00:00
* Isolate mypy prek hooks, cover all non-provider dirs, and clean up type errors Each non-provider mypy prek hook now builds and caches its own virtualenv at .build/mypy-venvs/<hook>/ and its own mypy cache at .build/mypy-caches/<hook>/. UV_PROJECT_ENVIRONMENT redirects uv away from the project's .venv, so running the hook never mutates a contributor's regular development environment while still matching CI's frozen dependency set. Mypy runs with --follow-imports=silent so each hook only reports errors for files it owns; transitive code is covered by its own hook and different venvs no longer produce divergent results on shared code. Adds mypy hooks for the non-provider directories that were previously uncovered: airflow-ctl-tests, helm-tests, airflow-e2e-tests, task-sdk-integration-tests, docker-tests, kubernetes-tests, and shared. The mypy-shared hook iterates every shared/<dist> workspace distribution and builds a separate venv + cache per distribution so each shared library is type-checked against its own dependency set. breeze down --cleanup-mypy-cache additionally removes .build/mypy-venvs/ and .build/mypy-caches/ so all per-hook state is wiped alongside the existing .mypy_cache and mypy-cache-volume. Also fixes pre-existing type errors surfaced by the newly added and cleaned-up checks: platform-specific ignores for Linux-only os.posix_fadvise in the shared logging helper, narrower types and type: ignore where appropriate in shared configuration/observability/ timezones/secrets_backend/secrets_masker, Liskov override markers on the AirflowConfigParser subclass methods, and small correctness fixes in dev/breeze and the docker-tests / kubernetes-tests helpers so the full non-provider mypy suite runs clean on macOS and in CI. * Move mypy prek hooks to their respective distribution configs The new mypy hooks for airflow-ctl-tests, helm-tests, airflow-e2e-tests, task-sdk-integration-tests, docker-tests, and kubernetes-tests now live in each distribution's own .pre-commit-config.yaml, matching the pattern already used by airflow-core, task-sdk, and airflow-ctl. New .pre-commit- config.yaml files are added to distributions that didn't have one. prek auto-discovers nested configs, so the hooks remain part of the default check set. mypy-dev (covers dev + scripts), mypy-devel-common, and mypy-shared stay at the repo root: dev/scripts/devel-common don't have their own configs, and mypy-shared iterates every shared/<dist> distribution so has no single home. * Split mypy-dev and mypy-scripts, each with its own pyproject.toml config Previously the mypy-dev prek hook ran mypy against dev/ and scripts/ in a single invocation under the dev project's virtualenv. The two now get independent hooks — mypy-dev in dev/.pre-commit-config.yaml and mypy-scripts in scripts/.pre-commit-config.yaml — so each can evolve its own dependency set and check its own folder. Copy the full [tool.mypy] section from the root pyproject.toml into both dev/pyproject.toml and scripts/pyproject.toml so each sub-project owns its mypy configuration. Paths inside mypy_path are rewritten from $MYPY_CONFIG_FILE_DIR/ to $MYPY_CONFIG_FILE_DIR/../ so they still resolve to the repo-root siblings from the sub-project location. The decorator/ outputs plugins are scoped to dev only (scripts does not author DAG code). mypy_local_folder.py now passes --config-file <project>/pyproject.toml when the folder maps to one of these sub-project configs, so mypy uses the sub-project's configuration rather than the root one. * Teach selective-checks about the new non-provider mypy hooks Add FileGroupForCi entries and regex patterns for helm-tests, airflow-e2e-tests, docker-tests, kubernetes-tests, scripts, and shared Python files, then wire them into skip_prek_hooks so the corresponding mypy-* prek hook is only kept when its folder changed: - mypy-scripts (split off from the old combined mypy-dev) - mypy-airflow-ctl-tests, mypy-helm-tests, mypy-airflow-e2e-tests, mypy-task-sdk-integration-tests, mypy-docker-tests, mypy-kubernetes-tests - mypy-shared Update test_selective_checks.py skip-list constants and per-case inline skip lists to include the new hooks. Targeted test cases for files under the new-hook directories override skip-prek-hooks to leave the matching hook out of the skip set, confirming it will run when its folder changes. * Trim dev/scripts pyproject mypy_path to just relevant distributions Drop the 200+ provider path entries that were blindly copied from the root pyproject.toml. dev and scripts only import from other non-provider workspace members, so listing every provider src/tests directory under mypy_path just adds noise. The remaining non-provider entries cover everything dev or scripts plausibly import from. * Install mypy into per-hook venvs from uv.lock via a `mypy` dep group Each non-provider distribution with a mypy prek hook now declares a `mypy` dependency group in its pyproject.toml resolving to `apache-airflow-devel-common[mypy]`. mypy_local_folder.py syncs each dedicated virtualenv with `uv sync --frozen --project <X> --group mypy` and runs mypy with `uv run --frozen --project <X> --group mypy` — so mypy and its type stubs come from the workspace uv.lock, not from an ephemeral `--with` overlay whose resolution is independent of the main lockfile. uv.lock is refreshed to include the new group. Covers airflow-core, task-sdk, airflow-ctl, devel-common, dev, scripts, airflow-ctl-tests, helm-tests, airflow-e2e-tests, task-sdk-integration- tests, docker-tests, kubernetes-tests, and every shared/<dist> workspace member. * Drop mypy_path from dev/scripts pyprojects — venv site-packages is enough After the switch to installing mypy (and every transitive workspace dependency) directly into each hook's virtualenv via the `mypy` dep group, workspace packages like airflow, airflow.sdk, airflowctl, airflow_breeze, tests_common are all available via the venv's site-packages. mypy resolves them without needing mypy_path entries, so drop the copied list and leave a short comment explaining why. * Split mypy-shared into per-distribution hooks and enforce the pattern Each shared/<dist> workspace member now owns a mypy-shared-<dist> prek hook backed by its own shared/<dist>/.pre-commit-config.yaml. The single mypy-shared iterator is gone — mypy_local_folder.py accepts shared/<dist> as a first-class folder and the per-hook virtualenv now lives at .build/mypy-venvs/shared-<dist>/ (slash in the folder name is replaced with a dash in the venv/cache path). Adds a new check-shared-mypy-hooks prek hook that fails when a shared/<dist> workspace member is missing its dedicated .pre-commit- config.yaml, printing the exact YAML to add. Selective-checks emits one skip entry per dist, enumerated from shared/ at run time. Contributing docs cover the two-step process for adding a new shared library. * Pin minimum_prek_version to 0.3.4 consistently across all configs All .pre-commit-config.yaml files now require prek >= 0.3.4 (the version already declared by the root config). Previously the nested configs pinned a mix of 0.2.0, 0.3.2, and 0.3.4, so a contributor could pass the root's version check and still trip on stale subproject pins as they moved between directories. * Refresh uv.lock after rebase to reflect the `mypy` dep groups The rebase onto main resolved the uv.lock conflict by taking main's version, so `uv sync --group mypy` would fail against uv.lock until the groups added to the per-distribution pyprojects were re-resolved. Regenerates the lockfile to include them. * Add explicit selective-checks test for per-shared-dist mypy hook skipping Verifies that when a file under shared/logging/ changes, only mypy-shared-logging is kept among the thirteen mypy-shared-* hooks; all other shared distributions' hooks land in the skip list. Pins the contract that the runtime enumeration over shared/*/pyproject.toml works as intended. * Refresh mypy docs to match the per-hook venv + --group mypy workflow Fills in the docs that still referenced the pre-split workflow: - AGENTS.md: mentions `mypy-shared-<dist>` per shared workspace member and the `uv sync --group mypy` install path for mypy itself. - scripts/ci/prek/AGENTS.md: clarifies that non-provider mypy hooks run locally through mypy_local_folder.py (Breeze image only needed for the providers hook). - dev/breeze/doc/03_developer_tasks.rst: renames stale `mypy-airflow` to `mypy-airflow-core`, and expands the cache note to cover the per-hook virtualenvs and caches under .build/. - dev/breeze/doc/ci/04_selective_checks.md: expands the file-group and skip-reason lists so every new mypy hook (scripts, task-sdk, airflow-ctl, the six test-dir hooks, and mypy-shared-<dist> enumerated at runtime) is documented. * Rename mypy_local_folder.py to run_mypy_full_dist_local_venv_or_breeze_in_ci.py Updates every .pre-commit-config.yaml entry and prose references so they point at the new script name. Two shared configs use YAML folded-scalar entries to stay under the 110-char yamllint limit; updates the validation script's expected template to match.
343 lines
13 KiB
ReStructuredText
343 lines
13 KiB
ReStructuredText
.. Licensed to the Apache Software Foundation (ASF) under one
|
|
or more contributor license agreements. See the NOTICE file
|
|
distributed with this work for additional information
|
|
regarding copyright ownership. The ASF licenses this file
|
|
to you under the Apache License, Version 2.0 (the
|
|
"License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
|
|
.. http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
.. Unless required by applicable law or agreed to in writing,
|
|
software distributed under the License is distributed on an
|
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
KIND, either express or implied. See the License for the
|
|
specific language governing permissions and limitations
|
|
under the License.
|
|
|
|
Static code checks
|
|
==================
|
|
|
|
The static code checks in Airflow are used to verify that the code meets certain quality standards.
|
|
All the static code checks can be run through prek hooks.
|
|
|
|
The prek hooks perform all the necessary installation when you run them
|
|
for the first time.
|
|
|
|
You can also run the checks via `Breeze <../dev/breeze/doc/README.rst>`_ environment.
|
|
|
|
.. contents:: Table of Contents
|
|
:depth: 2
|
|
:local:
|
|
|
|
Prek hooks
|
|
----------
|
|
|
|
Prek hooks help speed up your local development cycle and place less burden on the CI infrastructure.
|
|
Consider installing the prek hooks as a necessary prerequisite.
|
|
|
|
The hooks by default only check the files you are currently working on (and are staged) which makes the
|
|
checks rather fast. Yet, these checks use exactly the same environment as the CI tests
|
|
use. So, you can be sure your modifications will also work for CI if they pass
|
|
prek hooks.
|
|
|
|
We have integrated the `prek <https://github.com/j178/prek>`__ framework
|
|
in our development workflow. It can be installed in various ways and does not even need ``pip`` or
|
|
|
|
``python`` to be installed. It is a drop-in replacement for the legacy ``pre-commit`` tool, but it is
|
|
much faster and more feature-rich. It is written in Rust and it is designed to install environments in parallel,
|
|
so it is much faster than the ``pre-commit`` tool.
|
|
|
|
Installing prek hooks
|
|
---------------------
|
|
|
|
It is the best to use prek hooks when you have your local virtualenv for
|
|
Airflow activated since then prek hooks and other dependencies are
|
|
automatically installed. You can also install the prek hooks manually using ``uv`` or ``pipx``.
|
|
|
|
.. code-block:: bash
|
|
|
|
uv tool install prek
|
|
|
|
.. code-block:: bash
|
|
|
|
pipx install prek
|
|
|
|
Since we have a lot of hooks and sometimes you want to run them individually, it's advised to install
|
|
auto-completion for the ``prek`` command. You can do it by adding the following line to your
|
|
``.bashrc`` or ``.zshrc`` file:
|
|
|
|
For bash:
|
|
|
|
.. code-block:: bash
|
|
|
|
eval "$(COMPLETE=bash prek)" # for bash
|
|
|
|
For zsh:
|
|
|
|
.. code-block:: zsh
|
|
|
|
eval "$(COMPLETE=zsh prek)"
|
|
|
|
Similarly for other shells like fish, powershell, etc.
|
|
|
|
After installation, prek hooks are run automatically when you commit the code and they will
|
|
only run on the files that you change during your commit, so they are usually pretty fast and do
|
|
not slow down your iteration speed on your changes. There are also ways to disable the prek hooks
|
|
temporarily when you commit your code with ``--no-verify`` switch or skip certain checks that you find
|
|
to much disturbing your local workflow. See `Using prek <#using-prek>`_
|
|
|
|
The ``prek`` hooks use several external linters that need to be installed before prek is run.
|
|
Each of the checks installs its own environment, so you do not need to install those, but there are some
|
|
checks that require locally installed binaries. On Linux, you typically install
|
|
them with ``sudo apt install``, on macOS - with ``brew install``.
|
|
|
|
The current list of prerequisites is limited to ``xmllint`` and ``golang`` if you want to modify
|
|
the Golang code.:
|
|
|
|
- on Linux, install via ``sudo apt install libxml2-utils golang``
|
|
- on macOS, install via ``brew install libxml2 golang``
|
|
|
|
Some prek hooks also require the Docker Engine to be configured as the static
|
|
checks are executed in the Docker environment. You should build the images
|
|
locally before installing prek checks as described in `Breeze docs <../dev/breeze/doc/README.rst>`__.
|
|
|
|
Sometimes your image is outdated and needs to be rebuilt because some dependencies have been changed.
|
|
In such cases, the Docker-based prek will inform you that you should rebuild the image.
|
|
|
|
Enabling prek hooks
|
|
-------------------
|
|
|
|
To turn on prek checks for ``commit`` operations in git, enter:
|
|
|
|
.. code-block:: bash
|
|
|
|
prek install
|
|
|
|
To install the checks also for ``pre-push`` operations, enter:
|
|
|
|
.. code-block:: bash
|
|
|
|
prek install --hook-type pre-push
|
|
|
|
For details on advanced usage of the install method, use:
|
|
|
|
.. code-block:: bash
|
|
|
|
prek install --help
|
|
|
|
.. note::
|
|
|
|
The ``prek`` tool is a drop-in replacement for the legacy ``pre-commit`` tool - much faster and more
|
|
feature-rich, If you have already installed ``pre-commit`` to handle your hooks, you can run
|
|
``prek install -f`` to replace the existing ``pre-commit`` hooks with the ``prek`` hooks.
|
|
|
|
Available prek hooks
|
|
--------------------
|
|
|
|
You can see the list of available hooks by running:
|
|
|
|
.. code-block:: bash
|
|
|
|
prek list
|
|
|
|
You can also see more details about the hooks by running:
|
|
|
|
.. code-block:: bash
|
|
|
|
prek list --verbose
|
|
|
|
And if you want to see the details of a particular hook, you can run:
|
|
|
|
.. code-block:: bash
|
|
|
|
prek list --verbose <hook-id>
|
|
|
|
When you install auto-completion, you can also use the tab-completion to see the available hooks.
|
|
|
|
Using prek
|
|
----------
|
|
|
|
After installation, prek hooks are run automatically when you commit the
|
|
code or push it to the repository (depending on stages configured for the hooks). Some of the
|
|
hooks are configured to run on "manual" stage only and are not run automatically.
|
|
|
|
By default when you run ``prek``, the ``pre-commit`` stage hooks are run.
|
|
|
|
But you can run prek hooks manually as needed.
|
|
|
|
- Run all checks on your staged files by using:
|
|
|
|
.. code-block:: bash
|
|
|
|
prek
|
|
|
|
- Run only mypy check on your staged airflow and dev files by specifying the
|
|
``mypy-airflow-core`` and ``mypy-dev`` prek hooks (more hooks can be specified).
|
|
For non-provider projects, mypy runs locally via ``uv`` (no breeze image needed):
|
|
|
|
.. code-block:: bash
|
|
|
|
prek mypy-airflow-core mypy-dev
|
|
|
|
- Run only mypy airflow checks on all "airflow-core" files by using:
|
|
|
|
.. code-block:: bash
|
|
|
|
prek mypy-airflow-core --all-files
|
|
|
|
- Run all pre-commit stage hooks on all files by using:
|
|
|
|
.. code-block:: bash
|
|
|
|
prek --all-files
|
|
|
|
- Run all pre-commit stage hooks only on files modified in the last locally available
|
|
commit in your checked out branch:
|
|
|
|
.. code-block:: bash
|
|
|
|
prek --last-commit
|
|
|
|
- Run all pre-commit stage hooks only on files modified in your last branch that is targeted
|
|
to be merged into the main branch:
|
|
|
|
.. code-block:: bash
|
|
|
|
prek --from-ref main
|
|
|
|
- Show files modified automatically by prek when prek automatically fixes errors (after running all
|
|
``pre-commit`` stage hooks on locally modified files):
|
|
|
|
.. code-block:: bash
|
|
|
|
prek --show-diff-on-failure
|
|
|
|
- Skip one or more of the checks by specifying a comma-separated list of
|
|
checks to skip in the SKIP variable:
|
|
|
|
.. code-block:: bash
|
|
|
|
SKIP=ruff,rst-backticks prek --all-files
|
|
|
|
|
|
You can always skip running the tests by providing ``--no-verify`` flag to the
|
|
``git commit`` command.
|
|
|
|
To check other usage types of the pre-commit framework, see `Pre-commit website <https://pre-commit.com/>`__.
|
|
|
|
Disabling particular checks
|
|
---------------------------
|
|
|
|
In case you have a problem with running particular ``prek`` check you can still continue using the
|
|
benefits of having ``prek`` installed, with some of the checks disabled. In order to disable
|
|
checks you might need to set ``SKIP`` environment variable to coma-separated list of checks to skip. For example,
|
|
when you want to skip some checks (ruff/mypy for example), you should be able to do it by setting
|
|
``export SKIP=ruff,mypy-airflow-core,``. You can also add this to your ``.bashrc`` or ``.zshrc`` if you
|
|
do not want to set it manually every time you enter the terminal.
|
|
|
|
In case you do not have breeze image configured locally, you can also disable all checks that require breeze
|
|
the image by setting ``SKIP_BREEZE_PREK_HOOKS`` to "true". This will mark the tests as "green" automatically
|
|
when run locally (note that those checks will anyway run in CI).
|
|
|
|
Disabling goproxy for firewall issues
|
|
-------------------------------------
|
|
|
|
Sometimes your environment might not allow to connect to the ``goproxy`` server, which is used to
|
|
proxy/cache Go modules. When your firewall blocks go proxy it usually ends with message similar to:
|
|
|
|
.. code-block:: text
|
|
|
|
lookup proxy.golang.org: i/o timeout
|
|
|
|
In such case, you can disable the ``goproxy`` by setting the
|
|
``GOPROXY`` environment variable to "direct". You can do it by running:
|
|
|
|
.. code-block:: bash
|
|
|
|
export GOPROXY=direct
|
|
|
|
Alternatively if your company has its own Go proxy, you can set the ``GOPROXY`` to
|
|
your company Go proxy URL. For example:
|
|
|
|
.. code-block:: bash
|
|
|
|
export GOPROXY=https://mycompanygoproxy.com
|
|
|
|
See `Go Proxy lesson <https://www.practical-go-lessons.com/chap-18-go-module-proxies#configuration-of-the-go-module-proxy>`__)
|
|
for more details on how to configure Go proxy - including setting multiple proxies.
|
|
|
|
You can add the variable to your ``.bashrc`` or ``.zshrc`` if you do not want to set it manually every time you
|
|
enter the terminal.
|
|
|
|
Manual prek hooks
|
|
-----------------
|
|
|
|
Most of the checks we run are configured to run automatically when you commit the code or push PR. However,
|
|
there are some checks that are not run automatically and you need to run them manually. You can run
|
|
them manually by running ``prek --stage manual <hook-id>``.
|
|
|
|
Mypy checks
|
|
-----------
|
|
|
|
When we run mypy checks locally, the ``mypy-*`` checks run depending on the files you are changing:
|
|
``mypy-airflow-core``, ``mypy-dev``, ``mypy-providers``, ``mypy-scripts``, ``mypy-task-sdk``,
|
|
``mypy-airflow-ctl``, ``mypy-devel-common``, ``mypy-airflow-ctl-tests``, ``mypy-helm-tests``,
|
|
``mypy-airflow-e2e-tests``, ``mypy-task-sdk-integration-tests``, ``mypy-docker-tests``,
|
|
``mypy-kubernetes-tests``, and one ``mypy-shared-<dist>`` hook per ``shared/<dist>`` workspace
|
|
distribution (e.g. ``mypy-shared-configuration``, ``mypy-shared-logging``).
|
|
|
|
For **non-provider projects**, mypy runs locally using ``uv`` — no breeze CI image is needed. These
|
|
checks run as regular prek hooks in the ``pre-commit`` stage, checking whole directories at once. This
|
|
means they run both as part of local commits and as part of regular static checks in CI (not as
|
|
separate mypy CI jobs).
|
|
|
|
Each non-provider ``mypy-*`` hook uses a **dedicated virtualenv and mypy cache** under ``.build/`` so
|
|
running mypy never mutates your regular project ``.venv`` and each hook keeps a stable, CI-aligned
|
|
dependency set:
|
|
|
|
- virtualenvs: ``.build/mypy-venvs/<hook-name>/``
|
|
- mypy caches: ``.build/mypy-caches/<hook-name>/``
|
|
|
|
Adding a new shared library
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Every ``shared/<dist>`` workspace member has its own ``mypy-shared-<dist>`` prek hook so it is
|
|
type-checked in isolation against its own dependency set. When you add a new shared library under
|
|
``shared/<new-dist>/``, you also need to:
|
|
|
|
1. Add a ``[dependency-groups]`` section with ``mypy = ["apache-airflow-devel-common[mypy]"]`` in
|
|
``shared/<new-dist>/pyproject.toml`` (so ``uv sync --group mypy`` installs mypy into the hook's
|
|
dedicated virtualenv).
|
|
2. Create ``shared/<new-dist>/.pre-commit-config.yaml`` with a ``mypy-shared-<new-dist>`` hook
|
|
entry that calls ``../../scripts/ci/prek/run_mypy_full_dist_local_venv_or_breeze_in_ci.py shared/<new-dist>``.
|
|
|
|
The ``check-shared-mypy-hooks`` prek hook enforces step 2 — it fails and prints the exact config
|
|
contents to add when any ``shared/<dist>`` is missing its dedicated mypy hook.
|
|
|
|
To run the prek hook for a specific project (example for ``airflow-core`` files):
|
|
|
|
.. code-block:: bash
|
|
|
|
prek mypy-airflow-core --all-files
|
|
|
|
To show unused mypy ignores, run:
|
|
|
|
.. code-block:: bash
|
|
|
|
export SHOW_UNUSED_MYPY_WARNINGS=true
|
|
prek mypy-airflow-core --all-files
|
|
|
|
For **providers**, mypy still runs via breeze (``breeze run mypy``) as a separate CI job and requires
|
|
``breeze ci-image build --python 3.10`` to be built locally. Providers use a separate docker-volume
|
|
(called ``mypy-cache-volume``) that keeps the cache of last MyPy execution.
|
|
|
|
To clear all mypy caches (the Docker volume used by providers, any legacy repo-root ``.mypy_cache``,
|
|
and the per-hook venvs + caches under ``.build/mypy-venvs/`` and ``.build/mypy-caches/``), run
|
|
``breeze down --cleanup-mypy-cache``.
|
|
|
|
-----------
|
|
|
|
Once your code passes all the static code checks, you should take a look at `Testing documentation <09_testing.rst>`__
|
|
to learn about various ways to test the code.
|