This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new f2a3e5feac7 Add --frozen to uv run commands in run_tests and document 
uv lock behavior (#64699)
f2a3e5feac7 is described below

commit f2a3e5feac7c4d5a34f3691daf766ae84b130486
Author: Jarek Potiuk <[email protected]>
AuthorDate: Sat Apr 4 15:35:52 2026 +0200

    Add --frozen to uv run commands in run_tests and document uv lock behavior 
(#64699)
    
    Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
---
 contributing-docs/07_local_virtualenv.rst          | 75 ++++++++++------------
 .../13_airflow_dependencies_and_extras.rst         | 11 +++-
 dev/breeze/src/airflow_breeze/utils/run_tests.py   | 13 +++-
 3 files changed, 54 insertions(+), 45 deletions(-)

diff --git a/contributing-docs/07_local_virtualenv.rst 
b/contributing-docs/07_local_virtualenv.rst
index e6a0409ac9d..b75db8d4dbd 100644
--- a/contributing-docs/07_local_virtualenv.rst
+++ b/contributing-docs/07_local_virtualenv.rst
@@ -267,57 +267,52 @@ for the provider is as simple as running:
 
     uv run pytest
 
-Installing "golden" version of dependencies
--------------------------------------------
+Locked versions of dependencies
+-------------------------------
 
-Whatever virtualenv solution you use, when you want to make sure you are using 
the same
-version of dependencies as in main, you can install recommended version of the 
dependencies by using pip:
-constraint-python<PYTHON_MAJOR_MINOR_VERSION>.txt files as ``constraint`` 
file. This might be useful
-to avoid "works-for-me" syndrome, where you use different version of 
dependencies than the ones
-that are used in main, CI tests and by other contributors.
+The ``uv.lock`` file is committed to the Airflow repository and is used by 
``uv sync`` to ensure
+consistent dependency versions across all developers. When you run ``uv 
sync``, it uses the lock file
+to install exact dependency versions, so you don't need to pass constraint 
files manually.
 
-There are different constraint files for different python versions. For 
example this command will install
-all basic devel requirements and requirements of google provider as last 
successfully tested for Python 3.10:
-
-.. code:: bash
-
-    uv pip install -e ".[devel,google]" \
-      --constraint 
"https://raw.githubusercontent.com/apache/airflow/constraints-main/constraints-source-providers-3.10.txt";
+The ``uv sync`` command prefers the locked versions of dependencies from 
``uv.lock``. It will only
+attempt to resolve new dependencies when ``pyproject.toml`` files change (e.g. 
when a new dependency
+is added or version bounds are modified). This means that day-to-day ``uv 
sync`` is fast and
+deterministic — it simply installs what the lock file specifies without 
re-resolving the dependency
+tree.
 
+If you want to make sure that ``uv sync`` does not update your lock file at 
all (for example in CI
+or when running tests), you can pass the ``--frozen`` flag:
 
-In the future we will utilise ``uv.lock`` to manage dependencies and 
constraints, but for the moment we do not
-commit ``uv.lock`` file to Airflow repository because we need to figure out 
automation of updating the ``uv.lock``
-very frequently (few times a day sometimes). With Airflow's 700+ dependencies 
it's all but guaranteed that we
-will have 3-4 changes a day and currently automated constraints generation 
mechanism in ``canary`` build keeps
-constraints updated, but for ASF policy reasons we cannot update ``uv.lock`` 
in the same way - but work is in
-progress to fix it.
-
-Make sure to use latest main for such installation, those constraints are 
"development constraints" and they
-are refreshed several times a day to make sure they are up to date with the 
latest changes in the main branch.
+.. code:: bash
 
-Note that this might not always work as expected, because the constraints are 
not always updated
-immediately after the dependencies are updated, sometimes there is a very 
recent change (few hours, rarely more
-than a day) which still runs in ``canary`` build and constraints will not be 
updated until the canary build
-succeeds. Usually what works in this case is running your install command 
without constraints.
+    uv sync --frozen
 
-You can upgrade just airflow, without paying attention to provider's 
dependencies by using
-the 'constraints-no-providers' constraint files. This allows you to keep 
installed provider dependencies
-and install to latest supported ones by pure Airflow core.
+This will fail if the lock file is out of date with respect to 
``pyproject.toml``, rather than
+silently updating it. This is useful when you want to guarantee fully 
reproducible environments.
 
-.. code:: bash
+Cooldown via ``exclude-newer``
+..............................
 
-    uv pip install -e ".[devel]" \
-      --constraint 
"https://raw.githubusercontent.com/apache/airflow/constraints-main/constraints-no-providers-3.10.txt";
+The ``[tool.uv]`` section in the top-level ``pyproject.toml`` sets 
``exclude-newer = "4 days"``.
+This acts as a cooldown period — when ``uv`` resolves new dependencies, it 
ignores package versions
+released in the last 4 days. This protects against broken or yanked releases 
that might otherwise
+immediately break the dependency resolution for all developers. When ``uv`` 
writes the lock file, it
+records the resolved ``exclude-newer`` timestamp so that subsequent ``uv 
sync`` calls use the same
+cutoff, ensuring consistency across machines.
 
-These are examples of the development options available with the local 
virtualenv in your IDE:
+Constraints generated from the lock file
+.........................................
 
-* local debugging;
-* Airflow source view;
-* auto-completion;
-* documentation support;
-* unit tests.
+Airflow also publishes traditional ``pip``-style constraint files (see
+`Airflow dependencies and extras <13_airflow_dependencies_and_extras.rst>`_ 
for details). When
+installing Airflow from sources, these constraint files are generated directly 
from ``uv.lock`` using
+``uv export --frozen``, which converts the lock file into a flat list of 
pinned versions suitable for
+``pip install --constraint``. This ensures that both the ``uv sync`` workflow 
and the ``pip`` constraint
+workflow install the same dependency versions.
 
-This document describes minimum requirements and instructions for using a 
standalone version of the local virtualenv.
+The lock file is updated regularly — whenever dependencies are changed via any 
``pyproject.toml`` and
+when ``breeze ci upgrade`` is run. Make sure to use the latest main branch to 
get the most
+up-to-date ``uv.lock``.
 
 Running Tests
 -------------
diff --git a/contributing-docs/13_airflow_dependencies_and_extras.rst 
b/contributing-docs/13_airflow_dependencies_and_extras.rst
index cd43e486ffb..7c59ce25d17 100644
--- a/contributing-docs/13_airflow_dependencies_and_extras.rst
+++ b/contributing-docs/13_airflow_dependencies_and_extras.rst
@@ -318,17 +318,17 @@ example ``pip install apache-airflow==1.10.2 
Werkzeug<1.0.0``)
 
 There are several sets of constraints we keep:
 
-* 'constraints' - these are constraints generated by matching the current 
Airflow version from sources
+* ``constraints`` - these are constraints generated by matching the current 
Airflow version from sources
    and providers that are installed from PyPI. Those are constraints used by 
the users who want to
    install Airflow with pip, they are named 
``constraints-<PYTHON_MAJOR_MINOR_VERSION>.txt``.
 
-* "constraints-source-providers" - these are constraints generated by using 
providers installed from
+* ``constraints-source-providers`` - these are constraints generated by using 
providers installed from
   current sources. While adding new providers their dependencies might change, 
so this set of providers
   is the current set of the constraints for Airflow and providers from the 
current main sources.
   Those providers are used by CI system to keep "stable" set of constraints. 
They are named
   ``constraints-source-providers-<PYTHON_MAJOR_MINOR_VERSION>.txt``
 
-* "constraints-no-providers" - these are constraints generated from only 
Apache Airflow, without any
+* ``constraints-no-providers`` - these are constraints generated from only 
Apache Airflow, without any
   providers. If you want to manage Airflow separately and then add providers 
individually, you can
   use them. Those constraints are named 
``constraints-no-providers-<PYTHON_MAJOR_MINOR_VERSION>.txt``.
 
@@ -375,6 +375,11 @@ using ``constraints-no-providers`` constraint files as 
well.
     --constraint 
"https://raw.githubusercontent.com/apache/airflow/constraints-main/constraints-no-providers-3.10.txt";
 
 
+These constraint files are generated from the ``uv.lock`` file committed in 
the repository, using
+``uv export --frozen`` to convert the lock file into a flat list of pinned 
versions suitable for
+``pip install --constraint``. This means the constraint files always reflect 
the same dependency
+versions that ``uv sync`` installs for developers.
+
 The ``constraints-<PYTHON_MAJOR_MINOR_VERSION>.txt`` and 
``constraints-no-providers-<PYTHON_MAJOR_MINOR_VERSION>.txt``
 will be automatically regenerated by CI job every time after the 
``pyproject.toml`` is updated and pushed
 if the tests are successful.
diff --git a/dev/breeze/src/airflow_breeze/utils/run_tests.py 
b/dev/breeze/src/airflow_breeze/utils/run_tests.py
index 5cd4e6692d8..752c3c66cf7 100644
--- a/dev/breeze/src/airflow_breeze/utils/run_tests.py
+++ b/dev/breeze/src/airflow_breeze/utils/run_tests.py
@@ -90,7 +90,16 @@ def verify_an_image(
     if slim_image:
         env["TEST_SLIM_IMAGE"] = "true"
     command_result = run_command(
-        ["uv", "run", "--isolated", "pytest", test_path.as_posix(), 
*pytest_args, *extra_pytest_args],
+        [
+            "uv",
+            "run",
+            "--frozen",
+            "--isolated",
+            "pytest",
+            test_path.as_posix(),
+            *pytest_args,
+            *extra_pytest_args,
+        ],
         env=env,
         output=output,
         check=False,
@@ -177,7 +186,7 @@ def run_docker_compose_tests(
         env["INCLUDE_SUCCESS_OUTPUTS"] = "true"
     env["AIRFLOW_UID"] = str(os.getuid())
     command_result = run_command(
-        ["uv", "run", "pytest", *pytest_args],
+        ["uv", "run", "--frozen", "pytest", *pytest_args],
         env=env,
         check=False,
         cwd=cwd,

Reply via email to