mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-15 19:54:54 +00:00
fix(ci): clean review worktrees after cancellation (#8474)
* fix(ci): clean review worktrees after cancellation * fix(ci): remove orphaned review worktree directories * fix(tests): sync qwen-resolve-workflow expectations with externalized review timeouts (#8474) * fix(ci): pin review worktree cleanup patterns to paths.ts (#8474) * fix(ci): harden review cleanup sweeps and cover integration_cli (#8474) * fix(ci): extend review cleanup sweep to web_shell_e2e_smoke (#8474) * fix(ci): harden review cleanup git calls * fix(ci): tighten review cleanup comments and test guards (#8474) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(ci): pin review cleanup recipe copies byte-identical (#8474) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(ci): guard review worktree removal and pin cleanup invariants (#8474) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
2601d815dd
commit
e34780e24d
5 changed files with 448 additions and 7 deletions
157
.github/workflows/ci.yml
vendored
157
.github/workflows/ci.yml
vendored
|
|
@ -181,6 +181,51 @@ jobs:
|
|||
chmod -R u+w "$GITHUB_WORKSPACE/.qwen" 2>/dev/null || true
|
||||
rm -rf "$GITHUB_WORKSPACE/.qwen" 2>/dev/null || sudo -n rm -rf "$GITHUB_WORKSPACE/.qwen" 2>/dev/null || echo "::warning::leaked .qwen; runner needs manual cleanup"
|
||||
fi
|
||||
# Interrupted reviews leave worktree registrations under .qwen/tmp/
|
||||
# and qwen-review/* branches behind. prune drops registrations whose
|
||||
# directories the rm above removed; worktree remove --force then
|
||||
# clears any still-registered leftover directory (--force tolerates
|
||||
# dirty contents), since a branch checked out in a live worktree
|
||||
# cannot be deleted. If removal still fails, the registration
|
||||
# survives and the branch delete below warns. The sweep deletes all
|
||||
# review artifacts, not just the current PR's: safe because a runner
|
||||
# executes one job at a time. Kept inline rather than a shared
|
||||
# script: this runs pre-checkout on shared runners, where leftover
|
||||
# workspace files are untrusted.
|
||||
if [ -e "$GITHUB_WORKSPACE/.git" ]; then
|
||||
GIT_SAFE=(git -c core.hooksPath=/dev/null -c core.fsmonitor= -C "$GITHUB_WORKSPACE")
|
||||
"${GIT_SAFE[@]}" worktree prune -v || true
|
||||
"${GIT_SAFE[@]}" worktree list --porcelain \
|
||||
| awk '$1 == "worktree" && index($0, "/.qwen/tmp/review-pr-") > 0 { sub(/^worktree /, ""); print }' \
|
||||
| while read -r worktree; do
|
||||
[ -n "$worktree" ] || continue
|
||||
# Registered paths come from leftover git metadata and are
|
||||
# untrusted: the awk filter above matched by substring, so reject
|
||||
# `..` traversal and re-anchor to the review prefix before the
|
||||
# destructive remove.
|
||||
case "$worktree" in
|
||||
*/../*|../*|*/..)
|
||||
echo "::warning::skipping suspicious review worktree path: $worktree"
|
||||
continue
|
||||
;;
|
||||
"$GITHUB_WORKSPACE/.qwen/tmp/review-pr-"*) : ;;
|
||||
*)
|
||||
echo "::warning::skipping unexpected review worktree path: $worktree"
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
"${GIT_SAFE[@]}" worktree remove --force "$worktree" ||
|
||||
echo "::warning::could not remove review worktree: $worktree"
|
||||
done || true
|
||||
"${GIT_SAFE[@]}" worktree prune -v || true
|
||||
"${GIT_SAFE[@]}" for-each-ref --format='%(refname:short)' 'refs/heads/qwen-review/*' \
|
||||
| while read -r stale_ref; do
|
||||
if [ -n "$stale_ref" ]; then
|
||||
"${GIT_SAFE[@]}" branch -D "$stale_ref" ||
|
||||
echo "::warning::could not remove review branch: $stale_ref"
|
||||
fi
|
||||
done || true
|
||||
fi
|
||||
|
||||
# On PRs, check out refs/pull/N/head (the immutable PR head, published the
|
||||
# instant the branch is pushed) instead of github.ref. github.ref is the
|
||||
|
|
@ -523,6 +568,62 @@ jobs:
|
|||
fi
|
||||
chmod -R u+rwX "$GITHUB_WORKSPACE" 2>/dev/null || sudo -n chmod -R u+rwX "$GITHUB_WORKSPACE" || echo "::warning::could not restore workspace write permissions; checkout may fail on leftover read-only files"
|
||||
|
||||
# Same pre-checkout recovery as the test job: this job lands on the
|
||||
# same reused pool, so leftover review worktrees and branches from an
|
||||
# interrupted review would break this checkout too.
|
||||
- name: 'Clean stale .qwen before checkout'
|
||||
run: |-
|
||||
set -uo pipefail
|
||||
if [ -d "$GITHUB_WORKSPACE/.qwen" ] && [ ! -L "$GITHUB_WORKSPACE/.qwen" ]; then
|
||||
chmod -R u+w "$GITHUB_WORKSPACE/.qwen" 2>/dev/null || true
|
||||
rm -rf "$GITHUB_WORKSPACE/.qwen" 2>/dev/null || sudo -n rm -rf "$GITHUB_WORKSPACE/.qwen" 2>/dev/null || echo "::warning::leaked .qwen; runner needs manual cleanup"
|
||||
fi
|
||||
# Interrupted reviews leave worktree registrations under .qwen/tmp/
|
||||
# and qwen-review/* branches behind. prune drops registrations whose
|
||||
# directories the rm above removed; worktree remove --force then
|
||||
# clears any still-registered leftover directory (--force tolerates
|
||||
# dirty contents), since a branch checked out in a live worktree
|
||||
# cannot be deleted. If removal still fails, the registration
|
||||
# survives and the branch delete below warns. The sweep deletes all
|
||||
# review artifacts, not just the current PR's: safe because a runner
|
||||
# executes one job at a time. Kept inline rather than a shared
|
||||
# script: this runs pre-checkout on shared runners, where leftover
|
||||
# workspace files are untrusted.
|
||||
if [ -e "$GITHUB_WORKSPACE/.git" ]; then
|
||||
GIT_SAFE=(git -c core.hooksPath=/dev/null -c core.fsmonitor= -C "$GITHUB_WORKSPACE")
|
||||
"${GIT_SAFE[@]}" worktree prune -v || true
|
||||
"${GIT_SAFE[@]}" worktree list --porcelain \
|
||||
| awk '$1 == "worktree" && index($0, "/.qwen/tmp/review-pr-") > 0 { sub(/^worktree /, ""); print }' \
|
||||
| while read -r worktree; do
|
||||
[ -n "$worktree" ] || continue
|
||||
# Registered paths come from leftover git metadata and are
|
||||
# untrusted: the awk filter above matched by substring, so reject
|
||||
# `..` traversal and re-anchor to the review prefix before the
|
||||
# destructive remove.
|
||||
case "$worktree" in
|
||||
*/../*|../*|*/..)
|
||||
echo "::warning::skipping suspicious review worktree path: $worktree"
|
||||
continue
|
||||
;;
|
||||
"$GITHUB_WORKSPACE/.qwen/tmp/review-pr-"*) : ;;
|
||||
*)
|
||||
echo "::warning::skipping unexpected review worktree path: $worktree"
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
"${GIT_SAFE[@]}" worktree remove --force "$worktree" ||
|
||||
echo "::warning::could not remove review worktree: $worktree"
|
||||
done || true
|
||||
"${GIT_SAFE[@]}" worktree prune -v || true
|
||||
"${GIT_SAFE[@]}" for-each-ref --format='%(refname:short)' 'refs/heads/qwen-review/*' \
|
||||
| while read -r stale_ref; do
|
||||
if [ -n "$stale_ref" ]; then
|
||||
"${GIT_SAFE[@]}" branch -D "$stale_ref" ||
|
||||
echo "::warning::could not remove review branch: $stale_ref"
|
||||
fi
|
||||
done || true
|
||||
fi
|
||||
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd' # v6.0.2
|
||||
with:
|
||||
|
|
@ -815,6 +916,62 @@ jobs:
|
|||
fi
|
||||
chmod -R u+rwX "$GITHUB_WORKSPACE" 2>/dev/null || sudo -n chmod -R u+rwX "$GITHUB_WORKSPACE" || echo "::warning::could not restore workspace write permissions; checkout may fail on leftover read-only files"
|
||||
|
||||
# Same pre-checkout recovery as the test job: this job lands on the
|
||||
# same reused pool, so leftover review worktrees and branches from an
|
||||
# interrupted review would break this checkout too.
|
||||
- name: 'Clean stale .qwen before checkout'
|
||||
run: |-
|
||||
set -uo pipefail
|
||||
if [ -d "$GITHUB_WORKSPACE/.qwen" ] && [ ! -L "$GITHUB_WORKSPACE/.qwen" ]; then
|
||||
chmod -R u+w "$GITHUB_WORKSPACE/.qwen" 2>/dev/null || true
|
||||
rm -rf "$GITHUB_WORKSPACE/.qwen" 2>/dev/null || sudo -n rm -rf "$GITHUB_WORKSPACE/.qwen" 2>/dev/null || echo "::warning::leaked .qwen; runner needs manual cleanup"
|
||||
fi
|
||||
# Interrupted reviews leave worktree registrations under .qwen/tmp/
|
||||
# and qwen-review/* branches behind. prune drops registrations whose
|
||||
# directories the rm above removed; worktree remove --force then
|
||||
# clears any still-registered leftover directory (--force tolerates
|
||||
# dirty contents), since a branch checked out in a live worktree
|
||||
# cannot be deleted. If removal still fails, the registration
|
||||
# survives and the branch delete below warns. The sweep deletes all
|
||||
# review artifacts, not just the current PR's: safe because a runner
|
||||
# executes one job at a time. Kept inline rather than a shared
|
||||
# script: this runs pre-checkout on shared runners, where leftover
|
||||
# workspace files are untrusted.
|
||||
if [ -e "$GITHUB_WORKSPACE/.git" ]; then
|
||||
GIT_SAFE=(git -c core.hooksPath=/dev/null -c core.fsmonitor= -C "$GITHUB_WORKSPACE")
|
||||
"${GIT_SAFE[@]}" worktree prune -v || true
|
||||
"${GIT_SAFE[@]}" worktree list --porcelain \
|
||||
| awk '$1 == "worktree" && index($0, "/.qwen/tmp/review-pr-") > 0 { sub(/^worktree /, ""); print }' \
|
||||
| while read -r worktree; do
|
||||
[ -n "$worktree" ] || continue
|
||||
# Registered paths come from leftover git metadata and are
|
||||
# untrusted: the awk filter above matched by substring, so reject
|
||||
# `..` traversal and re-anchor to the review prefix before the
|
||||
# destructive remove.
|
||||
case "$worktree" in
|
||||
*/../*|../*|*/..)
|
||||
echo "::warning::skipping suspicious review worktree path: $worktree"
|
||||
continue
|
||||
;;
|
||||
"$GITHUB_WORKSPACE/.qwen/tmp/review-pr-"*) : ;;
|
||||
*)
|
||||
echo "::warning::skipping unexpected review worktree path: $worktree"
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
"${GIT_SAFE[@]}" worktree remove --force "$worktree" ||
|
||||
echo "::warning::could not remove review worktree: $worktree"
|
||||
done || true
|
||||
"${GIT_SAFE[@]}" worktree prune -v || true
|
||||
"${GIT_SAFE[@]}" for-each-ref --format='%(refname:short)' 'refs/heads/qwen-review/*' \
|
||||
| while read -r stale_ref; do
|
||||
if [ -n "$stale_ref" ]; then
|
||||
"${GIT_SAFE[@]}" branch -D "$stale_ref" ||
|
||||
echo "::warning::could not remove review branch: $stale_ref"
|
||||
fi
|
||||
done || true
|
||||
fi
|
||||
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3
|
||||
with:
|
||||
|
|
|
|||
62
.github/workflows/qwen-code-pr-review.yml
vendored
62
.github/workflows/qwen-code-pr-review.yml
vendored
|
|
@ -397,15 +397,17 @@ jobs:
|
|||
echo "no prior workspace; nothing to clean"
|
||||
exit 0
|
||||
fi
|
||||
GIT_SAFE=(git -c core.hooksPath=/dev/null -c core.fsmonitor= -C "$GITHUB_WORKSPACE")
|
||||
rm -rf .qwen/tmp/review-pr-* 2>/dev/null || true
|
||||
git worktree prune -v || true
|
||||
git for-each-ref --format='%(refname:short)' 'refs/heads/qwen-review/*' \
|
||||
"${GIT_SAFE[@]}" worktree prune -v || true
|
||||
"${GIT_SAFE[@]}" for-each-ref --format='%(refname:short)' 'refs/heads/qwen-review/*' \
|
||||
| while read -r stale_ref; do
|
||||
if [ -n "$stale_ref" ]; then
|
||||
git branch -D "$stale_ref" || true
|
||||
"${GIT_SAFE[@]}" branch -D "$stale_ref" ||
|
||||
echo "::warning::could not remove review branch: $stale_ref"
|
||||
fi
|
||||
done
|
||||
git worktree prune -v || true
|
||||
done || true
|
||||
"${GIT_SAFE[@]}" worktree prune -v || true
|
||||
echo "stale agent state cleaned"
|
||||
|
||||
# SECURITY: checkout trusted base code; /review fetches PR diff context.
|
||||
|
|
@ -1015,6 +1017,56 @@ jobs:
|
|||
--repo "$GITHUB_REPOSITORY" \
|
||||
--body "$body"
|
||||
|
||||
# A cancelled or timed-out review may not reach the CLI's process cleanup.
|
||||
# Remove both the worktree directories and Git's worktree registrations so
|
||||
# the next job on this reused runner can delete qwen-review/* branches.
|
||||
# The sweep deletes all review artifacts, not just this PR's: safe because
|
||||
# a runner executes one job at a time.
|
||||
- name: 'Clean review worktrees'
|
||||
if: 'always()'
|
||||
timeout-minutes: 5
|
||||
run: |-
|
||||
set -uo pipefail
|
||||
if [ ! -e .git ]; then
|
||||
echo "no Git checkout; nothing to clean"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
GIT_SAFE=(git -c core.hooksPath=/dev/null -c core.fsmonitor= -C "$GITHUB_WORKSPACE")
|
||||
"${GIT_SAFE[@]}" worktree prune -v || true
|
||||
"${GIT_SAFE[@]}" worktree list --porcelain \
|
||||
| awk '$1 == "worktree" && index($0, "/.qwen/tmp/review-pr-") > 0 { sub(/^worktree /, ""); print }' \
|
||||
| while read -r worktree; do
|
||||
[ -n "$worktree" ] || continue
|
||||
# Registered paths come from leftover git metadata and are
|
||||
# untrusted: the awk filter above matched by substring, so reject
|
||||
# `..` traversal and re-anchor to the review prefix before the
|
||||
# destructive remove.
|
||||
case "$worktree" in
|
||||
*/../*|../*|*/..)
|
||||
echo "::warning::skipping suspicious review worktree path: $worktree"
|
||||
continue
|
||||
;;
|
||||
"$GITHUB_WORKSPACE/.qwen/tmp/review-pr-"*) : ;;
|
||||
*)
|
||||
echo "::warning::skipping unexpected review worktree path: $worktree"
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
"${GIT_SAFE[@]}" worktree remove --force "$worktree" ||
|
||||
echo "::warning::could not remove review worktree: $worktree"
|
||||
done || true
|
||||
rm -rf .qwen/tmp/review-pr-* 2>/dev/null || true
|
||||
"${GIT_SAFE[@]}" worktree prune -v || true
|
||||
"${GIT_SAFE[@]}" for-each-ref --format='%(refname:short)' 'refs/heads/qwen-review/*' \
|
||||
| while read -r review_ref; do
|
||||
[ -n "$review_ref" ] || continue
|
||||
"${GIT_SAFE[@]}" branch -D "$review_ref" ||
|
||||
echo "::warning::could not remove review branch: $review_ref"
|
||||
done || true
|
||||
rm -f .qwen/tmp/qwen-review-lease-pr-*.json 2>/dev/null || true
|
||||
echo "review worktrees cleaned"
|
||||
|
||||
resolve-pr:
|
||||
needs: ['authorize']
|
||||
if: |-
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue