This is an automated email from the ASF dual-hosted git repository.
jasonliu 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 9d027a42683 Check execution API datamodel changes have corresponding
version updates (#62755)
9d027a42683 is described below
commit 9d027a42683c82f23d3217925e442e4052ea1637
Author: Jason(Zhe-You) Liu <[email protected]>
AuthorDate: Thu Mar 12 14:48:39 2026 +0800
Check execution API datamodel changes have corresponding version updates
(#62755)
* Initial plan
* Add pre-commit hook to check execution API datamodel changes have version
updates
Co-authored-by: jason810496 <[email protected]>
* Address code review: add warning on fetch failure
Co-authored-by: jason810496 <[email protected]>
* Align common prek script usage
* Fix CI error
* Test CI verfication: Should raise error on CI
* Revert "Test CI verfication: Should raise error on CI"
This reverts commit 3e28f36ba9bf81fd7cb9cc4523767309e0d828db.
* Exclude __init__.py from execution API datamodel version check
Co-authored-by: Jason(Zhe-You) Liu <[email protected]>
* Fix ruff formatting
---------
Co-authored-by: copilot-swe-agent[bot]
<[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Jason(Zhe-You) Liu <[email protected]>
---
.pre-commit-config.yaml | 7 ++
scripts/ci/prek/check_execution_api_versions.py | 132 ++++++++++++++++++++++++
2 files changed, 139 insertions(+)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 5883e31944a..fa144dc3d70 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -998,6 +998,13 @@ repos:
entry: ./scripts/ci/prek/check_template_fields.py
files: ^(providers/.*/)?airflow-core/.*/(sensors|operators)/.*\.py$
require_serial: true
+ - id: check-execution-api-versions
+ name: Check execution API datamodel changes have corresponding version
updates
+ entry: ./scripts/ci/prek/check_execution_api_versions.py
+ language: python
+ pass_filenames: false
+ files:
^airflow-core/src/airflow/api_fastapi/execution_api/(datamodels|versions)/.*\.py$
+ require_serial: true
- id: generate-tasksdk-datamodels
name: Generate Datamodels for TaskSDK client
language: python
diff --git a/scripts/ci/prek/check_execution_api_versions.py
b/scripts/ci/prek/check_execution_api_versions.py
new file mode 100755
index 00000000000..3841c32c140
--- /dev/null
+++ b/scripts/ci/prek/check_execution_api_versions.py
@@ -0,0 +1,132 @@
+#!/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.
+# /// script
+# requires-python = ">=3.10,<3.11"
+# dependencies = [
+# "rich>=13.6.0",
+# ]
+# ///
+from __future__ import annotations
+
+import os
+import subprocess
+import sys
+from pathlib import Path
+
+sys.path.insert(0, str(Path(__file__).parent.resolve())) # make sure
common_prek_utils is imported
+from common_prek_utils import console
+
+DATAMODELS_PREFIX =
"airflow-core/src/airflow/api_fastapi/execution_api/datamodels/"
+VERSIONS_PREFIX =
"airflow-core/src/airflow/api_fastapi/execution_api/versions/"
+
+
+def get_changed_files_ci() -> list[str]:
+ """Get changed files in a CI environment by comparing against the target
branch."""
+ target_branch = os.environ.get("GITHUB_BASE_REF", "main")
+ fetch_result = subprocess.run(
+ ["git", "fetch", "origin", target_branch],
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+ if fetch_result.returncode != 0:
+ console.print(
+ f"[yellow]WARNING: Failed to fetch origin/{target_branch}:
{fetch_result.stderr.strip()}[/]"
+ )
+ result = subprocess.run(
+ ["git", "diff", "--name-only", f"origin/{target_branch}...HEAD"],
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+ if result.returncode != 0:
+ # Shallow clone (fetch-depth: 1) may not have enough history to
compute the
+ # merge base required by the three-dot diff. Deepen the fetch and
retry once.
+ console.print(
+ f"[yellow]WARNING: git diff against origin/{target_branch} failed
(exit {result.returncode}), "
+ "retrying with deeper fetch...[/]"
+ )
+ subprocess.run(
+ ["git", "fetch", "--deepen=50", "origin", target_branch],
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+ try:
+ result = subprocess.run(
+ ["git", "diff", "--name-only",
f"origin/{target_branch}...HEAD"],
+ capture_output=True,
+ text=True,
+ check=True,
+ )
+ except subprocess.CalledProcessError as e:
+ console.print(f"[bold red]ERROR:[/] git diff failed (exit
{e.returncode})")
+ if e.stdout:
+ console.print(f"[dim]stdout:[/]\n{e.stdout.strip()}")
+ if e.stderr:
+ console.print(f"[dim]stderr:[/]\n{e.stderr.strip()}")
+ raise
+ return [f for f in result.stdout.strip().splitlines() if f]
+
+
+def get_changed_files_local() -> list[str]:
+ """Get staged files in a local (non-CI) environment."""
+ result = subprocess.run(
+ ["git", "diff", "--cached", "--name-only"],
+ capture_output=True,
+ text=True,
+ check=True,
+ )
+ return [f for f in result.stdout.strip().splitlines() if f]
+
+
+def main() -> int:
+ is_ci = os.environ.get("CI")
+ if is_ci:
+ changed_files = get_changed_files_ci()
+ else:
+ changed_files = get_changed_files_local()
+
+ datamodel_files = [
+ f for f in changed_files if f.startswith(DATAMODELS_PREFIX) and not
f.endswith("__init__.py")
+ ]
+ version_files = [f for f in changed_files if f.startswith(VERSIONS_PREFIX)]
+
+ if datamodel_files and not version_files:
+ console.print(
+ "[bold red]ERROR:[/] Changes to execution API datamodels require
corresponding changes in versions."
+ )
+ console.print("")
+ console.print("The following datamodel files were changed:")
+ for f in datamodel_files:
+ console.print(f" - [magenta]{f}[/]")
+ console.print("")
+ console.print(
+ "But no files were changed under:\n"
+ f" [cyan]{VERSIONS_PREFIX}[/]\n"
+ "\n"
+ "Please add or update a version file to reflect the datamodel
changes.\n"
+ "See [cyan]contributing-docs/19_execution_api_versioning.rst[/]
for details."
+ )
+ return 1
+
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())