fix(agent): run step scripts from outside the workspace so agents can't break the run
Stage the shared scripts into $RUNNER_TEMP and point $SCRIPTS there for every step, so an agent that commits/deletes the in-tree .agents-workflow checkout no longer destroys the scripts the post-agent steps run (issue #33). Scrub any in-tree .agents-workflow artifact before publishing, and add a failure-safe rescue step that opens a PR for pushed work when a run fails.
This commit is contained in:
@@ -98,6 +98,18 @@ if [ "$MODE" != "pr" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Scrub the runtime scripts checkout (.agents-workflow) from the tree so it never lands in a
|
||||
# commit/PR and never confuses the git ops below (issue #33). The scripts we run live outside the
|
||||
# workspace ($SCRIPTS -> runner.temp), so removing this in-tree copy is always safe. Handle every
|
||||
# way an agent might have left it: untracked dir, tracked files, or a committed gitlink/submodule.
|
||||
if git ls-files --error-unmatch .agents-workflow >/dev/null 2>&1 || \
|
||||
[ -n "$(git ls-files .agents-workflow 2>/dev/null)" ]; then
|
||||
git rm -r --cached --quiet --ignore-unmatch .agents-workflow 2>/dev/null || true
|
||||
fi
|
||||
git config -f .gitmodules --remove-section submodule..agents-workflow 2>/dev/null || true
|
||||
[ -s .gitmodules ] || rm -f .gitmodules 2>/dev/null || true
|
||||
rm -rf .agents-workflow 2>/dev/null || true
|
||||
|
||||
# The agent may have committed on the starting branch AND/OR created extra
|
||||
# ai/issue-N-<slug> branches. Commit any leftover on the current branch, push it, then
|
||||
# open a PR for EVERY ai/issue-N* branch that has commits beyond main.
|
||||
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
# Failure-safe rescue: when a run FAILED after a dev agent already pushed commits, the normal
|
||||
# Publish step never ran and the work would be stranded on the branch with no PR (issue #33).
|
||||
# This opens a PR for the pushed branch so nothing is silently lost. It is strictly best-effort:
|
||||
# every failure here is swallowed (the caller also appends `|| true`) so it can never itself break
|
||||
# the run. Comment-only roles (pm/qa) push nothing, so they are skipped.
|
||||
#
|
||||
# Required env (provided by the workflow step):
|
||||
# GT TOKEN_PM TOKEN_SENIOR TOKEN_JUNIOR TOKEN_LEAD TOKEN_QA
|
||||
# NAME MODE NUM TITLE BRANCH GITHUB_SERVER_URL GITHUB_REPOSITORY
|
||||
set +e
|
||||
|
||||
# Only dev agents (mode=pr) ever push a branch to rescue.
|
||||
[ "${MODE:-}" = "pr" ] || { echo "rescue: comment-mode agent, nothing to rescue"; exit 0; }
|
||||
[ -n "${BRANCH:-}" ] || { echo "rescue: no branch known, skipping"; exit 0; }
|
||||
|
||||
# Post/PR as the agent's OWN Gitea user when its token is configured; else the built-in bot.
|
||||
case "$NAME" in
|
||||
pm) TOK="$TOKEN_PM";; senior) TOK="$TOKEN_SENIOR";; junior) TOK="$TOKEN_JUNIOR";;
|
||||
lead) TOK="$TOKEN_LEAD";; qa) TOK="$TOKEN_QA";; *) TOK="";;
|
||||
esac
|
||||
[ -z "$TOK" ] && TOK="$GT"
|
||||
API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}"
|
||||
hdr=(-H "Authorization: token $TOK" -H "Content-Type: application/json")
|
||||
|
||||
git fetch -q origin 2>/dev/null || true
|
||||
|
||||
# Nothing to rescue unless the branch exists on the remote with commits beyond main.
|
||||
ahead=$(git rev-list --count "origin/main..origin/$BRANCH" 2>/dev/null || echo 0)
|
||||
if [ "${ahead:-0}" -eq 0 ]; then
|
||||
echo "rescue: no pushed commits on origin/$BRANCH beyond main — nothing to rescue"
|
||||
exit 0
|
||||
fi
|
||||
echo "rescue: origin/$BRANCH is $ahead commit(s) ahead of main — ensuring a PR exists"
|
||||
|
||||
# Idempotent: Gitea ignores ?head=, so match the head branch client-side.
|
||||
resp=$(curl -sS "${hdr[@]}" "$API/pulls?state=open&limit=50" \
|
||||
| jq -r --arg br "$BRANCH" 'if type=="array" then (map(select(.head.ref==$br)) | .[0] // empty) else empty end' 2>/dev/null)
|
||||
url=$(printf '%s' "$resp" | jq -r '.html_url // empty' 2>/dev/null)
|
||||
if [ -z "$url" ]; then
|
||||
body=$(printf 'The run failed before it could publish, but pushed work exists on this branch — opening a PR so it is not lost.\n\n---\nResolves #%s · 🤖 @%s (auto-rescued after a failed run)' "$NUM" "$NAME")
|
||||
resp=$(curl -sS -X POST "${hdr[@]}" "$API/pulls" \
|
||||
-d "$(jq -nc --arg t "@$NAME: $TITLE" --arg h "$BRANCH" --arg b "$body" \
|
||||
'{title:$t, head:$h, base:"main", body:$b}')")
|
||||
echo "rescue PR create ($BRANCH): $resp"
|
||||
url=$(printf '%s' "$resp" | jq -r '.html_url // empty' 2>/dev/null)
|
||||
fi
|
||||
|
||||
if [ -n "$url" ]; then
|
||||
curl -sS -X POST "${hdr[@]}" "$API/issues/$NUM/comments" \
|
||||
-d "$(jq -nc --arg b "$(printf '🤖 **@%s** — ⚠️ the run failed, but your pushed work was not lost — a PR was opened for branch \`%s\`:\n- %s' "$NAME" "$BRANCH" "$url")" '{body:$b}')" \
|
||||
-w '\nrescue comment -> HTTP %{http_code}\n' || true
|
||||
else
|
||||
echo "rescue: could not open/find a PR for $BRANCH"
|
||||
fi
|
||||
exit 0
|
||||
Reference in New Issue
Block a user