This is an automated email from the ASF dual-hosted git repository.
shahar1 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 49a52987956 Stop version-check hooks from clobbering the committing
worktree's index (#74115)
49a52987956 is described below
commit 49a52987956c514025171627b19ba536be6d713f
Author: Ash Berlin-Taylor <[email protected]>
AuthorDate: Fri Oct 2 19:27:27 2026 +0100
Stop version-check hooks from clobbering the committing worktree's index
(#74115)
This took me a while to track down, but when I ran a prek on a file that
triggers the supervisor-schema check, the entire git index (i.e. what
git thinks is changed or not) got completely mangled.
This turns out to be because git commit exports GIT_INDEX_FILE (and
related variables) to hooks. The supervisor-schema and execution-API
version checks run `git worktree add` as a child process, so the
checkout of origin/main wrote into the committing repo/worktree's index
instead of the temporary worktree's own. Anyone committing a file that
triggers either hook got origin/main's whole tree silently staged, and
the commit itself could end up with that tree.
---
scripts/ci/prek/check_execution_api_versions.py | 15 ++++++++++++---
.../ci/prek/check_supervisor_schemas_versions.py | 11 +++++++++--
scripts/ci/prek/common_prek_utils.py | 21 +++++++++++++++++++++
3 files changed, 42 insertions(+), 5 deletions(-)
diff --git a/scripts/ci/prek/check_execution_api_versions.py
b/scripts/ci/prek/check_execution_api_versions.py
index 833ce32bcf7..570a7ed1236 100755
--- a/scripts/ci/prek/check_execution_api_versions.py
+++ b/scripts/ci/prek/check_execution_api_versions.py
@@ -30,7 +30,7 @@ import sys
import tempfile
from pathlib import Path
-from common_prek_utils import console, get_remote_for_main
+from common_prek_utils import console, get_remote_for_main,
git_env_without_repo_overrides
DATAMODELS_PREFIX =
"airflow-core/src/airflow/api_fastapi/execution_api/datamodels/"
VERSIONS_PREFIX =
"airflow-core/src/airflow/api_fastapi/execution_api/versions/"
@@ -76,12 +76,21 @@ def generate_schema_from_main() -> dict:
ref = f"{remote}/{target_branch}"
worktree_path = Path(tempfile.mkdtemp()) / "airflow-main"
subprocess.run(["git", "fetch", remote, target_branch],
capture_output=True, check=False)
- subprocess.run(["git", "worktree", "add", str(worktree_path), ref],
capture_output=True, check=True)
+ git_env = git_env_without_repo_overrides()
+ subprocess.run(
+ ["git", "worktree", "add", str(worktree_path), ref],
+ capture_output=True,
+ check=True,
+ env=git_env,
+ )
try:
return generate_schema(worktree_path)
finally:
subprocess.run(
- ["git", "worktree", "remove", "--force", str(worktree_path)],
capture_output=True, check=False
+ ["git", "worktree", "remove", "--force", str(worktree_path)],
+ capture_output=True,
+ check=False,
+ env=git_env,
)
diff --git a/scripts/ci/prek/check_supervisor_schemas_versions.py
b/scripts/ci/prek/check_supervisor_schemas_versions.py
index 5e011e8f43f..217b459b09b 100755
--- a/scripts/ci/prek/check_supervisor_schemas_versions.py
+++ b/scripts/ci/prek/check_supervisor_schemas_versions.py
@@ -47,7 +47,7 @@ import sys
import tempfile
from pathlib import Path
-from common_prek_utils import console, get_remote_for_main
+from common_prek_utils import console, get_remote_for_main,
git_env_without_repo_overrides
SUPERVISOR_SCHEMAS_PREFIX = "task-sdk/src/airflow/sdk/execution_time/schema/"
VERSIONS_PREFIX = SUPERVISOR_SCHEMAS_PREFIX + "versions/"
@@ -123,7 +123,13 @@ def dump_snapshot_from_main() -> str:
ref = f"{remote}/{target_branch}"
worktree_path = Path(tempfile.mkdtemp()) / "airflow-main"
subprocess.run(["git", "fetch", remote, target_branch],
capture_output=True, check=False)
- subprocess.run(["git", "worktree", "add", str(worktree_path), ref],
capture_output=True, check=True)
+ git_env = git_env_without_repo_overrides()
+ subprocess.run(
+ ["git", "worktree", "add", str(worktree_path), ref],
+ capture_output=True,
+ check=True,
+ env=git_env,
+ )
try:
return dump_snapshot(worktree_path)
finally:
@@ -131,6 +137,7 @@ def dump_snapshot_from_main() -> str:
["git", "worktree", "remove", "--force", str(worktree_path)],
capture_output=True,
check=False,
+ env=git_env,
)
diff --git a/scripts/ci/prek/common_prek_utils.py
b/scripts/ci/prek/common_prek_utils.py
index b68ec286611..bbc6e1ecc42 100644
--- a/scripts/ci/prek/common_prek_utils.py
+++ b/scripts/ci/prek/common_prek_utils.py
@@ -901,6 +901,27 @@ def get_imports_from_file(file_path: Path, *,
only_top_level: bool) -> list[str]
return imports
+GIT_REPO_OVERRIDE_VARIABLES = (
+ "GIT_INDEX_FILE",
+ "GIT_DIR",
+ "GIT_WORK_TREE",
+ "GIT_PREFIX",
+ "GIT_COMMON_DIR",
+ "GIT_OBJECT_DIRECTORY",
+ "GIT_ALTERNATE_OBJECT_DIRECTORIES",
+)
+
+
+def git_env_without_repo_overrides() -> dict[str, str]:
+ """
+ Return an environment for git commands that must not act on the repo being
committed.
+
+ ``git commit`` exports ``GIT_INDEX_FILE`` (and friends) to hooks; a child
``git worktree add``
+ would otherwise check out into the committing repo's index instead of its
own.
+ """
+ return {k: v for k, v in os.environ.items() if k not in
GIT_REPO_OVERRIDE_VARIABLES}
+
+
def get_remote_for_main() -> str:
"""
Return the remote name to use when fetching main.