Skip to content

Commit 4b3eef8

Browse files
committed
fix(ci): harden weekly-update — allowedTools, two-phase update, diff validation
1 parent 880ee30 commit 4b3eef8

File tree

1 file changed

+173
-12
lines changed

1 file changed

+173
-12
lines changed

.github/workflows/weekly-update.yml

Lines changed: 173 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,30 +57,149 @@ jobs:
5757
GH_TOKEN: ${{ github.token }}
5858
run: |
5959
BRANCH_NAME="weekly-update-$(date +%Y%m%d)"
60-
git config user.name "github-actions[bot]"
61-
git config user.email "github-actions[bot]@users.noreply.github.com"
6260
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git"
6361
git checkout -b "$BRANCH_NAME"
6462
echo "branch=$BRANCH_NAME" >> $GITHUB_OUTPUT
6563
66-
- name: Run updating skill with Claude Code
67-
id: claude
68-
timeout-minutes: 30
64+
- uses: SocketDev/socket-registry/.github/actions/setup-git-signing@6096b06b1790f411714c89c40f72aade2eeaab7c # main
65+
with:
66+
gpg-private-key: ${{ secrets.BOT_GPG_PRIVATE_KEY }}
67+
68+
- name: Update dependencies (haiku)
69+
id: update
70+
timeout-minutes: 10
6971
env:
7072
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
7173
GITHUB_ACTIONS: 'true'
7274
run: |
75+
if [ -n "$SFW_BIN" ]; then
76+
mkdir -p /tmp/sfw-bin
77+
printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
78+
chmod +x /tmp/sfw-bin/pnpm
79+
export PATH="/tmp/sfw-bin:$PATH"
80+
fi
81+
7382
if [ -z "$ANTHROPIC_API_KEY" ]; then
7483
echo "ANTHROPIC_API_KEY not set - skipping automated update"
7584
echo "success=false" >> $GITHUB_OUTPUT
7685
exit 0
7786
fi
7887
7988
set +e
80-
pnpm exec claude --print --dangerously-skip-permissions \
89+
pnpm exec claude --print \
90+
--allowedTools "Bash(pnpm:*)" "Bash(git add:*)" "Bash(git commit:*)" "Bash(git status:*)" "Bash(git diff:*)" "Bash(git log:*)" "Bash(git rev-parse:*)" "Read" "Write" "Edit" "Glob" "Grep" \
91+
--model haiku \
92+
--max-turns 15 \
93+
"$(cat <<'PROMPT'
94+
/updating
95+
96+
<context>
97+
You are an automated CI agent in a weekly dependency update workflow.
98+
Git is configured with GPG signing. A branch has been created for you.
99+
</context>
100+
101+
<instructions>
102+
Update all dependencies to their latest versions.
103+
Create one atomic commit per dependency update with a conventional commit message.
104+
Leave all changes local — the workflow handles pushing and PR creation.
105+
Skip running builds, tests, and type checks — CI runs those separately.
106+
</instructions>
107+
108+
<success_criteria>
109+
Each updated dependency has its own commit.
110+
The lockfile is consistent with package.json changes.
111+
No uncommitted changes remain in the working tree.
112+
</success_criteria>
113+
PROMPT
114+
)" \
115+
2>&1 | tee claude-update.log
116+
CLAUDE_EXIT=${PIPESTATUS[0]}
117+
set -e
118+
119+
if [ "$CLAUDE_EXIT" -eq 0 ]; then
120+
echo "success=true" >> $GITHUB_OUTPUT
121+
else
122+
echo "success=false" >> $GITHUB_OUTPUT
123+
fi
124+
125+
- name: Run tests
126+
id: tests
127+
if: steps.update.outputs.success == 'true'
128+
continue-on-error: true
129+
run: |
130+
if [ -n "$SFW_BIN" ]; then
131+
mkdir -p /tmp/sfw-bin
132+
printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
133+
chmod +x /tmp/sfw-bin/pnpm
134+
export PATH="/tmp/sfw-bin:$PATH"
135+
fi
136+
137+
set +e
138+
pnpm build 2>&1 | tee build-output.log
139+
BUILD_EXIT=${PIPESTATUS[0]}
140+
141+
pnpm test 2>&1 | tee test-output.log
142+
TEST_EXIT=${PIPESTATUS[0]}
143+
set -e
144+
145+
if [ "$BUILD_EXIT" -eq 0 ] && [ "$TEST_EXIT" -eq 0 ]; then
146+
echo "result=pass" >> $GITHUB_OUTPUT
147+
else
148+
echo "result=fail" >> $GITHUB_OUTPUT
149+
fi
150+
151+
- name: Fix test failures (sonnet)
152+
id: claude
153+
if: steps.tests.outputs.result == 'fail'
154+
timeout-minutes: 15
155+
env:
156+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
157+
GITHUB_ACTIONS: 'true'
158+
run: |
159+
if [ -n "$SFW_BIN" ]; then
160+
mkdir -p /tmp/sfw-bin
161+
printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
162+
chmod +x /tmp/sfw-bin/pnpm
163+
export PATH="/tmp/sfw-bin:$PATH"
164+
fi
165+
166+
BUILD_LOG=$(cat build-output.log 2>/dev/null || echo "No build output")
167+
TEST_LOG=$(cat test-output.log 2>/dev/null || echo "No test output")
168+
169+
set +e
170+
pnpm exec claude --print \
171+
--allowedTools "Bash(pnpm:*)" "Bash(git add:*)" "Bash(git commit:*)" "Bash(git status:*)" "Bash(git diff:*)" "Bash(git log:*)" "Bash(git rev-parse:*)" "Read" "Write" "Edit" "Glob" "Grep" \
81172
--model sonnet \
82-
"/updating - Run the updating skill to update all dependencies. Create atomic commits for each update. You are running in CI mode - skip builds and tests. Do not push or create a PR." \
83-
2>&1 | tee claude-output.log
173+
--max-turns 25 \
174+
"$(cat <<PROMPT
175+
<context>
176+
You are an automated CI agent fixing test failures after a dependency update.
177+
Git is configured with GPG signing. Dependencies were already updated and committed.
178+
</context>
179+
180+
<failure_logs>
181+
BUILD OUTPUT:
182+
${BUILD_LOG}
183+
184+
TEST OUTPUT:
185+
${TEST_LOG}
186+
</failure_logs>
187+
188+
<instructions>
189+
The dependency updates above caused build or test failures.
190+
Analyze the failure logs, identify root causes, and fix them.
191+
Create one atomic commit per fix with a conventional commit message.
192+
Leave all changes local — the workflow handles pushing and PR creation.
193+
</instructions>
194+
195+
<success_criteria>
196+
All build and test failures are resolved.
197+
Each fix has its own commit.
198+
No uncommitted changes remain in the working tree.
199+
</success_criteria>
200+
PROMPT
201+
)" \
202+
2>&1 | tee claude-fix.log
84203
CLAUDE_EXIT=${PIPESTATUS[0]}
85204
set -e
86205
@@ -90,6 +209,41 @@ jobs:
90209
echo "success=false" >> $GITHUB_OUTPUT
91210
fi
92211
212+
- name: Set final status
213+
id: final
214+
if: always()
215+
run: |
216+
if [ "${{ steps.update.outputs.success }}" != "true" ]; then
217+
echo "success=false" >> $GITHUB_OUTPUT
218+
elif [ "${{ steps.tests.outputs.result }}" != "fail" ]; then
219+
# Tests passed (or were skipped) — update succeeded without needing fixes
220+
echo "success=true" >> $GITHUB_OUTPUT
221+
elif [ "${{ steps.claude.outputs.success }}" == "true" ]; then
222+
# Tests failed but sonnet fixed them
223+
echo "success=true" >> $GITHUB_OUTPUT
224+
else
225+
echo "success=false" >> $GITHUB_OUTPUT
226+
fi
227+
228+
- name: Validate changes
229+
id: validate
230+
if: steps.final.outputs.success == 'true'
231+
run: |
232+
UNEXPECTED=""
233+
for file in $(git diff --name-only origin/main..HEAD); do
234+
case "$file" in
235+
package.json|*/package.json|pnpm-lock.yaml|*/pnpm-lock.yaml|.npmrc|pnpm-workspace.yaml) ;;
236+
src/*|test/*) ;;
237+
*) UNEXPECTED="$UNEXPECTED $file" ;;
238+
esac
239+
done
240+
if [ -n "$UNEXPECTED" ]; then
241+
echo "::error::Unexpected files modified by Claude:$UNEXPECTED"
242+
echo "valid=false" >> $GITHUB_OUTPUT
243+
else
244+
echo "valid=true" >> $GITHUB_OUTPUT
245+
fi
246+
93247
- name: Check for changes
94248
id: changes
95249
run: |
@@ -100,13 +254,13 @@ jobs:
100254
fi
101255
102256
- name: Push branch
103-
if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
257+
if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
104258
env:
105259
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
106260
run: git push origin "$BRANCH_NAME"
107261

108262
- name: Create Pull Request
109-
if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
263+
if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
110264
env:
111265
GH_TOKEN: ${{ github.token }}
112266
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
@@ -135,7 +289,7 @@ jobs:
135289
--base main
136290
137291
- name: Add job summary
138-
if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
292+
if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
139293
env:
140294
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
141295
run: |
@@ -150,9 +304,16 @@ jobs:
150304
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
151305
with:
152306
name: claude-output-${{ github.run_id }}
153-
path: claude-output.log
307+
path: |
308+
claude-update.log
309+
claude-fix.log
310+
build-output.log
311+
test-output.log
154312
retention-days: 7
155313

314+
- uses: SocketDev/socket-registry/.github/actions/cleanup-git-signing@6096b06b1790f411714c89c40f72aade2eeaab7c # main
315+
if: always()
316+
156317
notify:
157318
name: Notify results
158319
needs: [check-updates, apply-updates]

0 commit comments

Comments
 (0)