This is an automated email from the ASF dual-hosted git repository.
jason810496 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 6a5cbdbc131 ci: sync AGENTS.md commands from contributing docs via
prek hook (#68204)
6a5cbdbc131 is described below
commit 6a5cbdbc1318df8f32b1c7322c84b1076c30deb2
Author: LI,JHE-CHEN <[email protected]>
AuthorDate: Wed Jun 10 21:55:36 2026 +0800
ci: sync AGENTS.md commands from contributing docs via prek hook (#68204)
---
.pre-commit-config.yaml | 11 +++
AGENTS.md | 2 +
contributing-docs/08_static_code_checks.rst | 14 +++
contributing-docs/testing/unit_tests.rst | 27 +++++
scripts/ci/prek/generate_agent_skills.py | 110 +++++++++++++++++++++
.../tests/ci/prek/test_generate_agent_skills.py | 47 +++++++++
6 files changed, 211 insertions(+)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 2be1cba4bd2..43a488e2bcc 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -917,6 +917,17 @@ repos:
^airflow-core/src/airflow/ui/public/i18n/locales/en/.*\.json$|
^\.agents/skills/airflow-translations/SKILL\.md$
pass_filenames: false
+ - id: generate-agent-skills
+ name: Sync AGENTS.md commands from contributing docs
+ entry: ./scripts/ci/prek/generate_agent_skills.py
+ language: python
+ files: >
+ (?x)
+ ^contributing-docs/.*\.rst$|
+ ^AGENTS\.md$|
+ ^scripts/ci/prek/generate_agent_skills\.py$
+ pass_filenames: false
+ additional_dependencies: ['pyyaml', 'rich>=13.6.0']
- id: update-pyproject-toml
name: Update Airflow's meta-package pyproject.toml
language: python
diff --git a/AGENTS.md b/AGENTS.md
index 10ee55a9ee9..20e8abf3ac0 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -31,6 +31,7 @@ Don't spell out **Directed Acyclic Graph** except for
historical context.
`<PROJECT>` is folder where pyproject.toml of the package you want to test is
located. For example, `airflow-core` or `providers/amazon`.
`<target_branch>` is the branch the PR will be merged into — usually `main`,
but could be `v3-1-test` when creating a PR for the 3.1 branch.
+<!-- START generated-commands, please keep comment here to allow auto update
-->
- **Run a single test:** `uv run --project <PROJECT> pytest
path/to/test.py::TestClass::test_method -xvs`
- **Run a test file:** `uv run --project <PROJECT> pytest path/to/test.py -xvs`
- **Run all tests in package:** `uv run --project <PROJECT> pytest
path/to/package -xvs`
@@ -54,6 +55,7 @@ Don't spell out **Directed Acyclic Graph** except for
historical context.
- **Run manual (slower) checks:** `prek run --from-ref <target_branch> --stage
manual`
- **Build docs:** `breeze build-docs`
- **Determine which tests to run based on changed files:** `breeze
selective-checks --commit-ref <commit_with_squashed_changes>`
+<!-- END generated-commands, please keep comment here to allow auto update -->
SQLite is the default backend. Use `--backend postgres` or `--backend mysql`
for integration tests that need those databases. If Docker networking fails,
run `docker network prune`.
diff --git a/contributing-docs/08_static_code_checks.rst
b/contributing-docs/08_static_code_checks.rst
index a4a796f81bd..11b008bd5d0 100644
--- a/contributing-docs/08_static_code_checks.rst
+++ b/contributing-docs/08_static_code_checks.rst
@@ -226,6 +226,20 @@ You can always skip running the tests by providing
``--no-verify`` flag to the
To check other usage types of the pre-commit framework, see `Pre-commit
website <https://pre-commit.com/>`__.
+.. AGENT-SKILL-START
+ type: agents-md-commands
+ order: 30
+ lines:
+ - "- **Type-check (non-providers):** run the prek hook \u2014 `prek run
mypy-<project> --all-files` (e.g. `mypy-airflow-core`, `mypy-task-sdk`,
`mypy-shared-logging`; each `shared/<dist>` workspace member has its own
`mypy-shared-<dist>` hook). The hook uses a dedicated virtualenv and mypy cache
under `.build/mypy-venvs/<hook>/` and `.build/mypy-caches/<hook>/`; mypy itself
is installed from `uv.lock` via the `mypy` dependency group (`uv sync --group
mypy`), so it never mutates your [...]
+ - "- **Type-check (providers):** `breeze run mypy path/to/code`"
+ - "- **Lint with ruff only:** `prek run ruff --from-ref <target_branch>`"
+ - "- **Format with ruff only:** `prek run ruff-format --from-ref
<target_branch>`"
+ - "- **Run regular (fast) static checks:** `prek run --from-ref
<target_branch> --stage pre-commit`"
+ - "- **Run manual (slower) checks:** `prek run --from-ref <target_branch>
--stage manual`"
+ - "- **Build docs:** `breeze build-docs`"
+ - "- **Determine which tests to run based on changed files:** `breeze
selective-checks --commit-ref <commit_with_squashed_changes>`"
+.. AGENT-SKILL-END
+
Disabling particular checks
---------------------------
diff --git a/contributing-docs/testing/unit_tests.rst
b/contributing-docs/testing/unit_tests.rst
index 3032abe8664..9388eaef576 100644
--- a/contributing-docs/testing/unit_tests.rst
+++ b/contributing-docs/testing/unit_tests.rst
@@ -218,6 +218,17 @@ rerun in Breeze as needed (``-n auto`` will parallelize
tests using the ``pytest
breeze shell --backend none --python 3.10
> pytest airflow-core/tests --skip-db-tests -n auto
+.. AGENT-SKILL-START
+ type: agents-md-commands
+ order: 10
+ lines:
+ - "- **Run a single test:** `uv run --project <PROJECT> pytest
path/to/test.py::TestClass::test_method -xvs`"
+ - "- **Run a test file:** `uv run --project <PROJECT> pytest
path/to/test.py -xvs`"
+ - "- **Run all tests in package:** `uv run --project <PROJECT> pytest
path/to/package -xvs`"
+ - "- **If uv tests fail with missing system dependencies, run the tests
with breeze**: `breeze run pytest <tests> -xvs`"
+ - "- **Run a Python script:** `uv run --project <PROJECT> python
dev/my_script.py`"
+.. AGENT-SKILL-END
+
Airflow DB tests
................
@@ -278,6 +289,22 @@ As explained before, you cannot run DB tests in parallel
using the ``pytest-xdis
breeze testing core-tests --run-db-tests-only --backend postgres --python
3.10 --run-in-parallel
+.. AGENT-SKILL-START
+ type: agents-md-commands
+ order: 20
+ lines:
+ - "- **Run core or provider tests suite in parallel:** `breeze testing
<test_group> --run-in-parallel` (test groups: `core-tests`, `providers-tests`)"
+ - "- **Run core or provider db tests suite in parallel:** `breeze testing
<test_group> --run-db-tests-only --run-in-parallel` (test groups: `core-tests`,
`providers-tests`)"
+ - "- **Run core or provider non-db tests suite in parallel:** `breeze
testing <test_group> --skip-db-tests --use-xdist` (test groups: `core-tests`,
`providers-tests`)"
+ - "- **Run single provider complete test suite:** `breeze testing
providers-tests --test-type \"Providers[PROVIDERS_LIST]\"` (e.g.,
`Providers[google]` or `Providers[amazon]` or \"Providers[amazon,google]\")"
+ - "- **Run Helm tests in parallel with xdist** `breeze testing helm-tests
--use-xdist`"
+ - "- **Run Helm tests with specific K8s version:** `breeze testing
helm-tests --use-xdist --kubernetes-version 1.35.0`"
+ - "- **Run specific Helm test type:** `breeze testing helm-tests
--use-xdist --test-type <type>` (types: `airflow_aux`, `airflow_core`,
`apiserver`, `dagprocessor`, `other`, `redis`, `security`, `statsd`,
`webserver`)"
+ - "- **Run other suites of tests** `breeze testing <test_group>` (test
groups: `airflow-ctl-tests`, `docker-compose-tests`, `task-sdk-tests`)"
+ - "- **Run scripts tests:** `uv run --project scripts pytest
scripts/tests/ -xvs`"
+ - "- **Run Airflow CLI:** `breeze run airflow dags list`"
+.. AGENT-SKILL-END
+
Examples of marking test as DB test
...................................
diff --git a/scripts/ci/prek/generate_agent_skills.py
b/scripts/ci/prek/generate_agent_skills.py
new file mode 100644
index 00000000000..a3c59418e13
--- /dev/null
+++ b/scripts/ci/prek/generate_agent_skills.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+# 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.
+"""Sync the AGENTS.md Commands section from contributing docs.
+
+Contributing docs contain ``.. AGENT-SKILL-START`` / ``.. AGENT-SKILL-END``
+RST comment blocks with pre-formatted AGENTS.md bullet lines. This script
+collects those blocks, sorts them by ``order``, and updates the
+generated-commands section in AGENTS.md via ``insert_documentation()``.
+
+The prek hook runs this automatically when contributing docs or AGENTS.md
+change.
+"""
+
+from __future__ import annotations
+
+import re
+import sys
+from dataclasses import dataclass
+from pathlib import Path
+
+import yaml
+from common_prek_utils import AIRFLOW_ROOT_PATH, console, insert_documentation
+
+CONTRIBUTING_DOCS_ROOT = AIRFLOW_ROOT_PATH / "contributing-docs"
+AGENTS_MD_PATH = AIRFLOW_ROOT_PATH / "AGENTS.md"
+AGENTS_MD_START = "<!-- START generated-commands, please keep comment here to
allow auto update -->"
+AGENTS_MD_END = "<!-- END generated-commands, please keep comment here to
allow auto update -->"
+BLOCK_RE = re.compile(r"\.\. AGENT-SKILL-START\n(.*?)\.\. AGENT-SKILL-END",
re.DOTALL)
+
+
+@dataclass(frozen=True)
+class CommandBlock:
+ order: int
+ lines: tuple[str, ...]
+
+
+def _dedent_rst_comment_body(raw: str) -> str:
+ """Remove the 3-space RST comment indentation from each line."""
+ lines = raw.splitlines()
+ return "\n".join(line[3:] if line.startswith(" ") else line for line in
lines).strip()
+
+
+def _parse_blocks_from_rst(rst_path: Path) -> list[CommandBlock]:
+ """Extract all AGENT-SKILL command blocks from a single RST file."""
+ text = rst_path.read_text()
+ blocks: list[CommandBlock] = []
+ for match in BLOCK_RE.finditer(text):
+ block = yaml.safe_load(_dedent_rst_comment_body(match.group(1)))
+ if not isinstance(block, dict) or block.get("type") !=
"agents-md-commands":
+ continue
+ order = int(block.get("order", 100))
+ raw_lines = block.get("lines", [])
+ lines = tuple(str(line).rstrip() for line in raw_lines)
+ blocks.append(CommandBlock(order=order, lines=lines))
+ return blocks
+
+
+def collect_blocks(
+ contributing_docs_root: Path = CONTRIBUTING_DOCS_ROOT,
+) -> list[CommandBlock]:
+ """Scan all RST files and return command blocks sorted by order."""
+ blocks: list[CommandBlock] = []
+ for rst_path in sorted(contributing_docs_root.rglob("*.rst")):
+ blocks.extend(_parse_blocks_from_rst(rst_path))
+ blocks.sort(key=lambda b: b.order)
+ return blocks
+
+
+def render_lines(blocks: list[CommandBlock]) -> list[str]:
+ """Flatten all blocks into a list of lines for insert_documentation()."""
+ result: list[str] = []
+ for block in blocks:
+ for line in block.lines:
+ result.append(line + "\n")
+ return result
+
+
+def main() -> int:
+ blocks = collect_blocks()
+ if not blocks:
+ console.print("[red]No AGENT-SKILL blocks found in contributing
docs.[/red]")
+ return 1
+ content = render_lines(blocks)
+ insert_documentation(
+ file_path=AGENTS_MD_PATH,
+ content=content,
+ header=AGENTS_MD_START,
+ footer=AGENTS_MD_END,
+ extra_information="AGENTS.md commands from contributing docs",
+ )
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/scripts/tests/ci/prek/test_generate_agent_skills.py
b/scripts/tests/ci/prek/test_generate_agent_skills.py
new file mode 100644
index 00000000000..166b7e65ee8
--- /dev/null
+++ b/scripts/tests/ci/prek/test_generate_agent_skills.py
@@ -0,0 +1,47 @@
+# 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.
+from __future__ import annotations
+
+from ci.prek.generate_agent_skills import (
+ collect_blocks,
+ render_lines,
+)
+
+
+def test_collect_blocks_finds_all_markers():
+ blocks = collect_blocks()
+
+ assert len(blocks) >= 3
+
+
+def test_collect_blocks_sorted_by_order():
+ blocks = collect_blocks()
+
+ orders = [b.order for b in blocks]
+ assert orders == sorted(orders)
+
+
+def test_render_lines_produces_bullet_list():
+ blocks = collect_blocks()
+ lines = render_lines(blocks)
+
+ assert len(lines) > 0
+ assert all(line.startswith("- **") for line in lines)
+ assert any("uv run --project <PROJECT> pytest" in line for line in lines)
+ assert any("breeze testing helm-tests" in line for line in lines)
+ assert any("prek run mypy-<project>" in line for line in lines)
+ assert any("breeze selective-checks" in line for line in lines)