Skip to content
Merged
Changes from 1 commit
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
229 changes: 160 additions & 69 deletions .github/workflows/dependabot-lockfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@ on:

permissions:
actions: read
checks: read
contents: write
pull-requests: write

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
fix-dependabot:
regen-lockfile:
runs-on: ubuntu-latest
timeout-minutes: 30
timeout-minutes: 10
outputs:
skip: ${{ steps.guard.outputs.skip }}
head_sha: ${{ steps.get-sha.outputs.sha }}

steps:
- name: Check if Dependabot PR
Expand Down Expand Up @@ -89,99 +97,182 @@ jobs:
echo "changed=true" >> "$GITHUB_OUTPUT"
fi

- name: Try building
- name: Get HEAD SHA
if: steps.guard.outputs.skip != 'true'
id: build
continue-on-error: true
run: |
set -o pipefail
pnpm install --frozen-lockfile
pnpm run build 2>&1 | tee /tmp/build-output.txt

- name: Try linting
if: steps.guard.outputs.skip != 'true' && steps.build.outcome == 'success'
id: lint
continue-on-error: true
run: |
set -o pipefail
pnpm exec eslint . 2>&1 | tee /tmp/lint-output.txt
id: get-sha
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"

- name: Try testing
if: steps.guard.outputs.skip != 'true' && steps.build.outcome == 'success'
id: test
continue-on-error: true
run: |
set -o pipefail
failed=0
pnpm test:unit 2>&1 | tee /tmp/test-output.txt || failed=1
pnpm --filter @ably/react-web-cli test 2>&1 | tee -a /tmp/test-output.txt || failed=1
exit $failed
fix-failures:
needs: regen-lockfile
if: needs.regen-lockfile.outputs.skip != 'true'
runs-on: ubuntu-latest
timeout-minutes: 45

- name: Check if fixes needed
if: steps.guard.outputs.skip != 'true'
id: needs-fix
run: |
if [[ "${{ steps.build.outcome }}" == "failure" || "${{ steps.lint.outcome }}" == "failure" || "${{ steps.test.outcome }}" == "failure" ]]; then
echo "needed=true" >> "$GITHUB_OUTPUT"
else
echo "needed=false" >> "$GITHUB_OUTPUT"
fi
steps:
- name: Generate App Token
id: generate-token
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.CI_APP_ID }}
private-key: ${{ secrets.CI_APP_PRIVATE_KEY }}

