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)

Reply via email to