mirror of https://github.com/openclaw/openclaw.git
fix(scripts/pr): make cleanup worktree-safe
This commit is contained in:
parent
27b9665871
commit
8dbba7d17c
|
|
@ -185,3 +185,151 @@ merge_author_email_candidates() {
|
|||
"${reviewer_id}+${reviewer}@users.noreply.github.com" \
|
||||
"${reviewer}@users.noreply.github.com" | awk 'NF && !seen[$0]++'
|
||||
}
|
||||
|
||||
common_repo_root() {
|
||||
if command -v repo_root >/dev/null 2>&1; then
|
||||
repo_root
|
||||
return
|
||||
fi
|
||||
|
||||
local base_dir
|
||||
base_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
git -C "$base_dir" rev-parse --show-toplevel
|
||||
}
|
||||
|
||||
worktree_path_for_branch() {
|
||||
local branch="$1"
|
||||
local ref="refs/heads/$branch"
|
||||
|
||||
git worktree list --porcelain | awk -v ref="$ref" '
|
||||
/^worktree / {
|
||||
worktree=$2
|
||||
next
|
||||
}
|
||||
/^branch / {
|
||||
if ($2 == ref) {
|
||||
print worktree
|
||||
found=1
|
||||
}
|
||||
}
|
||||
END {
|
||||
if (!found) {
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
worktree_is_registered() {
|
||||
local path="$1"
|
||||
git worktree list --porcelain | awk -v target="$path" '
|
||||
/^worktree / {
|
||||
if ($2 == target) {
|
||||
found=1
|
||||
}
|
||||
}
|
||||
END {
|
||||
exit found ? 0 : 1
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
resolve_existing_dir_path() {
|
||||
local path="$1"
|
||||
if [ ! -d "$path" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
(
|
||||
cd "$path" >/dev/null 2>&1 &&
|
||||
pwd -P
|
||||
)
|
||||
}
|
||||
|
||||
is_repo_pr_worktree_dir() {
|
||||
local path="$1"
|
||||
local root
|
||||
root=$(common_repo_root)
|
||||
|
||||
local worktrees_dir="$root/.worktrees"
|
||||
local resolved_path
|
||||
resolved_path=$(resolve_existing_dir_path "$path" 2>/dev/null || true)
|
||||
if [ -z "$resolved_path" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local resolved_worktrees_dir
|
||||
resolved_worktrees_dir=$(resolve_existing_dir_path "$worktrees_dir" 2>/dev/null || true)
|
||||
if [ -z "$resolved_worktrees_dir" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
case "$resolved_path" in
|
||||
"$resolved_worktrees_dir"/pr-*)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
remove_worktree_if_present() {
|
||||
local path="$1"
|
||||
if [ ! -e "$path" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if worktree_is_registered "$path"; then
|
||||
git worktree remove "$path" --force >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
if [ ! -e "$path" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if worktree_is_registered "$path"; then
|
||||
echo "Warning: failed to remove registered worktree $path"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! is_repo_pr_worktree_dir "$path"; then
|
||||
echo "Warning: refusing to trash non-PR-worktree path $path"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if command -v trash >/dev/null 2>&1; then
|
||||
trash "$path" >/dev/null 2>&1 || {
|
||||
echo "Warning: failed to trash orphaned worktree dir $path"
|
||||
return 0
|
||||
}
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Warning: orphaned worktree dir remains and trash is unavailable: $path"
|
||||
return 0
|
||||
}
|
||||
|
||||
delete_local_branch_if_safe() {
|
||||
local branch="$1"
|
||||
local ref="refs/heads/$branch"
|
||||
|
||||
if ! git show-ref --verify --quiet "$ref"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local branch_worktree=""
|
||||
branch_worktree=$(worktree_path_for_branch "$branch" 2>/dev/null || true)
|
||||
if [ -n "$branch_worktree" ]; then
|
||||
echo "Skipping local branch delete for $branch; checked out in worktree $branch_worktree"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if git branch -D "$branch" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
if git update-ref -d "$ref" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Warning: failed to delete local branch $branch"
|
||||
return 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -227,13 +227,41 @@ Co-authored-by: $reviewer <$reviewer_coauthor_email>
|
|||
Reviewed-by: @$reviewer
|
||||
EOF_BODY
|
||||
|
||||
delete_remote_pr_head_branch_after_merge() {
|
||||
local head_json
|
||||
head_json=$(gh pr view "$pr" --json headRefName,headRepository,headRepositoryOwner,isCrossRepository,maintainerCanModify)
|
||||
|
||||
local head_ref
|
||||
head_ref=$(printf '%s\n' "$head_json" | jq -r '.headRefName // ""')
|
||||
if [ -z "$head_ref" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local repo_owner
|
||||
repo_owner=$(printf '%s\n' "$head_json" | jq -r '.headRepositoryOwner.login // ""')
|
||||
local repo_name
|
||||
repo_name=$(printf '%s\n' "$head_json" | jq -r '.headRepository.name // ""')
|
||||
if [ -z "$repo_owner" ] || [ -z "$repo_name" ]; then
|
||||
echo "Warning: unable to resolve head repository for remote branch cleanup"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local encoded_ref
|
||||
encoded_ref=$(jq -rn --arg value "heads/$head_ref" '$value|@uri')
|
||||
if gh api -X DELETE "repos/$repo_owner/$repo_name/git/refs/$encoded_ref" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Warning: failed to delete remote branch $repo_owner/$repo_name:$head_ref"
|
||||
return 0
|
||||
}
|
||||
|
||||
run_merge_with_email() {
|
||||
local email="$1"
|
||||
local merge_output_file
|
||||
merge_output_file=$(mktemp)
|
||||
if gh pr merge "$pr" \
|
||||
--squash \
|
||||
--delete-branch \
|
||||
--match-head-commit "$PREP_HEAD_SHA" \
|
||||
--author-email "$email" \
|
||||
--subject "$pr_title (#$pr_number)" \
|
||||
|
|
@ -351,10 +379,11 @@ EOF_COMMENT
|
|||
local root
|
||||
root=$(repo_root)
|
||||
cd "$root"
|
||||
git worktree remove ".worktrees/pr-$pr" --force
|
||||
git branch -D "temp/pr-$pr" 2>/dev/null || true
|
||||
git branch -D "pr-$pr" 2>/dev/null || true
|
||||
git branch -D "pr-$pr-prep" 2>/dev/null || true
|
||||
delete_remote_pr_head_branch_after_merge
|
||||
remove_worktree_if_present ".worktrees/pr-$pr"
|
||||
delete_local_branch_if_safe "temp/pr-$pr"
|
||||
delete_local_branch_if_safe "pr-$pr"
|
||||
delete_local_branch_if_safe "pr-$pr-prep"
|
||||
|
||||
local pr_url
|
||||
pr_url=$(gh pr view "$pr" --json url --jq .url)
|
||||
|
|
|
|||
|
|
@ -131,10 +131,10 @@ gc_pr_worktrees() {
|
|||
if [ "$dry_run" = "true" ]; then
|
||||
echo "would remove $dir (PR #$pr state=$state)"
|
||||
else
|
||||
git worktree remove "$dir" --force
|
||||
git branch -D "temp/pr-$pr" 2>/dev/null || true
|
||||
git branch -D "pr-$pr" 2>/dev/null || true
|
||||
git branch -D "pr-$pr-prep" 2>/dev/null || true
|
||||
remove_worktree_if_present "$dir"
|
||||
delete_local_branch_if_safe "temp/pr-$pr"
|
||||
delete_local_branch_if_safe "pr-$pr"
|
||||
delete_local_branch_if_safe "pr-$pr-prep"
|
||||
echo "removed $dir (PR #$pr state=$state)"
|
||||
fi
|
||||
removed=$((removed + 1))
|
||||
|
|
|
|||
Loading…
Reference in New Issue