- name: Capture error output
if: steps.needs-fix.outputs.needed == 'true'
id: errors
- name: Wait for CI checks to complete
id: wait-for-checks
env:
GH_TOKEN: ${{ github.token }}
HEAD_SHA: ${{ needs.regen-lockfile.outputs.head_sha }}
REPO: ${{ github.repository }}
run: |
{
echo "build_output<<ENDOFOUTPUT"
if [ -f /tmp/build-output.txt ]; then
tail -n 200 /tmp/build-output.txt
else
echo "No build output captured"
POLL_INTERVAL=30
MAX_POLL_TIME=1500 # 25 minutes
INITIAL_WAIT=60

# Checks to skip: our own workflow jobs, Vercel, PR tooling
SKIP_PATTERN="^(regen-lockfile|fix-failures|Vercel|Vercel Preview Comments|claude-review|Generate PR Overview)$"

# Core CI checks we expect to see (at least 3 of 4 must appear)
EXPECTED_CHECKS=("test" "e2e-cli" "setup" "audit")
MIN_EXPECTED=3

echo "Waiting for CI checks on SHA: $HEAD_SHA"
echo "Initial wait of ${INITIAL_WAIT}s for checks to be queued..."
sleep "$INITIAL_WAIT"

start_time=$(date +%s)

while true; do
elapsed=$(( $(date +%s) - start_time ))
if [[ $elapsed -ge $MAX_POLL_TIME ]]; then
echo "::warning::Timed out after ${MAX_POLL_TIME}s waiting for checks"
break
fi

# Fetch all check runs for this SHA (handles pagination)
all_checks=$(gh api "repos/${REPO}/commits/${HEAD_SHA}/check-runs" \
--paginate \
--jq '.check_runs[] | {name: .name, status: .status, conclusion: .conclusion, details_url: .details_url}' \
2>/dev/null) || {
echo "::warning::API call failed (elapsed: ${elapsed}s), retrying in 10s..."
sleep 10
continue
}

# Filter out non-CI checks
ci_checks=$(echo "$all_checks" | jq -c "select(.name | test(\"${SKIP_PATTERN}\") | not)" 2>/dev/null)

if [[ -z "$ci_checks" ]]; then
echo "No CI checks found yet (elapsed: ${elapsed}s), waiting..."
sleep "$POLL_INTERVAL"
continue
fi
echo "ENDOFOUTPUT"
echo "lint_output<<ENDOFOUTPUT"
if [ -f /tmp/lint-output.txt ]; then
tail -n 200 /tmp/lint-output.txt
else
echo "Lint was not run"

# Count how many expected checks have appeared
appeared=0
for check_name in "${EXPECTED_CHECKS[@]}"; do
if echo "$ci_checks" | jq -e "select(.name == \"${check_name}\")" > /dev/null 2>&1; then
appeared=$((appeared + 1))
fi
done

if [[ $appeared -lt $MIN_EXPECTED && $elapsed -lt 300 ]]; then
echo "Only ${appeared}/${MIN_EXPECTED} expected checks appeared (elapsed: ${elapsed}s), waiting..."
sleep "$POLL_INTERVAL"
continue
fi
echo "ENDOFOUTPUT"
echo "test_output<<ENDOFOUTPUT"
if [ -f /tmp/test-output.txt ]; then
tail -n 200 /tmp/test-output.txt
else
echo "Tests were not run"

# Check if all CI checks are completed
total=$(echo "$ci_checks" | jq -s 'length')
pending=$(echo "$ci_checks" | jq -c 'select(.status != "completed")' | jq -s 'length')

echo "Check status: $((total - pending))/${total} completed (elapsed: ${elapsed}s)"

if [[ "$pending" -eq 0 && "$total" -gt 0 ]]; then
echo "All CI checks completed."
break
fi
echo "ENDOFOUTPUT"

sleep "$POLL_INTERVAL"
done

# Collect failures (exclude cancelled and skipped — only actual failures)
failed_checks=$(echo "$ci_checks" | jq -c 'select(.conclusion == "failure")' 2>/dev/null)
failed_count=0
if [[ -n "$failed_checks" ]]; then
failed_count=$(echo "$failed_checks" | jq -s 'length')
fi

echo "failed_count=${failed_count}" >> "$GITHUB_OUTPUT"

if [[ "$failed_count" -eq 0 ]]; then
echo "All checks passed! Nothing to fix."
exit 0
fi

echo "Found ${failed_count} failed check(s)"

# List failed check names
failed_names=$(echo "$failed_checks" | jq -r '.name' | sort)
echo "Failed: ${failed_names}"

# Extract unique workflow run IDs from details_url
# URL format: https://github.com/{owner}/{repo}/actions/runs/{run_id}/job/{job_id}
run_ids=$(echo "$failed_checks" | jq -r '.details_url' | sed -n 's|.*/runs/\([0-9]*\)/.*|\1|p' | sort -u)

# Fetch failed logs for each workflow run
failure_logs=""
for run_id in $run_ids; do
run_name=$(gh api "repos/${REPO}/actions/runs/${run_id}" --jq '.name' 2>/dev/null || echo "unknown")
echo "Fetching failed logs for: ${run_name} (run ${run_id})..."
logs=$(gh run view "$run_id" --repo "$REPO" --log-failed 2>&1 | tail -n 500) || logs="Failed to fetch logs for run ${run_id}"

failure_logs="${failure_logs}
=== Failed workflow: ${run_name} (run ${run_id}) ===
${logs}

"
done

# Write outputs
{
echo "failure_summary<<ENDOFFAILURES"
echo "Failed checks: $(echo "$failed_names" | tr '\n' ', ' | sed 's/, $//')"
echo "ENDOFFAILURES"
echo "failure_logs<<ENDOFLOGS"
echo "$failure_logs"
echo "ENDOFLOGS"
} >> "$GITHUB_OUTPUT"

- name: Fix issues with Claude
if: steps.needs-fix.outputs.needed == 'true'
- name: Checkout Dependabot branch
if: steps.wait-for-checks.outputs.failed_count > 0
uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.ref }}
token: ${{ steps.generate-token.outputs.token }}

- name: Fix failures with Claude
if: steps.wait-for-checks.outputs.failed_count > 0
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ steps.generate-token.outputs.token }}
allowed_bots: "dependabot[bot]"
prompt: |
This is a Dependabot PR that bumps dependencies. The lockfile has been
regenerated but the build, lint, or tests are failing.
regenerated, but CI checks are failing.

Read .claude/CLAUDE.md for project context.

## Errors
## Failed Checks

Build output (if failed):
${{ steps.errors.outputs.build_output }}
${{ steps.wait-for-checks.outputs.failure_summary }}

Lint output (if failed):
${{ steps.errors.outputs.lint_output }}
## Failure Logs

Test output (if failed):
${{ steps.errors.outputs.test_output }}
${{ steps.wait-for-checks.outputs.failure_logs }}

## Instructions

1. Diagnose why the build/lint/tests fail after the dependency bump
1. Analyze ALL the failure logs above to understand what broke
2. Make the MINIMUM changes needed to fix it — do not refactor unrelated code
3. Run `pnpm run build`, `pnpm exec eslint .`, `pnpm test:unit`, and `pnpm --filter @ably/react-web-cli test` to verify your fixes
4. Commit your changes with a descriptive message
Expand Down
Loading