ci(deps): bump github/codeql-action from 4 to 4.37.3 #243
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: Terrain AI Risk Review | |
| on: | |
| pull_request: | |
| branches: [main] | |
| concurrency: | |
| group: terrain-ai-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| jobs: | |
| ai-gate: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: go.mod | |
| - name: Build Terrain | |
| run: go build -o terrain ./cmd/terrain | |
| - name: Check AI surface coverage | |
| id: ai-check | |
| run: | | |
| # List AI surfaces and their coverage status | |
| ./terrain ai list --json > /tmp/ai-inventory.json | |
| TOTAL=$(jq '.summary.totalAISurfaces' /tmp/ai-inventory.json) | |
| UNCOVERED=$(jq '.summary.uncoveredSurfaces' /tmp/ai-inventory.json) | |
| SCENARIOS=$(jq '.summary.scenarios' /tmp/ai-inventory.json) | |
| echo "total_surfaces=$TOTAL" >> "$GITHUB_OUTPUT" | |
| echo "uncovered_surfaces=$UNCOVERED" >> "$GITHUB_OUTPUT" | |
| echo "scenarios=$SCENARIOS" >> "$GITHUB_OUTPUT" | |
| # Skip AI gate if no AI surfaces detected | |
| if [ "$TOTAL" = "0" ]; then | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| echo "No AI surfaces detected — skipping AI gate." | |
| exit 0 | |
| fi | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| - name: Run impact-scoped AI analysis | |
| id: ai-impact | |
| if: steps.ai-check.outputs.skip != 'true' | |
| run: | | |
| # Check which AI surfaces are affected by this change | |
| ./terrain pr --base "${{ github.event.pull_request.base.sha }}" --json > /tmp/pr-analysis.json | |
| # Extract AI risk review section | |
| AI_SELECTED=$(jq '.ai.selectedScenarios // 0' /tmp/pr-analysis.json) | |
| AI_TOTAL=$(jq '.ai.totalScenarios // 0' /tmp/pr-analysis.json) | |
| UNCOVERED=$(jq '[.ai.uncoveredContexts // [] | length] | add' /tmp/pr-analysis.json) | |
| BLOCKING=$(jq '[.ai.blockingSignals // [] | length] | add' /tmp/pr-analysis.json) | |
| echo "selected=$AI_SELECTED" >> "$GITHUB_OUTPUT" | |
| echo "blocking=$BLOCKING" >> "$GITHUB_OUTPUT" | |
| echo "uncovered=$UNCOVERED" >> "$GITHUB_OUTPUT" | |
| - name: Run selected eval scenarios | |
| id: ai-run | |
| if: steps.ai-check.outputs.skip != 'true' | |
| run: | | |
| set +e | |
| ./terrain ai run \ | |
| --base "${{ github.event.pull_request.base.sha }}" \ | |
| --timeout 15m \ | |
| --json > /tmp/ai-run.json 2> /tmp/ai-run.stderr | |
| STATUS=$? | |
| set -e | |
| if [ "$STATUS" -ne 0 ]; then | |
| cat /tmp/ai-run.stderr >&2 || true | |
| fi | |
| if jq -e '.decision.action' /tmp/ai-run.json >/dev/null 2>&1; then | |
| ACTION=$(jq -r '.decision.action' /tmp/ai-run.json) | |
| REASON=$(jq -r '.decision.reason // ""' /tmp/ai-run.json) | |
| else | |
| ACTION=block | |
| REASON=$(cat /tmp/ai-run.stderr 2>/dev/null || echo "terrain ai run failed before writing a decision artifact") | |
| fi | |
| { | |
| echo "action=$ACTION" | |
| echo "reason<<TERRAIN_AI_REASON" | |
| echo "$REASON" | |
| echo "TERRAIN_AI_REASON" | |
| echo "exit_code=$STATUS" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Generate AI risk review comment | |
| id: ai-comment | |
| if: steps.ai-check.outputs.skip != 'true' | |
| run: | | |
| { | |
| echo 'body<<TERRAIN_AI_EOF' | |
| echo '<!-- terrain-ai-risk-review -->' | |
| echo '### Terrain AI Risk Review' | |
| echo '' | |
| TOTAL="${{ steps.ai-check.outputs.total_surfaces }}" | |
| UNCOVERED="${{ steps.ai-check.outputs.uncovered_surfaces }}" | |
| SCENARIOS="${{ steps.ai-check.outputs.scenarios }}" | |
| SELECTED="${{ steps.ai-impact.outputs.selected }}" | |
| BLOCKING="${{ steps.ai-impact.outputs.blocking }}" | |
| ACTION="${{ steps.ai-run.outputs.action }}" | |
| echo "| Metric | Value |" | |
| echo "|--------|-------|" | |
| echo "| AI surfaces | $TOTAL |" | |
| echo "| Eval scenarios | $SCENARIOS |" | |
| echo "| Impacted scenarios | $SELECTED |" | |
| if [ "$UNCOVERED" != "0" ] && [ -n "$UNCOVERED" ]; then | |
| echo "| **Uncovered surfaces** | **$UNCOVERED** |" | |
| fi | |
| if [ "$BLOCKING" != "0" ] && [ -n "$BLOCKING" ]; then | |
| echo "| **Blocking signals** | **$BLOCKING** |" | |
| fi | |
| echo '' | |
| if [ "$ACTION" = "block" ]; then | |
| echo '**Decision: BLOCKED** — AI risk review found blocking signals.' | |
| echo '' | |
| echo "Reason: ${{ steps.ai-run.outputs.reason }}" | |
| elif [ "$ACTION" = "warn" ]; then | |
| echo '**Decision: WARNING** — review the AI risk findings before merging.' | |
| echo '' | |
| echo "Reason: ${{ steps.ai-run.outputs.reason }}" | |
| else | |
| echo '**Decision: PASS** — AI surfaces are covered.' | |
| fi | |
| echo 'TERRAIN_AI_EOF' | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Find existing AI comment | |
| if: steps.ai-check.outputs.skip != 'true' | |
| uses: peter-evans/find-comment@v4 | |
| id: find-comment | |
| with: | |
| issue-number: ${{ github.event.pull_request.number }} | |
| comment-author: 'github-actions[bot]' | |
| body-includes: '<!-- terrain-ai-risk-review -->' | |
| - name: Post or update AI comment | |
| if: steps.ai-check.outputs.skip != 'true' | |
| uses: peter-evans/create-or-update-comment@v5 | |
| with: | |
| issue-number: ${{ github.event.pull_request.number }} | |
| comment-id: ${{ steps.find-comment.outputs.comment-id }} | |
| edit-mode: replace | |
| body: ${{ steps.ai-comment.outputs.body }} | |
| - name: Fail if AI gate blocks | |
| if: steps.ai-run.outputs.action == 'block' | |
| run: | | |
| echo "::error::Terrain AI risk review blocked this PR: ${{ steps.ai-run.outputs.reason }}" | |
| exit 1 |