mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
refactor: dedupe prep branch push flow
This commit is contained in:
377
scripts/pr
377
scripts/pr
@@ -423,6 +423,163 @@ resolve_head_push_url_https() {
|
||||
return 1
|
||||
}
|
||||
|
||||
verify_pr_head_branch_matches_expected() {
|
||||
local pr="$1"
|
||||
local expected_head="$2"
|
||||
|
||||
local current_head
|
||||
current_head=$(gh pr view "$pr" --json headRefName --jq .headRefName)
|
||||
if [ "$current_head" != "$expected_head" ]; then
|
||||
echo "PR head branch changed from $expected_head to $current_head. Re-run prepare-init."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
setup_prhead_remote() {
|
||||
local push_url
|
||||
push_url=$(resolve_head_push_url) || {
|
||||
echo "Unable to resolve PR head repo push URL."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Always set prhead to the correct fork URL for this PR.
|
||||
# The remote is repo-level (shared across worktrees), so a previous
|
||||
# prepare-pr run for a different fork PR can leave a stale URL.
|
||||
git remote remove prhead 2>/dev/null || true
|
||||
git remote add prhead "$push_url"
|
||||
}
|
||||
|
||||
resolve_prhead_remote_sha() {
|
||||
local pr_head="$1"
|
||||
|
||||
local remote_sha
|
||||
remote_sha=$(git ls-remote prhead "refs/heads/$pr_head" 2>/dev/null | awk '{print $1}' || true)
|
||||
if [ -z "$remote_sha" ]; then
|
||||
local https_url
|
||||
https_url=$(resolve_head_push_url_https 2>/dev/null) || true
|
||||
local current_push_url
|
||||
current_push_url=$(git remote get-url prhead 2>/dev/null || true)
|
||||
if [ -n "$https_url" ] && [ "$https_url" != "$current_push_url" ]; then
|
||||
echo "SSH remote failed; falling back to HTTPS..."
|
||||
git remote set-url prhead "$https_url"
|
||||
git remote set-url --push prhead "$https_url"
|
||||
remote_sha=$(git ls-remote prhead "refs/heads/$pr_head" 2>/dev/null | awk '{print $1}' || true)
|
||||
fi
|
||||
if [ -z "$remote_sha" ]; then
|
||||
echo "Remote branch refs/heads/$pr_head not found on prhead"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
printf '%s\n' "$remote_sha"
|
||||
}
|
||||
|
||||
run_prepare_push_retry_gates() {
|
||||
local docs_only="${1:-false}"
|
||||
|
||||
bootstrap_deps_if_needed
|
||||
run_quiet_logged "pnpm build (lease-retry)" ".local/lease-retry-build.log" pnpm build
|
||||
run_quiet_logged "pnpm check (lease-retry)" ".local/lease-retry-check.log" pnpm check
|
||||
if [ "$docs_only" != "true" ]; then
|
||||
run_quiet_logged "pnpm test (lease-retry)" ".local/lease-retry-test.log" pnpm test
|
||||
fi
|
||||
}
|
||||
|
||||
PUSH_PREP_HEAD_SHA=""
|
||||
PUSHED_FROM_SHA=""
|
||||
PR_HEAD_SHA_AFTER_PUSH=""
|
||||
|
||||
push_prep_head_to_pr_branch() {
|
||||
local pr="$1"
|
||||
local pr_head="$2"
|
||||
local prep_head_sha="$3"
|
||||
local lease_sha="$4"
|
||||
local rerun_gates_on_lease_retry="${5:-false}"
|
||||
local docs_only="${6:-false}"
|
||||
|
||||
setup_prhead_remote
|
||||
|
||||
local remote_sha
|
||||
remote_sha=$(resolve_prhead_remote_sha "$pr_head")
|
||||
|
||||
local pushed_from_sha="$remote_sha"
|
||||
if [ "$remote_sha" = "$prep_head_sha" ]; then
|
||||
echo "Remote branch already at local prep HEAD; skipping push."
|
||||
else
|
||||
if [ "$remote_sha" != "$lease_sha" ]; then
|
||||
echo "Remote SHA $remote_sha differs from PR head SHA $lease_sha. Refreshing lease SHA from remote."
|
||||
lease_sha="$remote_sha"
|
||||
fi
|
||||
pushed_from_sha="$lease_sha"
|
||||
local push_output
|
||||
if ! push_output=$(
|
||||
git push --force-with-lease=refs/heads/$pr_head:$lease_sha prhead HEAD:$pr_head 2>&1
|
||||
); then
|
||||
echo "Push failed: $push_output"
|
||||
|
||||
if printf '%s' "$push_output" | grep -qiE '(permission|denied|403|forbidden)'; then
|
||||
echo "Permission denied on git push; trying GraphQL createCommitOnBranch fallback..."
|
||||
if [ -n "${PR_HEAD_OWNER:-}" ] && [ -n "${PR_HEAD_REPO_NAME:-}" ]; then
|
||||
local graphql_oid
|
||||
graphql_oid=$(graphql_push_to_fork "${PR_HEAD_OWNER}/${PR_HEAD_REPO_NAME}" "$pr_head" "$lease_sha")
|
||||
prep_head_sha="$graphql_oid"
|
||||
else
|
||||
echo "Git push permission denied and no fork owner/repo info for GraphQL fallback."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Lease push failed, retrying once with fresh PR head..."
|
||||
lease_sha=$(gh pr view "$pr" --json headRefOid --jq .headRefOid)
|
||||
pushed_from_sha="$lease_sha"
|
||||
|
||||
if [ "$rerun_gates_on_lease_retry" = "true" ]; then
|
||||
git fetch origin "pull/$pr/head:pr-$pr-latest" --force
|
||||
git rebase "pr-$pr-latest"
|
||||
prep_head_sha=$(git rev-parse HEAD)
|
||||
run_prepare_push_retry_gates "$docs_only"
|
||||
fi
|
||||
|
||||
if ! push_output=$(
|
||||
git push --force-with-lease=refs/heads/$pr_head:$lease_sha prhead HEAD:$pr_head 2>&1
|
||||
); then
|
||||
echo "Retry push failed: $push_output"
|
||||
if [ -n "${PR_HEAD_OWNER:-}" ] && [ -n "${PR_HEAD_REPO_NAME:-}" ]; then
|
||||
echo "Retry failed; trying GraphQL createCommitOnBranch fallback..."
|
||||
local graphql_oid
|
||||
graphql_oid=$(graphql_push_to_fork "${PR_HEAD_OWNER}/${PR_HEAD_REPO_NAME}" "$pr_head" "$lease_sha")
|
||||
prep_head_sha="$graphql_oid"
|
||||
else
|
||||
echo "Git push failed and no fork owner/repo info for GraphQL fallback."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! wait_for_pr_head_sha "$pr" "$prep_head_sha" 8 3; then
|
||||
local observed_sha
|
||||
observed_sha=$(gh pr view "$pr" --json headRefOid --jq .headRefOid)
|
||||
echo "Pushed head SHA propagation timed out. expected=$prep_head_sha observed=$observed_sha"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local pr_head_sha_after
|
||||
pr_head_sha_after=$(gh pr view "$pr" --json headRefOid --jq .headRefOid)
|
||||
|
||||
git fetch origin main
|
||||
git fetch origin "pull/$pr/head:pr-$pr-verify" --force
|
||||
git merge-base --is-ancestor origin/main "pr-$pr-verify" || {
|
||||
echo "PR branch is behind main after push."
|
||||
exit 1
|
||||
}
|
||||
git branch -D "pr-$pr-verify" 2>/dev/null || true
|
||||
|
||||
PUSH_PREP_HEAD_SHA="$prep_head_sha"
|
||||
PUSHED_FROM_SHA="$pushed_from_sha"
|
||||
PR_HEAD_SHA_AFTER_PUSH="$pr_head_sha_after"
|
||||
}
|
||||
|
||||
set_review_mode() {
|
||||
local mode="$1"
|
||||
cat > .local/review-mode.env <<EOF_ENV
|
||||
@@ -1299,121 +1456,14 @@ prepare_push() {
|
||||
local prep_head_sha
|
||||
prep_head_sha=$(git rev-parse HEAD)
|
||||
|
||||
local current_head
|
||||
current_head=$(gh pr view "$pr" --json headRefName --jq .headRefName)
|
||||
local lease_sha
|
||||
lease_sha=$(gh pr view "$pr" --json headRefOid --jq .headRefOid)
|
||||
|
||||
if [ "$current_head" != "$PR_HEAD" ]; then
|
||||
echo "PR head branch changed from $PR_HEAD to $current_head. Re-run prepare-init."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local push_url
|
||||
push_url=$(resolve_head_push_url) || {
|
||||
echo "Unable to resolve PR head repo push URL."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Always set prhead to the correct fork URL for this PR.
|
||||
# The remote is repo-level (shared across worktrees), so a previous
|
||||
# prepare-pr run for a different fork PR can leave a stale URL.
|
||||
git remote remove prhead 2>/dev/null || true
|
||||
git remote add prhead "$push_url"
|
||||
|
||||
local remote_sha
|
||||
remote_sha=$(git ls-remote prhead "refs/heads/$PR_HEAD" 2>/dev/null | awk '{print $1}' || true)
|
||||
if [ -z "$remote_sha" ]; then
|
||||
local https_url
|
||||
https_url=$(resolve_head_push_url_https 2>/dev/null) || true
|
||||
if [ -n "$https_url" ] && [ "$https_url" != "$push_url" ]; then
|
||||
echo "SSH remote failed; falling back to HTTPS..."
|
||||
git remote set-url prhead "$https_url"
|
||||
git remote set-url --push prhead "$https_url"
|
||||
push_url="$https_url"
|
||||
remote_sha=$(git ls-remote prhead "refs/heads/$PR_HEAD" 2>/dev/null | awk '{print $1}' || true)
|
||||
fi
|
||||
if [ -z "$remote_sha" ]; then
|
||||
echo "Remote branch refs/heads/$PR_HEAD not found on prhead"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
local pushed_from_sha="$remote_sha"
|
||||
if [ "$remote_sha" = "$prep_head_sha" ]; then
|
||||
echo "Remote branch already at local prep HEAD; skipping push."
|
||||
else
|
||||
if [ "$remote_sha" != "$lease_sha" ]; then
|
||||
echo "Remote SHA $remote_sha differs from PR head SHA $lease_sha. Refreshing lease SHA from remote."
|
||||
lease_sha="$remote_sha"
|
||||
fi
|
||||
pushed_from_sha="$lease_sha"
|
||||
local push_output
|
||||
if ! push_output=$(git push --force-with-lease=refs/heads/$PR_HEAD:$lease_sha prhead HEAD:$PR_HEAD 2>&1); then
|
||||
echo "Push failed: $push_output"
|
||||
|
||||
# Check if this is a permission error (fork PR) vs a lease conflict.
|
||||
# Permission errors go straight to GraphQL; lease conflicts retry with rebase.
|
||||
if printf '%s' "$push_output" | grep -qiE '(permission|denied|403|forbidden)'; then
|
||||
echo "Permission denied on git push; trying GraphQL createCommitOnBranch fallback..."
|
||||
if [ -n "${PR_HEAD_OWNER:-}" ] && [ -n "${PR_HEAD_REPO_NAME:-}" ]; then
|
||||
local graphql_oid
|
||||
graphql_oid=$(graphql_push_to_fork "${PR_HEAD_OWNER}/${PR_HEAD_REPO_NAME}" "$PR_HEAD" "$lease_sha")
|
||||
prep_head_sha="$graphql_oid"
|
||||
else
|
||||
echo "Git push permission denied and no fork owner/repo info for GraphQL fallback."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Lease push failed, retrying once with fresh PR head..."
|
||||
|
||||
lease_sha=$(gh pr view "$pr" --json headRefOid --jq .headRefOid)
|
||||
pushed_from_sha="$lease_sha"
|
||||
|
||||
git fetch origin "pull/$pr/head:pr-$pr-latest" --force
|
||||
git rebase "pr-$pr-latest"
|
||||
prep_head_sha=$(git rev-parse HEAD)
|
||||
|
||||
bootstrap_deps_if_needed
|
||||
run_quiet_logged "pnpm build (lease-retry)" ".local/lease-retry-build.log" pnpm build
|
||||
run_quiet_logged "pnpm check (lease-retry)" ".local/lease-retry-check.log" pnpm check
|
||||
if [ "${DOCS_ONLY:-false}" != "true" ]; then
|
||||
run_quiet_logged "pnpm test (lease-retry)" ".local/lease-retry-test.log" pnpm test
|
||||
fi
|
||||
|
||||
if ! git push --force-with-lease=refs/heads/$PR_HEAD:$lease_sha prhead HEAD:$PR_HEAD; then
|
||||
# Retry also failed — try GraphQL as last resort.
|
||||
if [ -n "${PR_HEAD_OWNER:-}" ] && [ -n "${PR_HEAD_REPO_NAME:-}" ]; then
|
||||
echo "Git push retry failed; trying GraphQL createCommitOnBranch fallback..."
|
||||
local graphql_oid
|
||||
graphql_oid=$(graphql_push_to_fork "${PR_HEAD_OWNER}/${PR_HEAD_REPO_NAME}" "$PR_HEAD" "$lease_sha")
|
||||
prep_head_sha="$graphql_oid"
|
||||
else
|
||||
echo "Git push failed and no fork owner/repo info for GraphQL fallback."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! wait_for_pr_head_sha "$pr" "$prep_head_sha" 8 3; then
|
||||
local observed_sha
|
||||
observed_sha=$(gh pr view "$pr" --json headRefOid --jq .headRefOid)
|
||||
echo "Pushed head SHA propagation timed out. expected=$prep_head_sha observed=$observed_sha"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local pr_head_sha_after
|
||||
pr_head_sha_after=$(gh pr view "$pr" --json headRefOid --jq .headRefOid)
|
||||
|
||||
git fetch origin main
|
||||
git fetch origin "pull/$pr/head:pr-$pr-verify" --force
|
||||
git merge-base --is-ancestor origin/main "pr-$pr-verify" || {
|
||||
echo "PR branch is behind main after push."
|
||||
exit 1
|
||||
}
|
||||
git branch -D "pr-$pr-verify" 2>/dev/null || true
|
||||
verify_pr_head_branch_matches_expected "$pr" "$PR_HEAD"
|
||||
push_prep_head_to_pr_branch "$pr" "$PR_HEAD" "$prep_head_sha" "$lease_sha" true "${DOCS_ONLY:-false}"
|
||||
prep_head_sha="$PUSH_PREP_HEAD_SHA"
|
||||
local pushed_from_sha="$PUSHED_FROM_SHA"
|
||||
local pr_head_sha_after="$PR_HEAD_SHA_AFTER_PUSH"
|
||||
|
||||
local contrib="${PR_AUTHOR:-}"
|
||||
if [ -z "$contrib" ]; then
|
||||
@@ -1464,107 +1514,14 @@ prepare_sync_head() {
|
||||
local prep_head_sha
|
||||
prep_head_sha=$(git rev-parse HEAD)
|
||||
|
||||
local current_head
|
||||
current_head=$(gh pr view "$pr" --json headRefName --jq .headRefName)
|
||||
local lease_sha
|
||||
lease_sha=$(gh pr view "$pr" --json headRefOid --jq .headRefOid)
|
||||
|
||||
if [ "$current_head" != "$PR_HEAD" ]; then
|
||||
echo "PR head branch changed from $PR_HEAD to $current_head. Re-run prepare-init."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local push_url
|
||||
push_url=$(resolve_head_push_url) || {
|
||||
echo "Unable to resolve PR head repo push URL."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Always set prhead to the correct fork URL for this PR.
|
||||
# The remote is repo-level (shared across worktrees), so a previous
|
||||
# run for a different fork PR can leave a stale URL.
|
||||
git remote remove prhead 2>/dev/null || true
|
||||
git remote add prhead "$push_url"
|
||||
|
||||
local remote_sha
|
||||
remote_sha=$(git ls-remote prhead "refs/heads/$PR_HEAD" 2>/dev/null | awk '{print $1}' || true)
|
||||
if [ -z "$remote_sha" ]; then
|
||||
local https_url
|
||||
https_url=$(resolve_head_push_url_https 2>/dev/null) || true
|
||||
if [ -n "$https_url" ] && [ "$https_url" != "$push_url" ]; then
|
||||
echo "SSH remote failed; falling back to HTTPS..."
|
||||
git remote set-url prhead "$https_url"
|
||||
git remote set-url --push prhead "$https_url"
|
||||
push_url="$https_url"
|
||||
remote_sha=$(git ls-remote prhead "refs/heads/$PR_HEAD" 2>/dev/null | awk '{print $1}' || true)
|
||||
fi
|
||||
if [ -z "$remote_sha" ]; then
|
||||
echo "Remote branch refs/heads/$PR_HEAD not found on prhead"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
local pushed_from_sha="$remote_sha"
|
||||
if [ "$remote_sha" = "$prep_head_sha" ]; then
|
||||
echo "Remote branch already at local prep HEAD; skipping push."
|
||||
else
|
||||
if [ "$remote_sha" != "$lease_sha" ]; then
|
||||
echo "Remote SHA $remote_sha differs from PR head SHA $lease_sha. Refreshing lease SHA from remote."
|
||||
lease_sha="$remote_sha"
|
||||
fi
|
||||
pushed_from_sha="$lease_sha"
|
||||
local push_output
|
||||
if ! push_output=$(git push --force-with-lease=refs/heads/$PR_HEAD:$lease_sha prhead HEAD:$PR_HEAD 2>&1); then
|
||||
echo "Push failed: $push_output"
|
||||
|
||||
if printf '%s' "$push_output" | grep -qiE '(permission|denied|403|forbidden)'; then
|
||||
echo "Permission denied on git push; trying GraphQL createCommitOnBranch fallback..."
|
||||
if [ -n "${PR_HEAD_OWNER:-}" ] && [ -n "${PR_HEAD_REPO_NAME:-}" ]; then
|
||||
local graphql_oid
|
||||
graphql_oid=$(graphql_push_to_fork "${PR_HEAD_OWNER}/${PR_HEAD_REPO_NAME}" "$PR_HEAD" "$lease_sha")
|
||||
prep_head_sha="$graphql_oid"
|
||||
else
|
||||
echo "Git push permission denied and no fork owner/repo info for GraphQL fallback."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Lease push failed, retrying once with fresh PR head lease..."
|
||||
lease_sha=$(gh pr view "$pr" --json headRefOid --jq .headRefOid)
|
||||
pushed_from_sha="$lease_sha"
|
||||
|
||||
if ! push_output=$(git push --force-with-lease=refs/heads/$PR_HEAD:$lease_sha prhead HEAD:$PR_HEAD 2>&1); then
|
||||
echo "Retry push failed: $push_output"
|
||||
if [ -n "${PR_HEAD_OWNER:-}" ] && [ -n "${PR_HEAD_REPO_NAME:-}" ]; then
|
||||
echo "Retry failed; trying GraphQL createCommitOnBranch fallback..."
|
||||
local graphql_oid
|
||||
graphql_oid=$(graphql_push_to_fork "${PR_HEAD_OWNER}/${PR_HEAD_REPO_NAME}" "$PR_HEAD" "$lease_sha")
|
||||
prep_head_sha="$graphql_oid"
|
||||
else
|
||||
echo "Git push failed and no fork owner/repo info for GraphQL fallback."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! wait_for_pr_head_sha "$pr" "$prep_head_sha" 8 3; then
|
||||
local observed_sha
|
||||
observed_sha=$(gh pr view "$pr" --json headRefOid --jq .headRefOid)
|
||||
echo "Pushed head SHA propagation timed out. expected=$prep_head_sha observed=$observed_sha"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local pr_head_sha_after
|
||||
pr_head_sha_after=$(gh pr view "$pr" --json headRefOid --jq .headRefOid)
|
||||
|
||||
git fetch origin main
|
||||
git fetch origin "pull/$pr/head:pr-$pr-verify" --force
|
||||
git merge-base --is-ancestor origin/main "pr-$pr-verify" || {
|
||||
echo "PR branch is behind main after push."
|
||||
exit 1
|
||||
}
|
||||
git branch -D "pr-$pr-verify" 2>/dev/null || true
|
||||
verify_pr_head_branch_matches_expected "$pr" "$PR_HEAD"
|
||||
push_prep_head_to_pr_branch "$pr" "$PR_HEAD" "$prep_head_sha" "$lease_sha"
|
||||
prep_head_sha="$PUSH_PREP_HEAD_SHA"
|
||||
local pushed_from_sha="$PUSHED_FROM_SHA"
|
||||
local pr_head_sha_after="$PR_HEAD_SHA_AFTER_PUSH"
|
||||
|
||||
local contrib="${PR_AUTHOR:-}"
|
||||
if [ -z "$contrib" ]; then
|
||||
|
||||
Reference in New Issue
Block a user