Fix Drift #39
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Fix Drift | |
| on: | |
| workflow_dispatch: | |
| workflow_run: | |
| workflows: ["Drift Tests"] | |
| types: [completed] | |
| branches: [main] | |
| concurrency: | |
| group: drift-fix | |
| cancel-in-progress: false | |
| jobs: | |
| fix: | |
| if: >- | |
| github.event_name == 'workflow_dispatch' || | |
| github.event.workflow_run.conclusion == 'failure' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| issues: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| # Step 0: Configure git identity and create fix branch | |
| - name: Configure git | |
| run: | | |
| git config user.name "aimock-drift-bot" | |
| git config user.email "drift-bot@copilotkit.ai" | |
| git checkout -B fix/drift-$(date +%Y-%m-%d)-${{ github.run_id }} | |
| # Step 1: Detect drift and produce report | |
| - name: Collect drift report | |
| id: detect | |
| run: | | |
| set +e | |
| npx tsx scripts/drift-report-collector.ts | |
| EXIT_CODE=$? | |
| set -e | |
| echo "exit_code=$EXIT_CODE" >> $GITHUB_OUTPUT | |
| if [ "$EXIT_CODE" -eq 2 ]; then | |
| : # critical drift found, continue | |
| elif [ "$EXIT_CODE" -ne 0 ]; then | |
| echo "::error::Collector script crashed with exit code $EXIT_CODE" | |
| exit $EXIT_CODE | |
| fi | |
| env: | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} | |
| # Always upload the report as an artifact | |
| - name: Upload drift report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: drift-report | |
| path: drift-report.json | |
| if-no-files-found: warn | |
| retention-days: 30 | |
| # Step 2: Exit if no critical drift | |
| - name: Check for critical diffs | |
| id: check | |
| env: | |
| DETECT_EXIT_CODE: ${{ steps.detect.outputs.exit_code }} | |
| run: | | |
| if [ "$DETECT_EXIT_CODE" = "2" ]; then | |
| echo "skip=false" >> $GITHUB_OUTPUT | |
| echo "Critical drift detected" | |
| else | |
| echo "skip=true" >> $GITHUB_OUTPUT | |
| echo "No critical drift detected (exit code: $DETECT_EXIT_CODE) — skipping fix" | |
| fi | |
| # Step 3: Invoke Claude Code to fix | |
| - name: Auto-fix drift | |
| if: steps.check.outputs.skip != 'true' | |
| run: npx tsx scripts/fix-drift.ts | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} | |
| # Upload Claude Code output for debugging | |
| - name: Upload Claude Code logs | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: claude-code-output | |
| path: claude-code-output.log | |
| if-no-files-found: warn | |
| retention-days: 30 | |
| # Step 4: Verify fix independently | |
| - name: Verify conformance | |
| if: steps.check.outputs.skip != 'true' | |
| run: pnpm test | |
| - name: Verify drift resolved | |
| if: steps.check.outputs.skip != 'true' | |
| run: pnpm test:drift | |
| env: | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} | |
| # Step 5: Create PR on success | |
| - name: Create PR | |
| id: pr | |
| if: success() && steps.check.outputs.skip != 'true' | |
| run: | | |
| npx tsx scripts/fix-drift.ts --create-pr 2>&1 | tee /tmp/pr-output.txt | |
| PR_URL=$(grep -oE 'https://github.com/[^ ]+/pull/[0-9]+' /tmp/pr-output.txt | head -1) | |
| if [ -z "$PR_URL" ]; then echo "No PR URL found"; exit 1; fi | |
| echo "url=$PR_URL" >> $GITHUB_OUTPUT | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Step 5.5: Auto-merge the drift fix PR | |
| - name: Auto-merge PR | |
| if: success() && steps.pr.outputs.url != '' | |
| run: | | |
| PR_URL="${{ steps.pr.outputs.url }}" | |
| gh pr merge "$PR_URL" --merge --auto | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Step 6: Open issue on failure | |
| - name: Create issue on failure | |
| if: failure() && steps.check.outputs.skip != 'true' | |
| run: npx tsx scripts/fix-drift.ts --create-issue | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Step 7: Slack notification on successful fix | |
| - name: Notify Slack on fix success | |
| if: success() && steps.pr.outputs.url != '' | |
| run: | | |
| if [ -z "$SLACK_WEBHOOK" ]; then echo "SLACK_WEBHOOK not set, skipping"; exit 0; fi | |
| curl -s -X POST "$SLACK_WEBHOOK" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{\"text\":\"✅ *Drift auto-fixed and merged*\nPR: ${{ steps.pr.outputs.url }}\nRun: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}" | |
| env: | |
| SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} | |
| # Step 8: Slack notification on fix failure | |
| - name: Notify Slack on fix failure | |
| if: failure() && steps.check.outputs.skip != 'true' | |
| run: | | |
| if [ -z "$SLACK_WEBHOOK" ]; then echo "SLACK_WEBHOOK not set, skipping"; exit 0; fi | |
| curl -s -X POST "$SLACK_WEBHOOK" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{\"text\":\"❌ *Drift auto-fix failed* — issue created\nRun: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}" | |
| env: | |
| SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} |