Skip to content

Commit 3edce06

Browse files
authored
fix(ci): harden weekly-update — allowedTools, two-phase update, diff validation (#33)
1 parent 35f3b1c commit 3edce06

1 file changed

Lines changed: 146 additions & 10 deletions

File tree

.github/workflows/weekly-update.yml

Lines changed: 146 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,28 +65,38 @@ jobs:
6565
with:
6666
gpg-private-key: ${{ secrets.BOT_GPG_PRIVATE_KEY }}
6767

68-
- name: Run updating skill with Claude Code
69-
id: claude
70-
timeout-minutes: 30
68+
- name: Update dependencies (haiku)
69+
id: update
70+
timeout-minutes: 10
7171
env:
7272
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
7373
GITHUB_ACTIONS: 'true'
7474
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+
7582
if [ -z "$ANTHROPIC_API_KEY" ]; then
7683
echo "ANTHROPIC_API_KEY not set - skipping automated update"
7784
echo "success=false" >> $GITHUB_OUTPUT
7885
exit 0
7986
fi
8087
8188
set +e
82-
pnpm exec claude --print --dangerously-skip-permissions \
83-
--model sonnet \
89+
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 \
8493
"$(cat <<'PROMPT'
8594
/updating
8695
8796
<context>
8897
You are an automated CI agent in a weekly dependency update workflow.
8998
Git is configured with GPG signing. A branch has been created for you.
99+
The pnpm binary is on PATH (wrapped through Socket firewall).
90100
</context>
91101
92102
<instructions>
@@ -103,7 +113,95 @@ jobs:
103113
</success_criteria>
104114
PROMPT
105115
)" \
106-
2>&1 | tee claude-output.log
116+
2>&1 | tee claude-update.log
117+
CLAUDE_EXIT=${PIPESTATUS[0]}
118+
set -e
119+
120+
if [ "$CLAUDE_EXIT" -eq 0 ]; then
121+
echo "success=true" >> $GITHUB_OUTPUT
122+
else
123+
echo "success=false" >> $GITHUB_OUTPUT
124+
fi
125+
126+
- name: Run tests
127+
id: tests
128+
if: steps.update.outputs.success == '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 "success=true" >> $GITHUB_OUTPUT
147+
else
148+
echo "success=false" >> $GITHUB_OUTPUT
149+
{
150+
echo "BUILD_LOG<<GHEOF"
151+
tail -50 build-output.log
152+
echo "GHEOF"
153+
} >> $GITHUB_OUTPUT
154+
{
155+
echo "TEST_LOG<<GHEOF"
156+
tail -50 test-output.log
157+
echo "GHEOF"
158+
} >> $GITHUB_OUTPUT
159+
fi
160+
161+
- name: Fix test failures (sonnet)
162+
id: claude
163+
if: steps.update.outputs.success == 'true' && steps.tests.outputs.success == 'false'
164+
timeout-minutes: 15
165+
env:
166+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
167+
GITHUB_ACTIONS: 'true'
168+
BUILD_LOG: ${{ steps.tests.outputs.BUILD_LOG }}
169+
TEST_LOG: ${{ steps.tests.outputs.TEST_LOG }}
170+
run: |
171+
if [ -n "$SFW_BIN" ]; then
172+
mkdir -p /tmp/sfw-bin
173+
printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
174+
chmod +x /tmp/sfw-bin/pnpm
175+
export PATH="/tmp/sfw-bin:$PATH"
176+
fi
177+
178+
set +e
179+
claude --print \
180+
--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" \
181+
--model sonnet \
182+
--max-turns 25 \
183+
"$(cat <<PROMPT
184+
Dependency updates were applied but tests are failing. Fix the failures.
185+
Git is configured with GPG signing.
186+
The pnpm binary is on PATH (wrapped through Socket firewall).
187+
188+
<build_output>
189+
$BUILD_LOG
190+
</build_output>
191+
192+
<test_output>
193+
$TEST_LOG
194+
</test_output>
195+
196+
<instructions>
197+
1. Analyze the build and test failures above.
198+
2. Fix the source code so tests pass (pnpm build && pnpm test).
199+
3. Commit each fix with a conventional commit message.
200+
4. Leave all changes local — the workflow handles pushing.
201+
</instructions>
202+
PROMPT
203+
)" \
204+
2>&1 | tee claude-fix.log
107205
CLAUDE_EXIT=${PIPESTATUS[0]}
108206
set -e
109207
@@ -113,6 +211,40 @@ jobs:
113211
echo "success=false" >> $GITHUB_OUTPUT
114212
fi
115213
214+
- name: Set final status
215+
id: final
216+
if: always()
217+
run: |
218+
UPDATE="${{ steps.update.outputs.success }}"
219+
TESTS="${{ steps.tests.outputs.success }}"
220+
FIX="${{ steps.claude.outputs.success }}"
221+
222+
if [ "$UPDATE" != "true" ]; then
223+
echo "success=false" >> $GITHUB_OUTPUT
224+
elif [ "$TESTS" = "true" ]; then
225+
echo "success=true" >> $GITHUB_OUTPUT
226+
elif [ "$FIX" = "true" ]; then
227+
echo "success=true" >> $GITHUB_OUTPUT
228+
else
229+
echo "success=false" >> $GITHUB_OUTPUT
230+
fi
231+
232+
- name: Validate changes
233+
id: validate
234+
if: steps.final.outputs.success == 'true'
235+
run: |
236+
UNEXPECTED=""
237+
for file in $(git diff --name-only origin/main..HEAD); do
238+
case "$file" in
239+
package.json|*/package.json|pnpm-lock.yaml|*/pnpm-lock.yaml|.npmrc|pnpm-workspace.yaml) ;;
240+
*) UNEXPECTED="$UNEXPECTED $file" ;;
241+
esac
242+
done
243+
if [ -n "$UNEXPECTED" ]; then
244+
echo "::warning::Non-dependency files modified:$UNEXPECTED"
245+
fi
246+
echo "valid=true" >> $GITHUB_OUTPUT
247+
116248
- name: Check for changes
117249
id: changes
118250
run: |
@@ -123,13 +255,13 @@ jobs:
123255
fi
124256
125257
- name: Push branch
126-
if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
258+
if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
127259
env:
128260
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
129261
run: git push origin "$BRANCH_NAME"
130262

131263
- name: Create Pull Request
132-
if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
264+
if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
133265
env:
134266
GH_TOKEN: ${{ github.token }}
135267
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
@@ -158,7 +290,7 @@ jobs:
158290
--base main
159291
160292
- name: Add job summary
161-
if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
293+
if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
162294
env:
163295
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
164296
run: |
@@ -173,7 +305,11 @@ jobs:
173305
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
174306
with:
175307
name: claude-output-${{ github.run_id }}
176-
path: claude-output.log
308+
path: |
309+
claude-update.log
310+
claude-fix.log
311+
build-output.log
312+
test-output.log
177313
retention-days: 7
178314

179315
- uses: SocketDev/socket-registry/.github/actions/cleanup-git-signing@6096b06b1790f411714c89c40f72aade2eeaab7c # main

0 commit comments

Comments
 (0)