Skip to content

Commit 692f2ec

Browse files
committed
fix(ci): harden weekly-update — allowedTools, two-phase update, diff validation
1 parent b77350a commit 692f2ec

File tree

1 file changed

+167
-12
lines changed

1 file changed

+167
-12
lines changed

.github/workflows/weekly-update.yml

Lines changed: 167 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -86,30 +86,146 @@ jobs:
8686
GH_TOKEN: ${{ github.token }}
8787
run: |
8888
BRANCH_NAME="weekly-update-$(date +%Y%m%d)"
89-
git config user.name "github-actions[bot]"
90-
git config user.email "github-actions[bot]@users.noreply.github.com"
9189
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git"
9290
git checkout -b "$BRANCH_NAME"
9391
echo "branch=$BRANCH_NAME" >> $GITHUB_OUTPUT
9492
95-
- name: Run updating skill with Claude Code
96-
id: claude
97-
timeout-minutes: 30
93+
- uses: SocketDev/socket-registry/.github/actions/setup-git-signing@6096b06b1790f411714c89c40f72aade2eeaab7c # main
94+
with:
95+
gpg-private-key: ${{ secrets.BOT_GPG_PRIVATE_KEY }}
96+
97+
- name: Update dependencies (haiku — fast, cheap)
98+
id: update
99+
timeout-minutes: 10
98100
env:
99101
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
100102
GITHUB_ACTIONS: 'true'
101103
run: |
104+
if [ -n "$SFW_BIN" ]; then
105+
mkdir -p /tmp/sfw-bin
106+
printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
107+
chmod +x /tmp/sfw-bin/pnpm
108+
export PATH="/tmp/sfw-bin:$PATH"
109+
fi
110+
102111
if [ -z "$ANTHROPIC_API_KEY" ]; then
103112
echo "ANTHROPIC_API_KEY not set - skipping automated update"
104113
echo "success=false" >> $GITHUB_OUTPUT
105114
exit 0
106115
fi
107116
108117
set +e
109-
pnpm exec claude --print --dangerously-skip-permissions \
118+
pnpm exec claude --print \
119+
--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" \
120+
--model haiku \
121+
--max-turns 15 \
122+
"$(cat <<'PROMPT'
123+
/updating
124+
125+
<context>
126+
You are an automated CI agent in a weekly dependency update workflow.
127+
Git is configured with GPG signing. A branch has been created for you.
128+
</context>
129+
130+
<instructions>
131+
Update all dependencies to their latest versions.
132+
Create one atomic commit per dependency update with a conventional commit message.
133+
Leave all changes local — the workflow handles pushing and PR creation.
134+
Do not run builds or tests — the next step handles that.
135+
</instructions>
136+
137+
<success_criteria>
138+
Each updated dependency has its own commit.
139+
The lockfile is consistent with package.json changes.
140+
No uncommitted changes remain in the working tree.
141+
</success_criteria>
142+
PROMPT
143+
)" \
144+
2>&1 | tee claude-update.log
145+
CLAUDE_EXIT=${PIPESTATUS[0]}
146+
set -e
147+
148+
if [ "$CLAUDE_EXIT" -eq 0 ]; then
149+
echo "success=true" >> $GITHUB_OUTPUT
150+
else
151+
echo "success=false" >> $GITHUB_OUTPUT
152+
fi
153+
154+
- name: Run tests
155+
id: tests
156+
if: steps.update.outputs.success == 'true'
157+
run: |
158+
if [ -n "$SFW_BIN" ]; then
159+
mkdir -p /tmp/sfw-bin
160+
printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
161+
chmod +x /tmp/sfw-bin/pnpm
162+
export PATH="/tmp/sfw-bin:$PATH"
163+
fi
164+
165+
set +e
166+
pnpm build 2>&1 | tee build.log
167+
BUILD_EXIT=${PIPESTATUS[0]}
168+
169+
pnpm test 2>&1 | tee test.log
170+
TEST_EXIT=${PIPESTATUS[0]}
171+
set -e
172+
173+
if [ "$BUILD_EXIT" -eq 0 ] && [ "$TEST_EXIT" -eq 0 ]; then
174+
echo "tests-passed=true" >> $GITHUB_OUTPUT
175+
else
176+
echo "tests-passed=false" >> $GITHUB_OUTPUT
177+
fi
178+
179+
- name: Fix test failures (sonnet — smarter, escalated)
180+
id: claude
181+
if: steps.update.outputs.success == 'true' && steps.tests.outputs.tests-passed == 'false'
182+
timeout-minutes: 15
183+
env:
184+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
185+
GITHUB_ACTIONS: 'true'
186+
run: |
187+
if [ -n "$SFW_BIN" ]; then
188+
mkdir -p /tmp/sfw-bin
189+
printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
190+
chmod +x /tmp/sfw-bin/pnpm
191+
export PATH="/tmp/sfw-bin:$PATH"
192+
fi
193+
194+
FAILURE_LOG="$(cat build.log test.log 2>/dev/null)"
195+
196+
set +e
197+
pnpm exec claude --print \
198+
--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" \
110199
--model sonnet \
111-
"/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." \
112-
2>&1 | tee claude-output.log
200+
--max-turns 25 \
201+
"$(cat <<PROMPT
202+
<context>
203+
You are an automated CI agent in a weekly dependency update workflow.
204+
Git is configured with GPG signing. A branch has been created for you.
205+
Dependencies were updated in the previous step but build/tests failed.
206+
</context>
207+
208+
<failure_log>
209+
$FAILURE_LOG
210+
</failure_log>
211+
212+
<instructions>
213+
The dependency updates above caused build or test failures.
214+
Diagnose the failures from the logs and fix the code so it builds and tests pass.
215+
Create one atomic commit per fix with a conventional commit message.
216+
Run pnpm build && pnpm test to verify your fixes.
217+
Leave all changes local — the workflow handles pushing and PR creation.
218+
</instructions>
219+
220+
<success_criteria>
221+
pnpm build succeeds.
222+
pnpm test succeeds.
223+
Each fix has its own commit.
224+
No uncommitted changes remain in the working tree.
225+
</success_criteria>
226+
PROMPT
227+
)" \
228+
2>&1 | tee claude-fix.log
113229
CLAUDE_EXIT=${PIPESTATUS[0]}
114230
set -e
115231
@@ -119,6 +235,38 @@ jobs:
119235
echo "success=false" >> $GITHUB_OUTPUT
120236
fi
121237
238+
- name: Set final status
239+
id: final
240+
if: always()
241+
run: |
242+
if [ "${{ steps.update.outputs.success }}" = "true" ] && [ "${{ steps.tests.outputs.tests-passed }}" = "true" ]; then
243+
echo "success=true" >> $GITHUB_OUTPUT
244+
elif [ "${{ steps.update.outputs.success }}" = "true" ] && [ "${{ steps.claude.outputs.success }}" = "true" ]; then
245+
echo "success=true" >> $GITHUB_OUTPUT
246+
else
247+
echo "success=false" >> $GITHUB_OUTPUT
248+
fi
249+
250+
- name: Validate changes
251+
id: validate
252+
if: steps.final.outputs.success == 'true'
253+
run: |
254+
UNEXPECTED=""
255+
for file in $(git diff --name-only origin/main..HEAD); do
256+
case "$file" in
257+
package.json|*/package.json|pnpm-lock.yaml|*/pnpm-lock.yaml|.npmrc|pnpm-workspace.yaml) ;;
258+
src/*|test/*) ;;
259+
*.ts|*.mts|*.js|*.mjs) ;;
260+
*) UNEXPECTED="$UNEXPECTED $file" ;;
261+
esac
262+
done
263+
if [ -n "$UNEXPECTED" ]; then
264+
echo "::error::Unexpected files modified by Claude:$UNEXPECTED"
265+
echo "valid=false" >> $GITHUB_OUTPUT
266+
else
267+
echo "valid=true" >> $GITHUB_OUTPUT
268+
fi
269+
122270
- name: Check for changes
123271
id: changes
124272
run: |
@@ -129,13 +277,13 @@ jobs:
129277
fi
130278
131279
- name: Push branch
132-
if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
280+
if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
133281
env:
134282
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
135283
run: git push origin "$BRANCH_NAME"
136284

137285
- name: Create Pull Request
138-
if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
286+
if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
139287
env:
140288
GH_TOKEN: ${{ github.token }}
141289
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
@@ -164,7 +312,7 @@ jobs:
164312
--base main
165313
166314
- name: Add job summary
167-
if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
315+
if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
168316
env:
169317
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
170318
run: |
@@ -179,9 +327,16 @@ jobs:
179327
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
180328
with:
181329
name: claude-output-${{ github.run_id }}
182-
path: claude-output.log
330+
path: |
331+
claude-update.log
332+
claude-fix.log
333+
build.log
334+
test.log
183335
retention-days: 7
184336

337+
- uses: SocketDev/socket-registry/.github/actions/cleanup-git-signing@6096b06b1790f411714c89c40f72aade2eeaab7c # main
338+
if: always()
339+
185340
notify:
186341
name: Notify results
187342
needs: [check-updates, apply-updates]

0 commit comments

Comments
 (0)