Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 147 additions & 10 deletions .github/workflows/weekly-update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,22 +65,31 @@ jobs:
with:
gpg-private-key: ${{ secrets.BOT_GPG_PRIVATE_KEY }}

- name: Run updating skill with Claude Code
id: claude
timeout-minutes: 30
- name: Update dependencies (haiku)
id: update
timeout-minutes: 10
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_ACTIONS: 'true'
run: |
if [ -n "$SFW_BIN" ]; then
mkdir -p /tmp/sfw-bin
printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
chmod +x /tmp/sfw-bin/pnpm
export PATH="/tmp/sfw-bin:$PATH"
fi

if [ -z "$ANTHROPIC_API_KEY" ]; then
echo "ANTHROPIC_API_KEY not set - skipping automated update"
echo "success=false" >> $GITHUB_OUTPUT
exit 0
fi

set +e
pnpm exec claude --print --dangerously-skip-permissions \
--model sonnet \
pnpm exec claude --print \
--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" \
--model haiku \
--max-turns 15 \
"$(cat <<'PROMPT'
/updating

Expand All @@ -103,7 +112,7 @@ jobs:
</success_criteria>
PROMPT
)" \
2>&1 | tee claude-output.log
2>&1 | tee claude-update.log
CLAUDE_EXIT=${PIPESTATUS[0]}
set -e

Expand All @@ -113,6 +122,130 @@ jobs:
echo "success=false" >> $GITHUB_OUTPUT
fi

- name: Run tests
id: tests
if: steps.update.outputs.success == 'true'
continue-on-error: true
run: |
if [ -n "$SFW_BIN" ]; then
mkdir -p /tmp/sfw-bin
printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
chmod +x /tmp/sfw-bin/pnpm
export PATH="/tmp/sfw-bin:$PATH"
fi

set +e
pnpm build 2>&1 | tee build-output.log
BUILD_EXIT=${PIPESTATUS[0]}

pnpm test 2>&1 | tee test-output.log
TEST_EXIT=${PIPESTATUS[0]}
set -e

if [ "$BUILD_EXIT" -eq 0 ] && [ "$TEST_EXIT" -eq 0 ]; then
echo "result=pass" >> $GITHUB_OUTPUT
else
echo "result=fail" >> $GITHUB_OUTPUT
fi

- name: Fix test failures (sonnet)
id: claude
if: steps.tests.outputs.result == 'fail'
timeout-minutes: 15
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_ACTIONS: 'true'
run: |
if [ -n "$SFW_BIN" ]; then
mkdir -p /tmp/sfw-bin
printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
chmod +x /tmp/sfw-bin/pnpm
export PATH="/tmp/sfw-bin:$PATH"
fi

BUILD_LOG=$(cat build-output.log 2>/dev/null || echo "No build output")
TEST_LOG=$(cat test-output.log 2>/dev/null || echo "No test output")

set +e
pnpm exec claude --print \
--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" \
--model sonnet \
--max-turns 25 \
"$(cat <<PROMPT
<context>
You are an automated CI agent fixing test failures after a dependency update.
Git is configured with GPG signing. Dependencies were already updated and committed.
</context>

<failure_logs>
BUILD OUTPUT:
${BUILD_LOG}

TEST OUTPUT:
${TEST_LOG}
</failure_logs>

<instructions>
The dependency updates above caused build or test failures.
Analyze the failure logs, identify root causes, and fix them.
Create one atomic commit per fix with a conventional commit message.
Leave all changes local — the workflow handles pushing and PR creation.
</instructions>

<success_criteria>
All build and test failures are resolved.
Each fix has its own commit.
No uncommitted changes remain in the working tree.
</success_criteria>
PROMPT
)" \
2>&1 | tee claude-fix.log
CLAUDE_EXIT=${PIPESTATUS[0]}
set -e

if [ "$CLAUDE_EXIT" -eq 0 ]; then
echo "success=true" >> $GITHUB_OUTPUT
else
echo "success=false" >> $GITHUB_OUTPUT
fi

- name: Set final status
id: final
if: always()
env:
UPDATE_SUCCESS: ${{ steps.update.outputs.success }}
TESTS_RESULT: ${{ steps.tests.outputs.result }}
FIX_SUCCESS: ${{ steps.claude.outputs.success }}
run: |
if [ "$UPDATE_SUCCESS" != "true" ]; then
echo "success=false" >> $GITHUB_OUTPUT
elif [ "$TESTS_RESULT" != "fail" ]; then
echo "success=true" >> $GITHUB_OUTPUT
elif [ "$FIX_SUCCESS" = "true" ]; then
echo "success=true" >> $GITHUB_OUTPUT
else
echo "success=false" >> $GITHUB_OUTPUT
fi

- name: Validate changes
id: validate
if: steps.final.outputs.success == 'true'
run: |
UNEXPECTED=""
for file in $(git diff --name-only origin/main..HEAD); do
case "$file" in
package.json|*/package.json|pnpm-lock.yaml|*/pnpm-lock.yaml|.npmrc|pnpm-workspace.yaml) ;;
src/*|test/*) ;;
*) UNEXPECTED="$UNEXPECTED $file" ;;
esac
done
if [ -n "$UNEXPECTED" ]; then
echo "::error::Unexpected files modified by Claude:$UNEXPECTED"
echo "valid=false" >> $GITHUB_OUTPUT
else
echo "valid=true" >> $GITHUB_OUTPUT
fi

- name: Check for changes
id: changes
run: |
Expand All @@ -123,13 +256,13 @@ jobs:
fi

- name: Push branch
if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
env:
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
run: git push origin "$BRANCH_NAME"

- name: Create Pull Request
if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
env:
GH_TOKEN: ${{ github.token }}
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
Expand Down Expand Up @@ -158,7 +291,7 @@ jobs:
--base main

- name: Add job summary
if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
env:
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
run: |
Expand All @@ -173,7 +306,11 @@ jobs:
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: claude-output-${{ github.run_id }}
path: claude-output.log
path: |
claude-update.log
claude-fix.log
build-output.log
test-output.log
retention-days: 7

- uses: SocketDev/socket-registry/.github/actions/cleanup-git-signing@6096b06b1790f411714c89c40f72aade2eeaab7c # main
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"clean": "node scripts/clean.mjs",
"cover": "node scripts/cover.mjs",
"fix": "node scripts/lint.mjs --fix",
"format": "oxfmt .",
"format": "oxfmt --write .",
"format:check": "oxfmt --check .",
"generate-sdk": "node scripts/generate-sdk.mjs",
"lint": "node scripts/lint.mjs",
Expand Down
5 changes: 2 additions & 3 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
settings:
# Wait 7 days (10080 minutes) before installing newly published packages.
minimumReleaseAge: 10080
# Wait 7 days (10080 minutes) before installing newly published packages.
minimumReleaseAge: 10080