This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch v3-3-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-3-test by this push:
new 6ad3c2c5eb1 [v3-3-test] Don't force full CI tests for non-test
workflow and prek-only changes (#68802) (#68999)
6ad3c2c5eb1 is described below
commit 6ad3c2c5eb143007e77a2fbcdd48e215aa1be86d
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri Jun 26 14:48:16 2026 -0400
[v3-3-test] Don't force full CI tests for non-test workflow and prek-only
changes (#68802) (#68999)
ENVIRONMENT_FILES forced the full test matrix on any .github/workflows
change and on any scripts/ci/prek change. Those over-match: non-test
workflows (security scans, doc publishing, notifications, backporting,
stale/calendar bots) cannot affect test outcomes, and prek hooks drive
static checks rather than the test matrix.
Exclude non-test workflows from ENVIRONMENT_FILES (test/CI-config
workflows like ci-amd, run-unit-tests, k8s-tests still force full), and
move scripts/ci/prek to a new PREK_FILES group that triggers only the CI
image build (so mypy-scripts and image-based static checks still run for
a prek-only change) without forcing the full matrix.
Static checks still run on every PR regardless; this only stops the
unnecessary full test matrix for changes that cannot affect tests.
(cherry picked from commit a4c269a6f9e0176995fbea971dd4e01ac9ae3123)
Co-authored-by: Jarek Potiuk <[email protected]>
---
dev/breeze/doc/ci/04_selective_checks.md | 23 ++++++++---
.../src/airflow_breeze/utils/selective_checks.py | 23 ++++++++++-
dev/breeze/tests/test_selective_checks.py | 48 +++++++++++++++++++++-
3 files changed, 85 insertions(+), 9 deletions(-)
diff --git a/dev/breeze/doc/ci/04_selective_checks.md
b/dev/breeze/doc/ci/04_selective_checks.md
index fb081df0aa1..5dbaf289f9e 100644
--- a/dev/breeze/doc/ci/04_selective_checks.md
+++ b/dev/breeze/doc/ci/04_selective_checks.md
@@ -160,7 +160,7 @@ not matter because any single hit forces the full matrix:
flowchart TD
A([full_tests_needed]) --> M{_should_run_all_tests_and_versions?}
M -->|yes| T[TRUE - full matrix]
- M -->|no| E1{environment files?<br/>.github/workflows,
dev/breeze/src,<br/>Dockerfile, scripts/ci, ...}
+ M -->|no| E1{environment files?<br/>test workflows,
dev/breeze/src,<br/>Dockerfile, scripts/ci/docker-compose+kubernetes,
...<br/>excl. prek + non-test workflows}
E1 -->|yes| T
E1 -->|no| E2{API contract / codegen?<br/>generated OpenAPI spec or
generator}
E2 -->|yes| T
@@ -240,6 +240,8 @@ representative examples (file → effect):
| `airflow-core/src/airflow/jobs/scheduler_job_runner.py` | unit tests with
**all** core test types | a core/other file → escape
hatch runs all core types |
| `pyproject.toml` | **full matrix, all
versions** | dependency surface changed
→ `_should_run_all_tests_and_versions` |
| `.github/workflows/ci-amd.yml` or `dev/breeze/src/...` | **full matrix**
| environment files → can
change the whole CI environment |
+| `.github/workflows/codeql-analysis.yml` (non-test workflow) | **basic checks
only** | non-test workflow → cannot
affect tests (env-files carve-out) |
+| `scripts/ci/prek/check_*.py` (static-check hook) | CI image + static
checks, **no full matrix** | prek hooks are static
checks → `Prek files` carve-out |
| the generated OpenAPI spec | **full matrix**
| the API *contract* ripples
to UI codegen + every client |
| `chart/templates/...yaml` (on `main`) | `run_helm_tests` (+
PROD image) | matches `HELM_FILES`; Helm
tests only on `main` |
| `airflow-core/src/airflow/ui/...tsx` only | `run_ui_tests`,
**no** unit tests | "only new-UI files"
short-circuit skips Python unit tests |
@@ -279,9 +281,11 @@ flowchart TD
2. **Pull request, core change** (`scheduler_job_runner.py`). Still
`full_tests_needed=False`, but the
core/other escape hatch adds **all core test types**. Providers are not
pulled in (no provider files
changed). Default versions.
-3. **Pull request that changes `pyproject.toml`** (or `.github/workflows/...`,
or the OpenAPI spec).
- `full_tests_needed=True` (and for `pyproject.toml` also
`all_versions=True`). The PR runs the **full
- matrix** — same as a canary — because the change can affect everything.
+3. **Pull request that changes `pyproject.toml`** (or a *test* workflow like
`.github/workflows/ci-amd.yml`,
+ or the OpenAPI spec). `full_tests_needed=True` (and for `pyproject.toml`
also `all_versions=True`). The
+ PR runs the **full matrix** — same as a canary — because the change can
affect everything. (A *non-test*
+ workflow such as `codeql-analysis.yml`, or a `scripts/ci/prek/...`
static-check hook, does **not** force
+ the full matrix — see the `Environment files` carve-outs above.)
4. **Push / merge to `main`.** `_should_run_all_tests_and_versions()` is true
for PUSH on `main` →
`full_tests_needed=True`, `all_versions=True`, `is_canary_run=True`. The
full matrix plus all
canary-only jobs run. This is the safety net that backstops aggressive
PR-time optimisation.
@@ -333,7 +337,16 @@ The authoritative, exhaustive rule list (kept in sync with
the code) is in
We have the following Groups of files for CI that determine which tests are
run:
* `Environment files` - if any of those changes, that forces 'full tests
needed' mode, because changes
- there might simply change the whole environment of what is going on in CI
(Container image, dependencies)
+ there might simply change the whole environment of what is going on in CI
(Container image, dependencies).
+ Two deliberate carve-outs do **not** force full tests: **static-check
hooks** (`scripts/ci/prek/...`)
+ drive prek static checks rather than the test matrix, so they only trigger
the CI image build (via the
+ `Prek files` group) — `mypy-scripts` and the image-based static checks still
run, but the full test
+ matrix does not; and **non-test workflows** (`.github/workflows/` for
security scans, doc publishing,
+ notifications, backporting, calendar/stale bots, …) cannot change test
outcomes. Workflows that run or
+ configure the test suite (`ci-amd`, `ci-arm`, `run-unit-tests`, `k8s-tests`,
`helm-tests`, image builds,
+ …) still force the full matrix.
+* `Prek files` - `scripts/ci/prek/...` static-check hooks. They do not force
full tests, but they trigger
+ the CI image build so `mypy-scripts` and the image-based static checks run
for a prek-only change.
* `Python production files` and `Javascript production files` - this area is
useful in CodeQL Security scanning
- if any of the python or javascript files for airflow "production" changed,
this means that the security
scans should run
diff --git a/dev/breeze/src/airflow_breeze/utils/selective_checks.py
b/dev/breeze/src/airflow_breeze/utils/selective_checks.py
index 8d4c512ff1a..fcaf8ae5c0a 100644
--- a/dev/breeze/src/airflow_breeze/utils/selective_checks.py
+++ b/dev/breeze/src/airflow_breeze/utils/selective_checks.py
@@ -149,6 +149,7 @@ class FileGroupForCi(Enum):
DEVEL_TOML_FILES = auto()
SCRIPTS_FILES = auto()
UV_LOCK_FILE = auto()
+ PREK_FILES = auto()
KERBEROS_FILES = auto()
OTEL_FILES = auto()
CELERY_FILES = auto()
@@ -171,7 +172,15 @@ class HashableDict(dict[T, list[str]]):
CI_FILE_GROUP_MATCHES: HashableDict[FileGroupForCi] = HashableDict(
{
FileGroupForCi.ENVIRONMENT_FILES: [
- r"^.github/workflows",
+ # Only workflows that actually run or configure the test suite
force the full
+ # matrix. Non-test workflows (security scans, doc publishing,
notifications,
+ # backporting, stale/calendar bots, …) cannot affect test
outcomes, so they
+ # are excluded here to avoid accidental full-matrix runs.
+ r"^\.github/workflows/(?!("
+
r"asf-allowlist-check|automatic-backport|backport-cli|ci-duration-monitor|ci-notification|"
+
r"codeql-analysis|e2e-flaky-tests-report|milestone-tag-assistant|notify-uv-lock-conflicts|"
+
r"publish-docs-to-s3|recheck-old-bug-report|scheduled-verify-release-calendar|stale"
+ r")\.yml$)",
r"^dev/breeze/src",
r"^dev/breeze/pyproject\.toml",
r"^dev/breeze/uv\.lock",
@@ -179,7 +188,10 @@ CI_FILE_GROUP_MATCHES: HashableDict[FileGroupForCi] =
HashableDict(
r"^Dockerfile",
r"^scripts/ci/docker-compose",
r"^scripts/ci/kubernetes",
- r"^scripts/ci/prek",
+ # NOTE: scripts/ci/prek (static-check hooks) is intentionally NOT
here. prek
+ # hooks drive static checks, not the test matrix, so they must not
force the
+ # full matrix. They still build the CI image via
FileGroupForCi.PREK_FILES
+ # below (so mypy-scripts and all static checks still run).
r"^scripts/docker",
r"^scripts/in_container",
],
@@ -438,6 +450,9 @@ CI_FILE_GROUP_MATCHES: HashableDict[FileGroupForCi] =
HashableDict(
r"^scripts/tools/.*\.py$",
r"^scripts/tests/.*\.py$",
],
+ FileGroupForCi.PREK_FILES: [
+ r"^scripts/ci/prek",
+ ],
FileGroupForCi.UV_LOCK_FILE: [
r"^uv\.lock$",
],
@@ -1100,6 +1115,10 @@ class SelectiveChecks:
or self.pyproject_toml_changed
or self.any_provider_yaml_or_pyproject_toml_changed
or self.prod_image_build
+ # prek hooks no longer force the full test matrix (they are static
checks, not
+ # tests), but they still need the CI image so mypy-scripts and the
image-based
+ # static checks run for a prek-only change.
+ or bool(self._matching_files(FileGroupForCi.PREK_FILES,
CI_FILE_GROUP_MATCHES))
)
@cached_property
diff --git a/dev/breeze/tests/test_selective_checks.py
b/dev/breeze/tests/test_selective_checks.py
index 98cf2333a46..7b2848a6f46 100644
--- a/dev/breeze/tests/test_selective_checks.py
+++ b/dev/breeze/tests/test_selective_checks.py
@@ -1768,9 +1768,13 @@ def test_excluded_providers():
pytest.param(
("scripts/ci/prek/file.sh",),
{
- "full-tests-needed": "true",
+ # prek hooks are static checks, not tests: they must not
force the full
+ # test matrix, but they still build the CI image so
mypy-scripts and the
+ # image-based static checks run.
+ "full-tests-needed": "false",
+ "ci-image-build": "true",
},
- id="Full tests needed when prek scripts change",
+ id="prek scripts build the CI image but do not force full
tests",
)
),
(
@@ -1812,6 +1816,46 @@ def test_full_test_needed_when_scripts_changes(files:
tuple[str, ...], expected_
assert_outputs_are_printed(expected_outputs, str(stderr))
[email protected](
+ ("files", "expected_outputs"),
+ [
+ pytest.param(
+ (".github/workflows/ci-amd.yml",),
+ {"full-tests-needed": "true"},
+ id="Test workflow (ci-amd) forces full tests",
+ ),
+ pytest.param(
+ (".github/workflows/run-unit-tests.yml",),
+ {"full-tests-needed": "true"},
+ id="Test workflow (run-unit-tests) forces full tests",
+ ),
+ pytest.param(
+ (".github/workflows/codeql-analysis.yml",),
+ {"full-tests-needed": "false"},
+ id="Non-test workflow (codeql) does not force full tests",
+ ),
+ pytest.param(
+ (".github/workflows/publish-docs-to-s3.yml",),
+ {"full-tests-needed": "false"},
+ id="Non-test workflow (publish-docs-to-s3) does not force full
tests",
+ ),
+ pytest.param(
+ (".github/workflows/ci-notification.yml",),
+ {"full-tests-needed": "false"},
+ id="Non-test workflow (ci-notification) does not force full tests",
+ ),
+ ],
+)
+def test_non_test_workflows_do_not_force_full_tests(files: tuple[str, ...],
expected_outputs: dict[str, str]):
+ stderr = SelectiveChecks(
+ files=files,
+ github_event=GithubEvents.PULL_REQUEST,
+ commit_ref=NEUTRAL_COMMIT,
+ default_branch="main",
+ )
+ assert_outputs_are_printed(expected_outputs, str(stderr))
+
+
@pytest.mark.parametrize(
("files", "pr_labels", "default_branch", "expected_outputs"),
[