From 736604a7736de48c656a2a7bb44b943e433463c1 Mon Sep 17 00:00:00 2001 From: Alex Reznichenko Date: Sat, 24 Oct 2026 11:34:13 +0000 Subject: [PATCH 1/2] ci: exclude vendored STM32Cube driver trees from the merge-marker check Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01TPiK6pUmmjNhPceECkKpR2 Signed-off-by: Alex Reznichenko --- .../workflows/repository-quality-reusable.yml | 3 +- tests/test_reusable_workflow.py | 34 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/.github/workflows/repository-quality-reusable.yml b/.github/workflows/repository-quality-reusable.yml index 302f30e..f2613f5 100644 --- a/.github/workflows/repository-quality-reusable.yml +++ b/.github/workflows/repository-quality-reusable.yml @@ -83,7 +83,8 @@ jobs: shell: bash run: | set -euo pipefail - if git grep -nE '^(<<<<<<<|=======|>>>>>>>)' -- ':!*.patch' ':!*.diff' ':!*.md' ':!**/*.md'; then + # Vendored STM32Cube driver trees keep ST's original files; their "=====" doc rules are not merge markers. + if git grep -nE '^(<<<<<<<|=======|>>>>>>>)' -- ':!*.patch' ':!*.diff' ':!*.md' ':!**/*.md' ':!**/Drivers/STM32*/**' ':!**/Drivers/CMSIS/**' ':!**/Drivers/BSP/**'; then echo "::error::Unresolved merge-conflict marker detected" exit 1 fi diff --git a/tests/test_reusable_workflow.py b/tests/test_reusable_workflow.py index 33b8931..7f7125f 100644 --- a/tests/test_reusable_workflow.py +++ b/tests/test_reusable_workflow.py @@ -139,5 +139,39 @@ def test_clean_run_passes_in_both_modes(self): self.assertEqual(self.run_step("Decisions of record", 0, enforce=enforce)[0], 0) +class MergeMarkers(unittest.TestCase): + """The "Reject unresolved merge markers" step, run from the workflow file in a scratch repository.""" + + def run_marker_step(self, files): + _, steps = load() + with tempfile.TemporaryDirectory() as tmp: + for rel, text in files.items(): + path = Path(tmp, rel) + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(text, encoding="utf-8") + git = ["git", "-c", "user.name=test", "-c", "user.email=test@example.invalid", "-C", tmp] + subprocess.run(["git", "init", "-q", tmp], check=True) + subprocess.run(git + ["add", "-A"], check=True) + subprocess.run(git + ["commit", "-q", "-m", "fixture"], check=True) + proc = subprocess.run(["bash", "-c", steps["Reject unresolved merge markers"]["run"]], cwd=tmp, + capture_output=True, text=True) + return proc.returncode, proc.stdout + proc.stderr + + def test_vendored_stm32cube_drivers_are_excluded_but_project_code_is_not(self): + hal = "boards/stm32/X/Drivers/STM32H7xx_HAL_Driver/Src/hal.c" + cmsis = "boards/stm32/X/Drivers/CMSIS/Include/c.h" + real = "src/real.c" + code, out = self.run_marker_step({ + hal: "/*\n=======\n ST documentation rule\n*/\n", + cmsis: "/*\n=======\n*/\n", + real: "int a;\n=======\nint b;\n", + }) + self.assertEqual(code, 1, out) + self.assertIn(real, out) + self.assertIn("Unresolved merge-conflict marker detected", out) + self.assertNotIn(hal, out) + self.assertNotIn(cmsis, out) + + if __name__ == "__main__": unittest.main() From 78ee2b26c22f79f1b6f23e815849bb2d53b85728 Mon Sep 17 00:00:00 2001 From: Alex Reznichenko Date: Sat, 24 Oct 2026 12:24:06 +0000 Subject: [PATCH 2/2] ci: limit the merge-marker exclusion to the exact STM32Cube vendor trees Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01TPiK6pUmmjNhPceECkKpR2 Signed-off-by: Alex Reznichenko --- .../workflows/repository-quality-reusable.yml | 2 +- tests/test_reusable_workflow.py | 31 +++++++++++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/.github/workflows/repository-quality-reusable.yml b/.github/workflows/repository-quality-reusable.yml index f2613f5..419abd3 100644 --- a/.github/workflows/repository-quality-reusable.yml +++ b/.github/workflows/repository-quality-reusable.yml @@ -84,7 +84,7 @@ jobs: run: | set -euo pipefail # Vendored STM32Cube driver trees keep ST's original files; their "=====" doc rules are not merge markers. - if git grep -nE '^(<<<<<<<|=======|>>>>>>>)' -- ':!*.patch' ':!*.diff' ':!*.md' ':!**/*.md' ':!**/Drivers/STM32*/**' ':!**/Drivers/CMSIS/**' ':!**/Drivers/BSP/**'; then + if git grep -nE '^(<<<<<<<|=======|>>>>>>>)' -- ':!*.patch' ':!*.diff' ':!*.md' ':!**/*.md' ':!**/Drivers/STM32H7xx_HAL_Driver/**' ':!**/Drivers/CMSIS/**' ':!**/Drivers/BSP/STM32H7xx_Nucleo/**'; then echo "::error::Unresolved merge-conflict marker detected" exit 1 fi diff --git a/tests/test_reusable_workflow.py b/tests/test_reusable_workflow.py index 7f7125f..b84ebe7 100644 --- a/tests/test_reusable_workflow.py +++ b/tests/test_reusable_workflow.py @@ -157,14 +157,19 @@ def run_marker_step(self, files): capture_output=True, text=True) return proc.returncode, proc.stdout + proc.stderr + VENDORED = ("boards/stm32/X/Drivers/STM32H7xx_HAL_Driver/Src/hal.c", + "boards/stm32/X/Drivers/CMSIS/Include/c.h", + "boards/stm32/X/Drivers/BSP/STM32H7xx_Nucleo/b.c") + PROJECT = ("src/Drivers/BSP/project.c", "src/Drivers/STM32Custom/project.c", "src/real.c") + MARKER = "int a;\n=======\nint b;\n" + def test_vendored_stm32cube_drivers_are_excluded_but_project_code_is_not(self): - hal = "boards/stm32/X/Drivers/STM32H7xx_HAL_Driver/Src/hal.c" - cmsis = "boards/stm32/X/Drivers/CMSIS/Include/c.h" + hal, cmsis, _ = self.VENDORED real = "src/real.c" code, out = self.run_marker_step({ hal: "/*\n=======\n ST documentation rule\n*/\n", cmsis: "/*\n=======\n*/\n", - real: "int a;\n=======\nint b;\n", + real: self.MARKER, }) self.assertEqual(code, 1, out) self.assertIn(real, out) @@ -172,6 +177,26 @@ def test_vendored_stm32cube_drivers_are_excluded_but_project_code_is_not(self): self.assertNotIn(hal, out) self.assertNotIn(cmsis, out) + def test_exact_stm32cube_vendor_trees_are_not_reported(self): + for path in self.VENDORED: + with self.subTest(path=path): + code, out = self.run_marker_step({path: self.MARKER, "src/clean.c": "int a;\n"}) + self.assertEqual(code, 0, out) + self.assertNotIn(path, out) + + def test_project_code_is_reported_even_under_a_drivers_directory(self): + for path in self.PROJECT: + with self.subTest(path=path): + code, out = self.run_marker_step({path: self.MARKER}) + self.assertEqual(code, 1, out) + self.assertIn(path, out) + self.assertIn("Unresolved merge-conflict marker detected", out) + + def test_repository_without_markers_passes(self): + code, out = self.run_marker_step({path: "int a;\n" for path in self.VENDORED + self.PROJECT}) + self.assertEqual(code, 0, out) + self.assertNotIn("Unresolved merge-conflict marker detected", out) + if __name__ == "__main__": unittest.main()