@@ -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