Skip to content

Commit ca42b64

Browse files
authored
chore(ci): align weekly-update workflow with other repos (#137)
* chore(ci): align weekly-update workflow with other repos - Use pnpm exec claude (not bare claude) - Add structured context/instructions/success_criteria to /updating prompt - Remove stale comment, shell: bash override, pnpm alias * fix: restore shell: bash and pnpm alias, keep structured prompt * - Replace --dangerously-skip-permissions with --allowedTools whitelist (Bash pnpm/git only, Read, Write, Edit, Glob, Grep) - Switch to --model haiku (cheapest, sufficient for dependency updates) - Add --max-turns 25 to prevent runaway loops - Fix SFW_BIN: use PATH wrapper instead of alias (propagates to subprocesses) - Add post-agent diff validation (block unexpected file modifications) - Gate push/PR on validation passing - Reduce timeout-minutes from 30 to 15 * fix(ci): use sonnet model for CI agent (haiku insufficient for test fixing) * feat(ci): two-phase update — haiku for deps, sonnet escalation for test fixes Phase 1: haiku updates deps (cheap, fast, 15 turns, 10min timeout) Phase 2: build + test to verify updates work Phase 3: if tests fail, sonnet diagnoses and fixes (25 turns, 15min) gets build/test failure logs as context Validate: allow dependency files + source/test files from fix step * fix(ci): restrict git commands to add/commit/status/diff/log (no push/remote)
1 parent 0145a82 commit ca42b64

File tree

1 file changed

+173
-15
lines changed

1 file changed

+173
-15
lines changed

.github/workflows/weekly-update.yml

Lines changed: 173 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ jobs:
5151
steps:
5252
- uses: SocketDev/socket-registry/.github/actions/setup-and-install@6096b06b1790f411714c89c40f72aade2eeaab7c # main
5353

54-
# claude-code is now a devDependency, installed via setup-and-install
55-
5654
- name: Create update branch
5755
id: branch
5856
env:
@@ -67,25 +65,58 @@ jobs:
6765
with:
6866
gpg-private-key: ${{ secrets.BOT_GPG_PRIVATE_KEY }}
6967

70-
- name: Run updating skill with Claude Code
71-
id: claude
72-
timeout-minutes: 30
68+
- name: Update dependencies (haiku — fast, cheap)
69+
id: update
70+
timeout-minutes: 10
7371
shell: bash
7472
env:
7573
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
7674
GITHUB_ACTIONS: 'true'
7775
run: |
78-
alias pnpm="$SFW_BIN pnpm"
76+
# Wrap pnpm through Socket firewall for all subprocesses (not just this shell).
77+
if [ -n "$SFW_BIN" ]; then
78+
mkdir -p /tmp/sfw-bin
79+
printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
80+
chmod +x /tmp/sfw-bin/pnpm
81+
export PATH="/tmp/sfw-bin:$PATH"
82+
fi
83+
7984
if [ -z "$ANTHROPIC_API_KEY" ]; then
8085
echo "ANTHROPIC_API_KEY not set - skipping automated update"
8186
echo "success=false" >> $GITHUB_OUTPUT
8287
exit 0
8388
fi
8489
8590
set +e
86-
claude --print --dangerously-skip-permissions \
87-
--model sonnet \
88-
"/updating" \
91+
claude --print \
92+
--model haiku \
93+
--max-turns 15 \
94+
--allowedTools \
95+
"Bash(pnpm:*)" \
96+
"Bash(git add:*)" "Bash(git commit:*)" "Bash(git status:*)" "Bash(git diff:*)" "Bash(git log:*)" "Bash(git rev-parse:*)" \
97+
"Read" "Write" "Edit" "Glob" "Grep" \
98+
"$(cat <<'PROMPT'
99+
/updating
100+
101+
<context>
102+
You are an automated CI agent in a weekly dependency update workflow.
103+
Git is configured with GPG signing. A branch has been created for you.
104+
</context>
105+
106+
<instructions>
107+
Update all dependencies to their latest versions.
108+
Create one atomic commit per dependency update with a conventional commit message.
109+
Leave all changes local — the workflow handles pushing and PR creation.
110+
Do not run builds or tests — the next step handles that.
111+
</instructions>
112+
113+
<success_criteria>
114+
Each updated dependency has its own commit.
115+
The lockfile is consistent with package.json changes.
116+
No uncommitted changes remain in the working tree.
117+
</success_criteria>
118+
PROMPT
119+
)" \
89120
2>&1 | tee claude-output.log
90121
CLAUDE_EXIT=${PIPESTATUS[0]}
91122
set -e
@@ -96,6 +127,130 @@ jobs:
96127
echo "success=false" >> $GITHUB_OUTPUT
97128
fi
98129
130+
- name: Run tests
131+
id: tests
132+
if: steps.update.outputs.success == 'true'
133+
shell: bash
134+
run: |
135+
if [ -n "$SFW_BIN" ]; then
136+
mkdir -p /tmp/sfw-bin
137+
printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
138+
chmod +x /tmp/sfw-bin/pnpm
139+
export PATH="/tmp/sfw-bin:$PATH"
140+
fi
141+
142+
set +e
143+
pnpm run build 2>&1 | tee build-output.log
144+
BUILD_EXIT=$?
145+
if [ "$BUILD_EXIT" -ne 0 ]; then
146+
echo "tests-passed=false" >> $GITHUB_OUTPUT
147+
exit 0
148+
fi
149+
150+
pnpm test 2>&1 | tee test-output.log
151+
TEST_EXIT=$?
152+
set -e
153+
154+
if [ "$TEST_EXIT" -eq 0 ]; then
155+
echo "tests-passed=true" >> $GITHUB_OUTPUT
156+
else
157+
echo "tests-passed=false" >> $GITHUB_OUTPUT
158+
fi
159+
160+
- name: Fix test failures (sonnet — smarter, escalated)
161+
id: claude
162+
if: steps.update.outputs.success == 'true' && steps.tests.outputs.tests-passed == 'false'
163+
timeout-minutes: 15
164+
shell: bash
165+
env:
166+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
167+
GITHUB_ACTIONS: 'true'
168+
run: |
169+
if [ -n "$SFW_BIN" ]; then
170+
mkdir -p /tmp/sfw-bin
171+
printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
172+
chmod +x /tmp/sfw-bin/pnpm
173+
export PATH="/tmp/sfw-bin:$PATH"
174+
fi
175+
176+
# Collect failure context for the agent.
177+
FAILURE_LOG=""
178+
if [ -f build-output.log ]; then
179+
FAILURE_LOG+="Build output (last 50 lines):"$'\n'
180+
FAILURE_LOG+="$(tail -50 build-output.log)"$'\n\n'
181+
fi
182+
if [ -f test-output.log ]; then
183+
FAILURE_LOG+="Test output (last 100 lines):"$'\n'
184+
FAILURE_LOG+="$(tail -100 test-output.log)"$'\n'
185+
fi
186+
187+
set +e
188+
claude --print \
189+
--model sonnet \
190+
--max-turns 25 \
191+
--allowedTools \
192+
"Bash(pnpm:*)" \
193+
"Bash(git add:*)" "Bash(git commit:*)" "Bash(git status:*)" "Bash(git diff:*)" "Bash(git log:*)" "Bash(git rev-parse:*)" \
194+
"Read" "Write" "Edit" "Glob" "Grep" \
195+
"$(cat <<PROMPT
196+
Build or tests failed after dependency updates. Fix them.
197+
198+
<failure_output>
199+
$FAILURE_LOG
200+
</failure_output>
201+
202+
<instructions>
203+
Diagnose the failures from the output above.
204+
Fix the code — do not revert the dependency updates.
205+
Run the tests again to verify your fix works.
206+
Create a commit for each fix with a conventional commit message.
207+
</instructions>
208+
PROMPT
209+
)" \
210+
2>&1 | tee -a claude-output.log
211+
CLAUDE_EXIT=${PIPESTATUS[0]}
212+
set -e
213+
214+
if [ "$CLAUDE_EXIT" -eq 0 ]; then
215+
echo "success=true" >> $GITHUB_OUTPUT
216+
else
217+
echo "success=false" >> $GITHUB_OUTPUT
218+
fi
219+
220+
- name: Set final status
221+
id: final
222+
if: always()
223+
run: |
224+
# Success if: update succeeded AND (tests passed OR fix succeeded)
225+
if [ "${{ steps.update.outputs.success }}" = "true" ]; then
226+
if [ "${{ steps.tests.outputs.tests-passed }}" = "true" ] || [ "${{ steps.claude.outputs.success }}" = "true" ]; then
227+
echo "success=true" >> $GITHUB_OUTPUT
228+
exit 0
229+
fi
230+
fi
231+
echo "success=false" >> $GITHUB_OUTPUT
232+
233+
- name: Validate changes
234+
id: validate
235+
if: steps.final.outputs.success == 'true'
236+
run: |
237+
# Allow dependency files + source/test fixes from the fix step.
238+
UNEXPECTED=""
239+
for file in $(git diff --name-only origin/main..HEAD); do
240+
case "$file" in
241+
package.json|*/package.json|pnpm-lock.yaml|*/pnpm-lock.yaml) ;;
242+
.npmrc|pnpm-workspace.yaml) ;;
243+
src/*|test/*|*.ts|*.mts|*.js|*.mjs) ;;
244+
*) UNEXPECTED="$UNEXPECTED $file" ;;
245+
esac
246+
done
247+
if [ -n "$UNEXPECTED" ]; then
248+
echo "::error::Unexpected files modified:$UNEXPECTED"
249+
echo "valid=false" >> $GITHUB_OUTPUT
250+
else
251+
echo "valid=true" >> $GITHUB_OUTPUT
252+
fi
253+
99254
- name: Check for changes
100255
id: changes
101256
run: |
@@ -106,13 +261,13 @@ jobs:
106261
fi
107262
108263
- name: Push branch
109-
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'
110265
env:
111266
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
112267
run: git push origin "$BRANCH_NAME"
113268

114269
- name: Create Pull Request
115-
if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
270+
if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
116271
env:
117272
GH_TOKEN: ${{ github.token }}
118273
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
@@ -141,7 +296,7 @@ jobs:
141296
--base main
142297
143298
- name: Add job summary
144-
if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
299+
if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
145300
env:
146301
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
147302
run: |
@@ -151,12 +306,15 @@ jobs:
151306
echo "**Branch:** \`${BRANCH_NAME}\`" >> $GITHUB_STEP_SUMMARY
152307
echo "**Commits:** ${COMMIT_COUNT}" >> $GITHUB_STEP_SUMMARY
153308
154-
- name: Upload Claude output
309+
- name: Upload logs
155310
if: always()
156311
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
157312
with:
158-
name: claude-output-${{ github.run_id }}
159-
path: claude-output.log
313+
name: weekly-update-logs-${{ github.run_id }}
314+
path: |
315+
claude-output.log
316+
build-output.log
317+
test-output.log
160318
retention-days: 7
161319

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

0 commit comments

Comments
 (0)