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:
@@ -42,6 +42,15 @@ jobs:
|
||||
# .gitea/workflows/scripts/) are NOT on disk yet. Check this repo out into a separate subdir
|
||||
# and run the scripts from $SCRIPTS. Pinned to @main to match the caller's
|
||||
# `uses: …/agent.yml@main`, so the scripts and the workflow always move together.
|
||||
#
|
||||
# actions/checkout requires `path` to be inside the workspace, so this necessarily lands the
|
||||
# clone at `.agents-workflow/` INSIDE the tree the dev agent later edits. That checkout dir is
|
||||
# untracked and looks like a stray artifact: an agent that commits it as a gitlink or
|
||||
# `rm -rf`s it as "leftover" would destroy the very scripts the post-agent steps run, breaking
|
||||
# the run with exit 127 and stranding pushed work with no PR (issue #33). To make the run
|
||||
# immune, the next step copies the scripts to a stable location OUTSIDE the workspace
|
||||
# (${{ runner.temp }}) and every later step runs from $SCRIPTS there — so nothing the agent
|
||||
# does to the working tree can break the run's own execution environment.
|
||||
- name: Fetch shared agent scripts (this repo)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -50,10 +59,24 @@ jobs:
|
||||
path: .agents-workflow
|
||||
token: ${{ secrets.GITEA_TOKEN }}
|
||||
|
||||
# Copy the step scripts out of the workspace so the agent cannot break them (issue #33).
|
||||
# $SCRIPTS points here for every subsequent step, NOT into the in-tree .agents-workflow/.
|
||||
- name: Stage shared scripts outside the workspace
|
||||
env:
|
||||
SRC: ${{ github.workspace }}/.agents-workflow/.gitea/workflows/scripts
|
||||
DST: ${{ runner.temp }}/agents-scripts
|
||||
run: |
|
||||
set -eu
|
||||
rm -rf "$DST"
|
||||
mkdir -p "$DST"
|
||||
cp -a "$SRC"/. "$DST"/
|
||||
chmod -R a+rx "$DST" || true
|
||||
echo "staged $(ls -1 "$DST" | wc -l) scripts at $DST"
|
||||
|
||||
- name: Route agent + prepare branch
|
||||
id: prep
|
||||
env:
|
||||
SCRIPTS: ${{ github.workspace }}/.agents-workflow/.gitea/workflows/scripts
|
||||
SCRIPTS: ${{ runner.temp }}/agents-scripts
|
||||
BODY: ${{ github.event.comment.body }} # event text via env, never inline in shell
|
||||
IBODY: ${{ github.event.issue.body }}
|
||||
# Comment-vs-issue discriminator. Do NOT use github.event_name here: this is a REUSABLE
|
||||
@@ -73,7 +96,7 @@ jobs:
|
||||
|
||||
- name: Install opencode + provider config (+ Playwright MCP for browser agents)
|
||||
env:
|
||||
SCRIPTS: ${{ github.workspace }}/.agents-workflow/.gitea/workflows/scripts
|
||||
SCRIPTS: ${{ runner.temp }}/agents-scripts
|
||||
OLLAMA_URL: ${{ secrets.OLLAMA_URL }}
|
||||
OLLAMA_CLOUD_API_KEY: ${{ secrets.OLLAMA_CLOUD_API_KEY }}
|
||||
NAME: ${{ steps.prep.outputs.name }}
|
||||
@@ -90,7 +113,7 @@ jobs:
|
||||
# All three secrets are passed via env and never inlined into shell — this shared workflow
|
||||
# runs in repos that don't have them and must not fail there.
|
||||
env:
|
||||
SCRIPTS: ${{ github.workspace }}/.agents-workflow/.gitea/workflows/scripts
|
||||
SCRIPTS: ${{ runner.temp }}/agents-scripts
|
||||
SWARM_HOST: ${{ secrets.SWARM_HOST }}
|
||||
SWARM_USER: ${{ secrets.SWARM_USER }}
|
||||
SSH_PRIV_KEY: ${{ secrets.SSH_PRIV_KEY }}
|
||||
@@ -104,14 +127,14 @@ jobs:
|
||||
# Only emitted when AGENT_TOKEN is actually present, so repos without it don't get a
|
||||
# broken skill. The token is passed via env and never inlined into shell.
|
||||
env:
|
||||
SCRIPTS: ${{ github.workspace }}/.agents-workflow/.gitea/workflows/scripts
|
||||
SCRIPTS: ${{ runner.temp }}/agents-scripts
|
||||
AGENT_TOKEN: ${{ secrets.AGENT_TOKEN }}
|
||||
run: bash "$SCRIPTS/skill-gitea-api.sh"
|
||||
|
||||
- name: Inspect / fetch image attachments (download only for vision agents)
|
||||
id: imgs
|
||||
env:
|
||||
SCRIPTS: ${{ github.workspace }}/.agents-workflow/.gitea/workflows/scripts
|
||||
SCRIPTS: ${{ runner.temp }}/agents-scripts
|
||||
GT: ${{ secrets.GITEA_TOKEN }}
|
||||
NUM: ${{ github.event.issue.number }}
|
||||
VISION: ${{ steps.prep.outputs.vision }}
|
||||
@@ -119,7 +142,7 @@ jobs:
|
||||
|
||||
- name: Fetch the full issue thread (shared memory)
|
||||
env:
|
||||
SCRIPTS: ${{ github.workspace }}/.agents-workflow/.gitea/workflows/scripts
|
||||
SCRIPTS: ${{ runner.temp }}/agents-scripts
|
||||
GT: ${{ secrets.GITEA_TOKEN }}
|
||||
NUM: ${{ github.event.issue.number }}
|
||||
run: bash "$SCRIPTS/fetch-thread.sh"
|
||||
@@ -127,7 +150,7 @@ jobs:
|
||||
- name: Run agent
|
||||
id: run
|
||||
env:
|
||||
SCRIPTS: ${{ github.workspace }}/.agents-workflow/.gitea/workflows/scripts
|
||||
SCRIPTS: ${{ runner.temp }}/agents-scripts
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
# AGENT_TOKEN powers the `gitea-api` skill (cross-repo issue/PR/Actions read+write).
|
||||
# It is already a required secret for the delegation step below; exposing it here too
|
||||
@@ -149,13 +172,13 @@ jobs:
|
||||
- name: Build activity log (tool calls + reasoning) from the event stream
|
||||
id: log
|
||||
env:
|
||||
SCRIPTS: ${{ github.workspace }}/.agents-workflow/.gitea/workflows/scripts
|
||||
SCRIPTS: ${{ runner.temp }}/agents-scripts
|
||||
MODE: ${{ steps.prep.outputs.mode }}
|
||||
run: bash "$SCRIPTS/build-activity-log.sh"
|
||||
|
||||
- name: Publish — PR (dev agents) or comment (pm), always reply in the issue
|
||||
env:
|
||||
SCRIPTS: ${{ github.workspace }}/.agents-workflow/.gitea/workflows/scripts
|
||||
SCRIPTS: ${{ runner.temp }}/agents-scripts
|
||||
GT: ${{ secrets.GITEA_TOKEN }}
|
||||
AGENT_TOKEN: ${{ secrets.AGENT_TOKEN }}
|
||||
TOKEN_PM: ${{ secrets.TOKEN_PM }}
|
||||
@@ -171,6 +194,27 @@ jobs:
|
||||
NEW: ${{ steps.prep.outputs.new }}
|
||||
run: bash "$SCRIPTS/publish.sh"
|
||||
|
||||
# Failure-safe: if any step above failed AFTER a dev agent already pushed commits, the normal
|
||||
# Publish step never ran, so the work would be stranded on the branch with no PR (issue #33).
|
||||
# This best-effort step opens a PR for the pushed branch so nothing is silently lost. Runs from
|
||||
# $SCRIPTS (outside the workspace) so it works even if the tree was mangled by the agent.
|
||||
- name: Rescue — open a PR for pushed work if the run failed
|
||||
if: failure()
|
||||
env:
|
||||
SCRIPTS: ${{ runner.temp }}/agents-scripts
|
||||
GT: ${{ secrets.GITEA_TOKEN }}
|
||||
TOKEN_PM: ${{ secrets.TOKEN_PM }}
|
||||
TOKEN_SENIOR: ${{ secrets.TOKEN_SENIOR }}
|
||||
TOKEN_JUNIOR: ${{ secrets.TOKEN_JUNIOR }}
|
||||
TOKEN_LEAD: ${{ secrets.TOKEN_LEAD }}
|
||||
TOKEN_QA: ${{ secrets.TOKEN_QA }}
|
||||
NAME: ${{ steps.prep.outputs.name }}
|
||||
MODE: ${{ steps.prep.outputs.mode }}
|
||||
NUM: ${{ github.event.issue.number }}
|
||||
TITLE: ${{ github.event.issue.title }}
|
||||
BRANCH: ${{ steps.prep.outputs.branch }}
|
||||
run: bash "$SCRIPTS/rescue-pr.sh" || true
|
||||
|
||||
- name: Mark done with 🚀 (remove 👀)
|
||||
env:
|
||||
GT: ${{ secrets.GITEA_TOKEN }}
|
||||
|
||||
Reference in New Issue
Block a user