diff --git a/.github/scripts/fork_sync_digest.py b/.github/scripts/fork_sync_digest.py index 97442b9..63e769f 100644 --- a/.github/scripts/fork_sync_digest.py +++ b/.github/scripts/fork_sync_digest.py @@ -339,6 +339,9 @@ def git(*args, **kw): if behind == 0: return row(200, "already-current", 0, "up to date with base") + # The exact remote tip we just fetched — used as an explicit lease below. + remote_sha = git("rev-parse", f"origin/{branch}").stdout.decode().strip() + # -f: discard the prior iteration's rebased worktree state cleanly. git("checkout", "--detach", "-f", f"origin/{branch}", check=True) git("clean", "-fdq") @@ -360,9 +363,15 @@ def git(*args, **kw): return row(409, "conflict", behind, "conflict in: " + ", ".join(files[:8])) # PAT-authed push ⇒ re-triggers the fork's tests/pre-commit (a GITHUB_TOKEN - # push would not). --force-with-lease: the clone fetched origin/ - # moments ago, so this guards against a racing push in the same window. - pushed = git("push", "--force-with-lease", "origin", f"HEAD:refs/heads/{branch}") + # push would not). Explicit lease (:) rather than the bare form: + # a tracking ref set by an explicit-refspec fetch makes bare + # --force-with-lease report "stale info" and reject. Pinning the lease to + # the sha we fetched keeps the race protection (rejects if someone pushed + # in the seconds since) without the false stale. + pushed = git( + "push", f"--force-with-lease=refs/heads/{branch}:{remote_sha}", + "origin", f"HEAD:refs/heads/{branch}", + ) if pushed.returncode != 0: return row(500, "error", behind, f"push failed: {pushed.stderr.decode()[:200]}") return row(200, "rebased", behind, f"rebased onto {base_branch} (+{behind})") diff --git a/.github/scripts/tests/test_rebase_feature_branches.py b/.github/scripts/tests/test_rebase_feature_branches.py index c7d710c..7f2e70f 100644 --- a/.github/scripts/tests/test_rebase_feature_branches.py +++ b/.github/scripts/tests/test_rebase_feature_branches.py @@ -71,6 +71,8 @@ def fake_run(cmd, cwd=None, capture_output=False, env=None, check=False, **k): args = [c for c in cmd if c != "git"] if "rev-list" in args: return _Fake(0, out=b"2\n") + if "rev-parse" in args: + return _Fake(0, out=b"deadbeefcafe\n") if "merge-base" in args: return _Fake(0, out=b"abc123\n") return _Fake(0) @@ -83,3 +85,10 @@ def fake_run(cmd, cwd=None, capture_output=False, env=None, check=False, **k): "refs/heads/19.0-mig-foo:refs/remotes/origin/19.0-mig-foo" in c for c in fetch_cmds ), f"fetch must use a full refspec, got: {fetch_cmds}" + # Push must use an explicit lease (:) — the bare form trips + # "stale info" against a refspec-fetched tracking ref. + push_cmds = [c for c in calls if "push" in c] + assert push_cmds and any( + "--force-with-lease=refs/heads/19.0-mig-foo:deadbeefcafe" in c + for c in push_cmds + ), f"push must use an explicit lease, got: {push_cmds}"