Skip to content

Commit dc6ca05

Browse files
committed
fix(ci): validate the batch checkpoint as the upstream merge parent
1 parent 330a45d commit dc6ca05

3 files changed

Lines changed: 21 additions & 8 deletions

File tree

‎docs/upstream-sync/ci-policy.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ documents, and tags.
4141

4242
`scripts/ci/check-sync-ancestry.py --checkpoint SHA --target SHA --merge SHA`
4343
requires full commit IDs, resolves each exact object, proves checkpoint is an
44-
ancestor of the pinned upstream target, and proves target is in a non-first
45-
parent's history for the integration merge. Git replacement refs are disabled
44+
ancestor of the pinned upstream target, and proves the checkpoint is an exact non-first parent of the integration
45+
merge. Early batches do not need to contain the final campaign target. Git replacement refs are disabled
4646
for these graph checks, so a synthetic replacement cannot manufacture the
4747
ancestry claim. The helper is local/manual and does not run hosted Actions.
4848
Jenkins runs its self-test, which constructs true and replacement-ref-faked
@@ -58,7 +58,7 @@ python3 scripts/ci/check-github-actions-policy.py
5858
python3 scripts/ci/test_github_actions_policy.py
5959
Ran 4 tests; OK (real workflows plus positive/negative fixtures)
6060
python3 scripts/ci/test_sync_ancestry.py
61-
Ran 3 tests; OK (true ancestry, replacement-ref spoof, invalid inputs)
61+
Ran 5 tests; OK (early checkpoint, exact parent, replacement-ref spoof, invalid inputs)
6262
git diff --check
6363
passed
6464
```

‎scripts/ci/check-sync-ancestry.py‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,8 @@ def verify(checkpoint: str, target: str, merge: str) -> None:
3131
parents = git("rev-list", "--parents", "-n", "1", merge).split()[1:]
3232
if len(parents) < 2:
3333
raise ValueError("result is not a merge commit")
34-
if not any(subprocess.run(["git", "merge-base", "--is-ancestor", target, parent],
35-
env=os.environ | {"GIT_NO_REPLACE_OBJECTS": "1"},
36-
check=False).returncode == 0 for parent in parents[1:]):
37-
raise ValueError("pinned target is not in a non-first merge-parent history")
34+
if checkpoint not in parents[1:]:
35+
raise ValueError("checkpoint is not an exact non-first merge parent")
3836

3937

4038
def main() -> int:

‎scripts/ci/test_sync_ancestry.py‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,22 @@ def git(self, *args):
4646
text=True, stderr=subprocess.PIPE).strip()
4747

4848
def test_accepts_real_checkpoint_and_second_parent(self):
49-
ancestry.verify(self.base, self.target, self.merge)
49+
ancestry.verify(self.target, self.target, self.merge)
50+
51+
def test_accepts_early_checkpoint_before_campaign_target(self):
52+
self.git("checkout", "-q", "side")
53+
self.git("commit", "--allow-empty", "-qm", "later campaign target")
54+
campaign_target = self.git("rev-parse", "HEAD")
55+
ancestry.verify(self.target, campaign_target, self.merge)
56+
57+
def test_rejects_first_parent_or_wrong_checkpoint(self):
58+
with self.assertRaises(ValueError):
59+
ancestry.verify(self.base, self.target, self.merge)
60+
self.git("checkout", "-q", "side")
61+
self.git("commit", "--allow-empty", "-qm", "unmerged checkpoint")
62+
unmerged = self.git("rev-parse", "HEAD")
63+
with self.assertRaises(ValueError):
64+
ancestry.verify(unmerged, unmerged, self.merge)
5065

5166
def test_rejects_ancestor_claim_using_replacement_ref(self):
5267
self.git("checkout", "--orphan", "unrelated")

0 commit comments

Comments
 (0)