Compare commits

...
Author SHA1 Message Date
Henry ParkandClaude Opus 4.6 12508253ba refactor(ci): PR-based Claude review instead of batch extraction [skip-regression-check]
Restructure staging-ci to create the promotion PR first, then let
claude-review.yml trigger on the PR via pull_request event. Claude
posts findings as native PR review comments (no JSON extraction).

- claude-review.yml: trigger on pull_request with staging-promotion
  label, uses claude-code-action in native PR review mode, Sonnet
  tags comments with [SEVERITY:CONFIDENCE] for downstream parsing
- staging-ci.yml: new create-promotion-pr job runs in parallel with
  tests/e2e, gate waits for Claude review check on PR, parses
  review comments to create issues and evaluate blocking findings
- create-labels.sh: add staging-promotion label

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
2026-03-08 15:35:55 -07:00
Henry ParkandClaude Opus 4.6 1ecc41e0ce fix(ci): lean Claude review — 5 turns, diff-only, no prompt embedding [skip-regression-check]
- Reduce --max-turns from 30 to 5 (enough for diff review)
- Remove full source file reading instruction (diff-only review)
- Don't embed diff in prompt — let Claude run git diff itself
- Extract results from execution_file output instead of /tmp/ file
- Reduces cost from ~$1.50 to ~$0.10-0.30 per review

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
2026-03-08 15:20:45 -07:00
Henry ParkandClaude Opus 4.6 454db90f84 fix(ci): add id-token:write to staging-ci parent workflow [skip-regression-check]
Called workflows inherit permissions from the parent. claude-review.yml
needs id-token:write for OIDC, so the parent staging-ci.yml must also
declare it.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
2026-03-08 15:04:20 -07:00
Henry ParkandClaude Opus 4.6 7e5a50b2de fix(ci): add id-token:write permission for claude-code-action OIDC [skip-regression-check]
The anthropics/claude-code-action@v1 requires OIDC token access for
authentication. Without id-token:write permission, the action fails
with "Could not fetch an OIDC token".

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
2026-03-08 15:03:06 -07:00
Henry ParkandClaude Opus 4.6 926f868a3e fix(ci): address PR review feedback on staging-ci [skip-regression-check]
- Remove broken skip-claude-gate label check (label always exists after
  bootstrap); keep only workflow_dispatch input override
- Require claude-review.result == 'success' in gate condition so a
  crashed review blocks promotion instead of silently passing
