From 905f0b0488aa336ac42334c48932127e9796b79f Mon Sep 17 00:00:00 2001
From: LIU ZHE YOU <zhu424.dev@gmail.com>
Date: Tue, 21 Jul 2026 06:37:44 +0000
Subject: [PATCH] Fix flaky static checks caused by migration-reference hook
 race

The update-migration-references, -fab, and -edge3 hooks live in three
different prek workspaces that run in parallel, yet each invocation
processed all three apps and rewrote every migration file and
migrations-ref.rst even when nothing changed. Concurrent containers
racing on the same files intermittently failed CI static checks with
'revision = not found' when a reader caught a file mid-write.

Scope each hook to its own app and write files only when the content
actually changed, so no two hooks touch the same files.
---
 airflow-core/.pre-commit-config.yaml          |  1 +
 providers/edge3/.pre-commit-config.yaml       |  1 +
 providers/fab/.pre-commit-config.yaml         |  1 +
 scripts/ci/prek/migration_reference.py        |  4 +++-
 .../in_container/run_migration_reference.py   | 19 ++++++++++++++++---
 5 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/airflow-core/.pre-commit-config.yaml b/airflow-core/.pre-commit-config.yaml
index 949db83a81e63..591b27fd02134 100644
--- a/airflow-core/.pre-commit-config.yaml
+++ b/airflow-core/.pre-commit-config.yaml
@@ -262,6 +262,7 @@ repos:
         name: Update migration ref doc
         language: python
         entry: ../scripts/ci/prek/migration_reference.py
+        args: ["--app", "airflow"]
         pass_filenames: false
         files:
           (?x)
diff --git a/providers/edge3/.pre-commit-config.yaml b/providers/edge3/.pre-commit-config.yaml
index ec6accf2a86fa..000f8b6dab6f5 100644
--- a/providers/edge3/.pre-commit-config.yaml
+++ b/providers/edge3/.pre-commit-config.yaml
@@ -59,6 +59,7 @@ repos:
         name: Update migration ref doc for Edge3
         language: python
         entry: ../../scripts/ci/prek/migration_reference.py
+        args: ["--app", "edge3"]
         pass_filenames: false
         files: >
           (?x)
diff --git a/providers/fab/.pre-commit-config.yaml b/providers/fab/.pre-commit-config.yaml
index 4396285f744a1..2b5032ea3650f 100644
--- a/providers/fab/.pre-commit-config.yaml
+++ b/providers/fab/.pre-commit-config.yaml
@@ -55,6 +55,7 @@ repos:
         name: Update migration ref doc for FAB
         language: python
         entry: ../../scripts/ci/prek/migration_reference.py
+        args: ["--app", "fab"]
         pass_filenames: false
         files: >
           (?x)
diff --git a/scripts/ci/prek/migration_reference.py b/scripts/ci/prek/migration_reference.py
index d899783b0e97a..f63ca318f5749 100755
--- a/scripts/ci/prek/migration_reference.py
+++ b/scripts/ci/prek/migration_reference.py
@@ -23,6 +23,8 @@
 # ///
 from __future__ import annotations
 
+import sys
+
 from common_prek_utils import (
     initialize_breeze_prek,
     run_command_via_breeze_run,
@@ -32,7 +34,7 @@
 initialize_breeze_prek(__name__, __file__)
 
 cmd_result = run_command_via_breeze_run(
-    ["python3", "/opt/airflow/scripts/in_container/run_migration_reference.py"],
+    ["python3", "/opt/airflow/scripts/in_container/run_migration_reference.py", *sys.argv[1:]],
     backend="sqlite",
 )
 
diff --git a/scripts/in_container/run_migration_reference.py b/scripts/in_container/run_migration_reference.py
index 6a61bbf6d526a..067244ad7b7be 100755
--- a/scripts/in_container/run_migration_reference.py
+++ b/scripts/in_container/run_migration_reference.py
@@ -22,6 +22,7 @@
 
 from __future__ import annotations
 
+import argparse
 import os
 import re
 import textwrap
@@ -53,7 +54,9 @@ def replace_text_between(file: Path, start: str, end: str, replacement_text: str
     original_text = file.read_text()
     leading_text = original_text.split(start)[0]
     trailing_text = original_text.split(end)[1]
-    file.write_text(leading_text + start + replacement_text + end + trailing_text)
+    new_text = leading_text + start + replacement_text + end + trailing_text
+    if new_text != original_text:
+        file.write_text(new_text)
 
 
 def wrap_backticks(val):
@@ -275,11 +278,21 @@ def correct_mismatching_revision_nums(revisions: Iterable[Script]):
             if revises_id_match is None:
                 raise RuntimeError(f"Revises: not found in {file}")
             new_content = new_content.replace(revises_id_match.group(1), down_revision_match.group(1), 1)
-        file.write_text(new_content)
+        if new_content != content:
+            file.write_text(new_content)
 
 
 if __name__ == "__main__":
-    apps = ["airflow", "fab", "edge3"]
+    all_apps = ["airflow", "fab", "edge3"]
+    parser = argparse.ArgumentParser(description="Update migration references and docs.")
+    parser.add_argument(
+        "--app",
+        choices=all_apps,
+        default=None,
+        help="Only process this app (default: all apps).",
+    )
+    args = parser.parse_args()
+    apps = [args.app] if args.app else all_apps
     for app in apps:
         console.print(f"[bright_blue]Updating migration reference for {app}")
         revisions = list(reversed(list(get_revisions(app))))
