Skip to content

🔄 Weekly Dependency Update #10

🔄 Weekly Dependency Update

🔄 Weekly Dependency Update #10

Workflow file for this run

name: 🔄 Weekly Dependency Update
on:
schedule:
# Run weekly on Monday at 9 AM UTC
- cron: '0 9 * * 1'
workflow_dispatch:
inputs:
dry-run:
description: 'Check for updates without creating PR'
required: false
type: boolean
default: false
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
jobs:
check-updates:
name: Check for dependency updates
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
outputs:
has-updates: ${{ steps.check.outputs.has-updates }}
steps:
- uses: SocketDev/socket-registry/.github/actions/setup-and-install@3362af95fadd1e325cb48e9ad6daff21c112bd72 # main
- name: Check for npm updates
id: check
shell: bash
run: |
echo "Checking for npm package updates..."
HAS_UPDATES=false
NPM_UPDATES=$(pnpm outdated 2>/dev/null || true)
if [ -n "$NPM_UPDATES" ] && ! echo "$NPM_UPDATES" | grep -q "No outdated"; then
echo "npm packages have updates available"
HAS_UPDATES=true
fi
echo "has-updates=$HAS_UPDATES" >> $GITHUB_OUTPUT
apply-updates:
name: Apply updates with Claude Code
needs: check-updates
if: needs.check-updates.outputs.has-updates == 'true' && inputs.dry-run != true
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: write
pull-requests: write
steps:
- uses: SocketDev/socket-registry/.github/actions/setup-and-install@3362af95fadd1e325cb48e9ad6daff21c112bd72 # main
- name: Create update branch
id: branch
env:
GH_TOKEN: ${{ github.token }}
run: |
BRANCH_NAME="weekly-update-$(date +%Y%m%d)"
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git"
git checkout -b "$BRANCH_NAME"
echo "branch=$BRANCH_NAME" >> $GITHUB_OUTPUT
- uses: SocketDev/socket-registry/.github/actions/setup-git-signing@3362af95fadd1e325cb48e9ad6daff21c112bd72 # main
with:
gpg-private-key: ${{ secrets.BOT_GPG_PRIVATE_KEY }}
- name: Update dependencies (haiku — fast, cheap)
id: update
timeout-minutes: 10
shell: bash
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_ACTIONS: 'true'
run: |
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
claude --print \
--model haiku \
--max-turns 15 \
--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" \
"$(cat <<'PROMPT'
/updating
<context>
You are an automated CI agent in a weekly dependency update workflow.
Git is configured with GPG signing. A branch has been created for you.
</context>
<instructions>
Update all dependencies to their latest versions.
Create one atomic commit per dependency update with a conventional commit message.
Leave all changes local — the workflow handles pushing and PR creation.
Do not run builds or tests — the next step handles that.
</instructions>
<success_criteria>
Each updated dependency has its own commit.
The lockfile is consistent with package.json changes.
No uncommitted changes remain in the working tree.
</success_criteria>
PROMPT
)" \
2>&1 | tee claude-output.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: Run tests
id: tests
if: steps.update.outputs.success == 'true'
shell: bash
run: |
set +e
pnpm run build 2>&1 | tee build-output.log
BUILD_EXIT=${PIPESTATUS[0]}
if [ "$BUILD_EXIT" -ne 0 ]; then
echo "tests-passed=false" >> $GITHUB_OUTPUT
exit 0
fi
pnpm test 2>&1 | tee test-output.log
TEST_EXIT=${PIPESTATUS[0]}
set -e
if [ "$TEST_EXIT" -eq 0 ]; then
echo "tests-passed=true" >> $GITHUB_OUTPUT
else
echo "tests-passed=false" >> $GITHUB_OUTPUT
fi
- name: Fix test failures (sonnet — smarter, escalated)
id: claude
if: steps.update.outputs.success == 'true' && steps.tests.outputs.tests-passed == 'false'
timeout-minutes: 15
shell: bash
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_ACTIONS: 'true'
run: |
# Collect failure context for the agent.
FAILURE_LOG=""
if [ -f build-output.log ]; then
FAILURE_LOG+="Build output (last 50 lines):"$'\n'
FAILURE_LOG+="$(tail -50 build-output.log)"$'\n\n'
fi
if [ -f test-output.log ]; then
FAILURE_LOG+="Test output (last 100 lines):"$'\n'
FAILURE_LOG+="$(tail -100 test-output.log)"$'\n'
fi
# Write prompt to file to avoid shell expansion of FAILURE_LOG.
PROMPT_FILE=$(mktemp)
{
printf '%s\n' 'Build or tests failed after dependency updates. Fix them.'
printf '\n<failure_output>\n'
printf '%s' "$FAILURE_LOG"
printf '\n</failure_output>\n\n'
printf '%s\n' '<instructions>'
printf '%s\n' 'Diagnose the failures from the output above.'
printf '%s\n' 'Fix the code — do not revert the dependency updates.'
printf '%s\n' 'Run the tests again to verify your fix works.'
printf '%s\n' 'Create a commit for each fix with a conventional commit message.'
printf '%s\n' '</instructions>'
} > "$PROMPT_FILE"
set +e
claude --print \
--model sonnet \
--max-turns 25 \
--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" \
"$(cat "$PROMPT_FILE")" \
2>&1 | tee -a claude-output.log
CLAUDE_EXIT=${PIPESTATUS[0]}
set -e
rm -f "$PROMPT_FILE"
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_PASSED: ${{ steps.tests.outputs.tests-passed }}
FIX_SUCCESS: ${{ steps.claude.outputs.success }}
run: |
if [ "$UPDATE_SUCCESS" = "true" ] && { [ "$TESTS_PASSED" = "true" ] || [ "$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: |
# Allow dependency files + source/test fixes from the fix step.
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/*|*.ts|*.mts|*.js|*.mjs) ;;
*) UNEXPECTED="$UNEXPECTED $file" ;;
esac
done
if [ -n "$UNEXPECTED" ]; then
echo "::error::Unexpected files modified:$UNEXPECTED"
echo "valid=false" >> $GITHUB_OUTPUT
else
echo "valid=true" >> $GITHUB_OUTPUT
fi
- name: Check for changes
id: changes
run: |
if [ -n "$(git status --porcelain)" ] || [ "$(git rev-list --count HEAD ^origin/main)" -gt 0 ]; then
echo "has-changes=true" >> $GITHUB_OUTPUT
else
echo "has-changes=false" >> $GITHUB_OUTPUT
fi
- name: Push branch
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.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 }}
run: |
COMMITS=$(git log --oneline origin/main..HEAD)
COMMIT_COUNT=$(git rev-list --count origin/main..HEAD)
PR_BODY="## Weekly Dependency Update"$'\n\n'
PR_BODY+="Automated weekly update of npm packages."$'\n\n'
PR_BODY+="---"$'\n\n'
PR_BODY+="### Commits (${COMMIT_COUNT})"$'\n\n'
PR_BODY+="<details>"$'\n'
PR_BODY+="<summary>View commit history</summary>"$'\n\n'
PR_BODY+="\`\`\`"$'\n'
PR_BODY+="${COMMITS}"$'\n'
PR_BODY+="\`\`\`"$'\n\n'
PR_BODY+="</details>"$'\n\n'
PR_BODY+="---"$'\n\n'
PR_BODY+="<sub>Generated by [weekly-update.yml](.github/workflows/weekly-update.yml)</sub>"
gh pr create \
--title "chore(deps): weekly dependency update ($(date +%Y-%m-%d))" \
--body "$PR_BODY" \
--draft \
--head "$BRANCH_NAME" \
--base main
- name: Add job summary
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: |
COMMIT_COUNT=$(git rev-list --count origin/main..HEAD)
echo "## Weekly Update Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Branch:** \`${BRANCH_NAME}\`" >> $GITHUB_STEP_SUMMARY
echo "**Commits:** ${COMMIT_COUNT}" >> $GITHUB_STEP_SUMMARY
- name: Upload logs
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: weekly-update-logs-${{ github.run_id }}
path: |
claude-output.log
build-output.log
test-output.log
retention-days: 7
- uses: SocketDev/socket-registry/.github/actions/cleanup-git-signing@3362af95fadd1e325cb48e9ad6daff21c112bd72 # main
if: always()
notify:
name: Notify results
needs: [check-updates, apply-updates]
if: always()
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Report status
env:
HAS_UPDATES: ${{ needs.check-updates.outputs.has-updates }}
DRY_RUN: ${{ inputs.dry-run }}
run: |
if [ "$HAS_UPDATES" = "true" ]; then
if [ "$DRY_RUN" = "true" ]; then
echo "Updates available (dry-run mode - no PR created)"
else
echo "Weekly update workflow completed"
echo "Check the PRs tab for the automated update PR"
fi
else
echo "All dependencies are up to date - no action needed!"
fi