- Load review prompt into GITHUB_ENV instead of $(cat) in YAML with:
  block (YAML doesn't do shell substitution)
- version-check roll-up: check != 'success' instead of == 'failure'
  to catch cancelled/skipped states
- Force run: set diff_range to valid empty range when no new commits
- Align low-confidence label spacing in create-labels.sh

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
2026-03-08 14:56:33 -07:00
Henry ParkandClaude Opus 4.6 6556d7ebcd ci: staging branch with batched CI and Claude Code review [skip-regression-check]
Redesign CI to speed up PR feedback by deferring heavy tests to a
30-minute batch on the staging branch. PRs only run fmt + clippy +
regression-test-check. The batch CI runs tests, E2E, and a Claude
Code review with confidence-scored findings that gate promotion to
main.

- test.yml: replace PR/push triggers with workflow_call + dispatch
- e2e.yml: replace PR paths trigger with workflow_call
- coverage.yml: add workflow_call + dispatch triggers
- code_style.yml: absorb version-check job from test.yml
- staging-ci.yml: new orchestrator (30-min cron, change detection,
  tests + e2e + claude-review → gate → tag + promote-to-main)
- claude-review.yml: new reusable review workflow with 0-100
  confidence scoring (CRITICAL ≥80 blocks, ≥50 creates issues,
  <50 CRITICAL gets low-confidence label)
- create-labels.sh: add staging-ci-review, skip-claude-gate,
  low-confidence labels

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
2026-03-07 13:23:02 -08:00
7 changed files with 492 additions and 30 deletions
+4
View File
@@ -64,6 +64,10 @@ create "scope: dependencies" "90A4AE" "Dependency updates"
echo "==> Creating workflow labels..."
create "skip-regression-check" "9E9E9E" "Acknowledged: fix without regression test"
create "staging-ci-review" "D93F0B" "Auto-created by staging CI Claude Code review"
create "skip-claude-gate" "FBCA04" "Override: bypass Claude CRITICAL gate on staging CI"
create "low-confidence" "C5DEF5" "Claude review finding with <50 confidence"
create "staging-promotion" "0E8A16" "Auto-created staging→main promotion PR"
echo "==> Creating contributor labels..."
create "contributor: new" "FFF9C4" "First-time contributor"
+49
View File
@@ -0,0 +1,49 @@
name: Claude Code Review
on:
pull_request:
types: [opened, synchronize]
permissions:
contents: read
pull-requests: write
issues: write
id-token: write
jobs:
review:
name: Claude Code Review
if: contains(github.event.pull_request.labels.*.name, 'staging-promotion')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Run Claude Code review
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Review this PR for security vulnerabilities, bugs, and code quality issues.
Prefix EVERY review comment with a severity and confidence tag:
[SEVERITY:CONFIDENCE] where SEVERITY is CRITICAL/HIGH/MEDIUM/LOW
and CONFIDENCE is 0-100.
Example: [CRITICAL:92] This .unwrap() can panic in production when the config file is missing.
Severity guide:
- CRITICAL: security vulns, panics in prod (.unwrap/.expect), data exfiltration, race conditions
- HIGH: logic bugs, missing error handling, breaking API/schema changes
- MEDIUM: missing tests, unnecessary complexity, performance issues
- LOW: documentation gaps, naming suggestions
Confidence guide:
- 90-100: certain this is a real issue
- 70-89: very likely but needs human verification
- 50-69: possible issue, not fully sure of context
- 0-49: speculative, might be false positive
Only report real issues you're confident about. Be concise. No nitpicks.
claude_args: "--max-turns 5"
+18 -1
View File
@@ -44,15 +44,32 @@ jobs:
- name: Check lints
run: cargo clippy --all --benches --tests --examples ${{ matrix.flags }} -- -D warnings
version-check:
name: Version Bump Check
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check version bumps for changed extensions
env:
PR_LABELS: ${{ join(github.event.pull_request.labels.*.name, ',') }}
run: ./scripts/check-version-bumps.sh
# Roll-up job for branch protection
code-style:
name: Code Style (fmt + clippy)
runs-on: ubuntu-latest
if: always()
needs: [format, clippy]
needs: [format, clippy, version-check]
steps:
- run: |
if [[ "${{ needs.format.result }}" != "success" || "${{ needs.clippy.result }}" != "success" ]]; then
echo "One or more jobs failed"
exit 1
fi
if [[ "${{ needs.version-check.result }}" != "success" ]]; then
echo "Version bump check did not succeed (status: ${{ needs.version-check.result }})"
exit 1
fi
+3 -1
View File
@@ -1,7 +1,9 @@
name: Code Coverage
on:
push:
branches: [main]
branches: [main] # Runs when staging merges to main
workflow_call: # Optional: staging-ci can invoke
workflow_dispatch: # Manual trigger
permissions:
id-token: write
+1 -4
View File
@@ -2,11 +2,8 @@ name: E2E Tests
on:
schedule:
- cron: "0 6 * * 1" # Weekly Monday 6 AM UTC
workflow_call: # Called by staging-ci.yml
workflow_dispatch:
pull_request:
paths:
- "src/channels/web/**"
- "tests/e2e/**"
jobs:
# ── Step 1: compile once ──────────────────────────────────────────────────
+414
View File
@@ -0,0 +1,414 @@
name: Staging CI (Batched)
on:
schedule:
- cron: "*/30 * * * *" # Every 30 minutes
workflow_dispatch:
inputs:
force:
description: "Force run even if no new commits"
type: boolean
default: false
skip_claude_gate:
description: "Skip Claude review gate (bypass blocking findings)"
type: boolean
default: false
permissions:
contents: write
issues: write
pull-requests: write
concurrency:
group: staging-ci
cancel-in-progress: false # Let running suites finish
jobs:
# ── Check for new commits ──────────────────────────────────────
check-changes:
name: Check for new commits
runs-on: ubuntu-latest
outputs:
has_changes: ${{ steps.check.outputs.has_changes }}
current_head: ${{ steps.check.outputs.current_head }}
diff_range: ${{ steps.check.outputs.diff_range }}
steps:
- uses: actions/checkout@v6
with:
ref: staging
fetch-depth: 0
- name: Check for changes since last tested
id: check
run: |
CURRENT_HEAD=$(git rev-parse HEAD)
echo "current_head=${CURRENT_HEAD}" >> "$GITHUB_OUTPUT"
if git rev-parse staging-tested >/dev/null 2>&1; then
LAST_TESTED=$(git rev-parse staging-tested)
else
LAST_TESTED=""
fi
DIFF_RANGE=""
if [ -n "$LAST_TESTED" ] && [ "$LAST_TESTED" = "$CURRENT_HEAD" ]; then
echo "No new commits since last tested (${CURRENT_HEAD})"
HAS_CHANGES=false
else
HAS_CHANGES=true
if [ -n "$LAST_TESTED" ]; then
COMMIT_COUNT=$(git rev-list --count "${LAST_TESTED}..HEAD")
echo "Found ${COMMIT_COUNT} new commit(s) since last tested"
DIFF_RANGE="${LAST_TESTED}..${CURRENT_HEAD}"
else
git fetch origin main
MERGE_BASE=$(git merge-base origin/main HEAD)
echo "First run -- reviewing from merge-base ${MERGE_BASE}"
DIFF_RANGE="${MERGE_BASE}..${CURRENT_HEAD}"
fi
fi
# Force override from workflow_dispatch
if [ "${{ inputs.force }}" = "true" ]; then
echo "Force run requested"
HAS_CHANGES=true
if [ -z "$DIFF_RANGE" ]; then
DIFF_RANGE="${CURRENT_HEAD}..${CURRENT_HEAD}"
fi
fi
echo "has_changes=${HAS_CHANGES}" >> "$GITHUB_OUTPUT"
echo "diff_range=${DIFF_RANGE}" >> "$GITHUB_OUTPUT"
# ── Run full test suite ──────────────────────────────────────────
tests:
name: Test Suite
needs: check-changes
if: needs.check-changes.outputs.has_changes == 'true'
uses: ./.github/workflows/test.yml
# ── Run E2E browser tests ────────────────────────────────────────
e2e:
name: E2E Browser Tests
needs: check-changes
if: needs.check-changes.outputs.has_changes == 'true'
uses: ./.github/workflows/e2e.yml
# ── Create promotion PR (triggers claude-review.yml on the PR) ──
create-promotion-pr:
name: Create Promotion PR
needs: check-changes
if: needs.check-changes.outputs.has_changes == 'true'
runs-on: ubuntu-latest
outputs:
pr_number: ${{ steps.create-pr.outputs.pr_number }}
steps:
- uses: actions/checkout@v6
with:
ref: staging
fetch-depth: 0
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.GH_RELEASES_MANAGER_APP_ID }}
private-key: ${{ secrets.GH_RELEASES_MANAGER_APP_PRIVATE_KEY }}
- name: Check if staging is ahead of main
id: ahead-check
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
git fetch origin main
AHEAD=$(git rev-list --count origin/main..origin/staging)
echo "commits_ahead=${AHEAD}" >> "$GITHUB_OUTPUT"
if [ "$AHEAD" -eq 0 ]; then
echo "Staging is not ahead of main. Nothing to promote."
else
echo "Staging is ${AHEAD} commits ahead of main."
fi
- name: Close stale promotion PRs
if: steps.ahead-check.outputs.commits_ahead != '0'
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
# Close any existing staging->main PRs to avoid duplicates
EXISTING=$(gh pr list --base main --head staging --state open --json number -q '.[].number')
for PR in $EXISTING; do
echo "Closing stale promotion PR #${PR}"
gh pr close "$PR" --comment "Superseded by new staging-ci batch run"
done
- name: Create promotion PR
id: create-pr
if: steps.ahead-check.outputs.commits_ahead != '0'
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
RANGE="${{ needs.check-changes.outputs.diff_range }}"
TIMESTAMP=$(date -u +"%Y-%m-%d %H:%M UTC")
PR_URL=$(gh pr create \
--base main \
--head staging \
--title "chore: promote staging to main (${TIMESTAMP})" \
--body "## Auto-promotion from staging CI
**Batch range:** \`${RANGE}\`
**Triggered by:** Staging CI batch at ${TIMESTAMP}
Waiting for gates:
- Tests: pending
- E2E: pending
- Claude Code review: pending (will post comments on this PR)
---
*Auto-created by staging-ci workflow*" \
--label "staging-promotion")
PR_NUM=$(echo "$PR_URL" | grep -oE '[0-9]+$')
echo "pr_number=${PR_NUM}" >> "$GITHUB_OUTPUT"
echo "Created promotion PR #${PR_NUM}"
# ── Gate: wait for all checks, process findings, merge or block ──
gate:
name: Staging Gate
needs: [check-changes, tests, e2e, create-promotion-pr]
if: >
always() &&
needs.check-changes.outputs.has_changes == 'true' &&
needs.tests.result == 'success' &&
needs.e2e.result == 'success' &&
needs.create-promotion-pr.result == 'success'
runs-on: ubuntu-latest
outputs:
gate_passed: ${{ steps.evaluate.outputs.passed }}
steps:
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.GH_RELEASES_MANAGER_APP_ID }}
private-key: ${{ secrets.GH_RELEASES_MANAGER_APP_PRIVATE_KEY }}
- name: Wait for Claude review on PR
id: wait-review
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
PR_NUMBER: ${{ needs.create-promotion-pr.outputs.pr_number }}
run: |
if [ -z "$PR_NUMBER" ]; then
echo "No PR number — skipping Claude review wait"
echo "review_done=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "Waiting for Claude Code Review check on PR #${PR_NUMBER}..."
TIMEOUT=600 # 10 minutes
ELAPSED=0
INTERVAL=15
while [ "$ELAPSED" -lt "$TIMEOUT" ]; do
# Check if the claude-review check has completed
STATUS=$(gh pr checks "$PR_NUMBER" --json name,state \
--jq '.[] | select(.name == "Claude Code Review") | .state' 2>/dev/null || echo "PENDING")
if [ "$STATUS" = "SUCCESS" ] || [ "$STATUS" = "FAILURE" ]; then
echo "Claude review completed with status: ${STATUS}"
echo "review_status=${STATUS}" >> "$GITHUB_OUTPUT"
echo "review_done=true" >> "$GITHUB_OUTPUT"
break
fi
echo "Claude review status: ${STATUS} (${ELAPSED}s elapsed)"
sleep "$INTERVAL"
ELAPSED=$((ELAPSED + INTERVAL))
done
if [ "$ELAPSED" -ge "$TIMEOUT" ]; then
echo "::warning::Claude review timed out after ${TIMEOUT}s"
echo "review_status=TIMEOUT" >> "$GITHUB_OUTPUT"
echo "review_done=false" >> "$GITHUB_OUTPUT"
fi
- name: Process Claude review comments and create issues
id: process-findings
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
PR_NUMBER: ${{ needs.create-promotion-pr.outputs.pr_number }}
REPO: ${{ github.repository }}
run: |
HAS_BLOCKING=false
ISSUES_CREATED=0
if [ -z "$PR_NUMBER" ]; then
echo "No PR — skipping finding processing"
echo "has_blocking=false" >> "$GITHUB_OUTPUT"
exit 0
fi
# Get all review comments from Claude on this PR
COMMENTS=$(gh api "repos/${REPO}/pulls/${PR_NUMBER}/comments" \
--jq '[.[] | select(.user.login == "claude[bot]" or .user.type == "Bot") | {body: .body, path: .path, line: .line, url: .html_url}]' 2>/dev/null || echo "[]")
COMMENT_COUNT=$(echo "$COMMENTS" | jq 'length')
echo "Found ${COMMENT_COUNT} Claude review comment(s)"
# Also check PR review body comments
REVIEW_COMMENTS=$(gh api "repos/${REPO}/pulls/${PR_NUMBER}/reviews" \
--jq '[.[] | select(.user.login == "claude[bot]" or .user.type == "Bot") | {body: .body, url: .html_url}]' 2>/dev/null || echo "[]")
# Combine all comments
ALL_COMMENTS=$(echo "$COMMENTS $REVIEW_COMMENTS" | jq -s 'add // []')
# Parse [SEVERITY:CONFIDENCE] tags from each comment
echo "$ALL_COMMENTS" | jq -c '.[]' | while read -r comment; do
BODY=$(echo "$comment" | jq -r '.body // ""')
URL=$(echo "$comment" | jq -r '.url // ""')
FILE=$(echo "$comment" | jq -r '.path // "unknown"')
LINE=$(echo "$comment" | jq -r '.line // 0')
# Extract [SEVERITY:CONFIDENCE] tag
TAG=$(echo "$BODY" | grep -oE '\[(CRITICAL|HIGH|MEDIUM|LOW):[0-9]+\]' | head -1 || true)
if [ -z "$TAG" ]; then
continue
fi
SEVERITY=$(echo "$TAG" | sed 's/\[\(.*\):\(.*\)\]/\1/')
CONFIDENCE=$(echo "$TAG" | sed 's/\[\(.*\):\(.*\)\]/\2/')
# Strip tag from body for issue description
DESC=$(echo "$BODY" | sed "s/\[${SEVERITY}:${CONFIDENCE}\] *//" | head -5)
echo "Found: [${SEVERITY}:${CONFIDENCE}] in ${FILE}:${LINE}"
# Determine if this should create an issue (confidence matrix)
CREATE_ISSUE=false
case "$SEVERITY" in
CRITICAL) CREATE_ISSUE=true ;; # Always
HIGH) [ "$CONFIDENCE" -ge 50 ] && CREATE_ISSUE=true ;;
MEDIUM) [ "$CONFIDENCE" -ge 80 ] && CREATE_ISSUE=true ;;
LOW) [ "$CONFIDENCE" -ge 80 ] && CREATE_ISSUE=true ;;
esac
# Check if blocking (CRITICAL ≥80)
if [ "$SEVERITY" = "CRITICAL" ] && [ "$CONFIDENCE" -ge 80 ]; then
HAS_BLOCKING=true
fi
if [ "$CREATE_ISSUE" = "true" ]; then
# Determine labels
case "$SEVERITY" in
CRITICAL) LABELS="bug,risk: high,staging-ci-review" ;;
HIGH) LABELS="bug,risk: medium,staging-ci-review" ;;
MEDIUM) LABELS="risk: medium,staging-ci-review" ;;
LOW) LABELS="risk: low,staging-ci-review" ;;
esac
if [ "$SEVERITY" = "CRITICAL" ] && [ "$CONFIDENCE" -lt 50 ]; then
LABELS="${LABELS},low-confidence"
fi
TITLE=$(echo "$DESC" | head -1 | cut -c1-80)
{
echo "## ${SEVERITY} Issue Found by Staging CI Review"
echo ""
echo "**Severity:** ${SEVERITY}"
echo "**Confidence:** ${CONFIDENCE}/100"
echo "**File:** \`${FILE}:${LINE}\`"
echo "**PR comment:** ${URL}"
echo ""
echo "### Description"
echo "$DESC"
echo ""
echo "---"
echo "*Auto-created by staging-ci Claude Code review*"
} > /tmp/issue-body.md
gh issue create \
--title "[${SEVERITY}] ${TITLE}" \
--body-file /tmp/issue-body.md \
--label "${LABELS}" || echo "::warning::Failed to create issue for ${SEVERITY} finding"
ISSUES_CREATED=$((ISSUES_CREATED + 1))
fi
done
echo "Created ${ISSUES_CREATED} issues"
echo "has_blocking=${HAS_BLOCKING}" >> "$GITHUB_OUTPUT"
- name: Evaluate gate
id: evaluate
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
PR_NUMBER: ${{ needs.create-promotion-pr.outputs.pr_number }}
run: |
HAS_BLOCKING="${{ steps.process-findings.outputs.has_blocking }}"
SKIP_INPUT="${{ inputs.skip_claude_gate }}"
if [ "$HAS_BLOCKING" = "true" ]; then
echo "::warning::Claude review found blocking issues (CRITICAL ≥80 confidence)"
if [ "$SKIP_INPUT" = "true" ]; then
echo "::warning::Gate overridden by skip_claude_gate workflow input"
echo "passed=true" >> "$GITHUB_OUTPUT"
else
echo "::error::Blocking promotion due to CRITICAL findings (≥80 confidence)"
echo "::error::PR #${PR_NUMBER} left open with review comments"
echo "passed=false" >> "$GITHUB_OUTPUT"
exit 1
fi
else
echo "No blocking findings. Gate passed."
echo "passed=true" >> "$GITHUB_OUTPUT"
fi
# Merge the promotion PR
if [ -n "$PR_NUMBER" ]; then
echo "Merging promotion PR #${PR_NUMBER}"
gh pr merge "$PR_NUMBER" --merge --auto || echo "::warning::Auto-merge failed for PR #${PR_NUMBER}"
fi
# ── Update tested tag on success ─────────────────────────────────
update-tag:
name: Update staging-tested tag
needs: [check-changes, gate]
if: >
always() &&
needs.check-changes.outputs.has_changes == 'true' &&
needs.gate.result == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: staging
fetch-depth: 1
- name: Update staging-tested tag
run: |
git tag -f staging-tested "${{ needs.check-changes.outputs.current_head }}"
git push origin staging-tested --force
echo "Updated staging-tested tag to ${{ needs.check-changes.outputs.current_head }}"
# ── Report ───────────────────────────────────────────────────────
report:
name: Staging CI Summary
needs: [check-changes, tests, e2e, create-promotion-pr, gate, update-tag]
if: always() && needs.check-changes.outputs.has_changes == 'true'
runs-on: ubuntu-latest
steps:
- name: Summary
run: |
echo "## Staging CI Batch Results" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "| Check | Result |" >> "$GITHUB_STEP_SUMMARY"
echo "|-------|--------|" >> "$GITHUB_STEP_SUMMARY"
echo "| Tests | ${{ needs.tests.result }} |" >> "$GITHUB_STEP_SUMMARY"
echo "| E2E | ${{ needs.e2e.result }} |" >> "$GITHUB_STEP_SUMMARY"
echo "| Promotion PR | ${{ needs.create-promotion-pr.result }} |" >> "$GITHUB_STEP_SUMMARY"
echo "| Gate | ${{ needs.gate.result }} |" >> "$GITHUB_STEP_SUMMARY"
echo "| Tag Updated | ${{ needs.update-tag.result }} |" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "Range: ${{ needs.check-changes.outputs.diff_range }}" >> "$GITHUB_STEP_SUMMARY"
PR_NUM="${{ needs.create-promotion-pr.outputs.pr_number }}"
if [ -n "$PR_NUM" ]; then
echo "Promotion PR: #${PR_NUM}" >> "$GITHUB_STEP_SUMMARY"
fi
+3 -24
View File
@@ -1,9 +1,7 @@
name: Run Tests
on:
pull_request:
push:
branches:
- main
workflow_call: # Called by staging-ci.yml
workflow_dispatch: # Manual escape hatch
jobs:
tests:
@@ -81,34 +79,15 @@ jobs:
- name: Build Docker image
run: docker build -t ironclaw-test:ci .
version-check:
name: Version Bump Check
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check version bumps for changed extensions
env:
PR_LABELS: ${{ join(github.event.pull_request.labels.*.name, ',') }}
run: ./scripts/check-version-bumps.sh
# Roll-up job for branch protection
run-tests:
name: Run Tests
runs-on: ubuntu-latest
if: always()
needs: [tests, telegram-tests, wasm-wit-compat, docker-build, version-check]
needs: [tests, telegram-tests, wasm-wit-compat, docker-build]
steps:
- run: |
if [[ "${{ needs.tests.result }}" != "success" || "${{ needs.telegram-tests.result }}" != "success" || "${{ needs.wasm-wit-compat.result }}" != "success" || "${{ needs.docker-build.result }}" != "success" ]]; then
echo "One or more jobs failed"
exit 1
fi
# version-check only runs on PRs, so skip/success are both acceptable
if [[ "${{ needs.version-check.result }}" == "failure" ]]; then
echo "Version bump check failed"
exit 1
fi