Skip to content

chore(release): v3.9.0 #602

chore(release): v3.9.0

chore(release): v3.9.0 #602

Workflow file for this run

name: Optimized CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
workflow_dispatch:
# On PRs: cancel stale runs when a new push arrives (old run is obsolete).
# On main: never cancel — every push must run to completion for accurate CI signal.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
actions: write
pull-requests: write
checks: write
# ─── Shared exclusions for journey test shards ───
# The original test:journeys:ci command excludes these directories:
# browser, browser-integration, mcp, sync, learning, domains, llm, n8n, *.e2e.test.ts
# By targeting specific subdirectories per shard, most exclusions are implicit.
# Only the "remaining" shard needs explicit excludes.
jobs:
# ─── Journey Tests: 4 parallel shards ───
# Previously a single 25-min job that timed out. Now 4 shards of ~5-8 min each.
journey-governance:
name: Journey — Governance
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '24', cache: 'npm' }
- run: npm ci
- run: npm run build
- name: Run governance integration tests
run: timeout 480 npx vitest run tests/integration/governance/ --reporter=verbose
env:
NODE_OPTIONS: '--max-old-space-size=1024'
journey-ruvector-coordination:
name: Journey — RuVector + Coordination
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '24', cache: 'npm' }
- run: npm ci
- run: npm run build
- name: Run ruvector + coordination integration tests
run: bash scripts/ci-vitest-run.sh tests/integration/ruvector/ tests/integration/coordination/ --reporter=verbose
env:
NODE_OPTIONS: '--max-old-space-size=1024'
journey-root:
name: Journey — Root-level
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '24', cache: 'npm' }
- run: npm ci
- run: npm run build
- name: Run root-level integration tests
run: |
# Run only test files directly in tests/integration/ (not in subdirectories)
FILES=$(find tests/integration -maxdepth 1 -name '*.test.ts' ! -name '*.e2e.test.ts' | sort)
if [ -n "$FILES" ]; then
bash scripts/ci-vitest-run.sh $FILES --reporter=verbose
else
echo "No root-level integration tests found"
fi
env:
NODE_OPTIONS: '--max-old-space-size=1024'
journey-remaining:
name: Journey — Remaining Subdirs
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '24', cache: 'npm' }
- run: npm ci
- run: npm run build
- name: Run remaining integration subdirectory tests
run: |
# Everything in tests/integration subdirs EXCEPT the ones covered by
# other shards (governance, ruvector, coordination) and the original
# exclusions (browser, mcp, sync, learning, domains, llm, n8n).
bash scripts/ci-vitest-run.sh \
tests/integration/adapters/ \
tests/integration/cli/ \
tests/integration/code-intelligence/ \
tests/integration/feedback/ \
tests/integration/migration/ \
tests/integration/opencode/ \
tests/integration/parsers/ \
tests/integration/planning/ \
tests/integration/protocols/ \
tests/integration/requirements-validation/ \
tests/integration/rl-suite/ \
tests/integration/rvf/ \
tests/integration/security/ \
tests/integration/templates/ \
tests/integration/test-execution/ \
tests/integration/test-generation/ \
tests/integration/validation/ \
--reporter=verbose
env:
NODE_OPTIONS: '--max-old-space-size=1024'
# ─── Contract + Code Intelligence: separate parallel jobs ───
contract-tests:
name: Contract Tests
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '24', cache: 'npm' }
- run: npm ci
- run: npm run build
- name: Run contract tests
run: |
if [ -d "tests/unit/domains/contract-testing" ] && [ "$(ls -A tests/unit/domains/contract-testing/*.test.ts 2>/dev/null)" ]; then
timeout 480 npm run test:contracts
else
echo "No contract tests found"
fi
env:
NODE_OPTIONS: '--max-old-space-size=512'
code-intelligence-tests:
name: Code Intelligence Tests
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '24', cache: 'npm' }
- run: npm ci
- run: npm run build
- name: Run code intelligence tests
run: timeout 480 npm run test:code-intelligence
env:
NODE_OPTIONS: '--max-old-space-size=768'
SKIP_INTEGRATION_TESTS: 'true'
# ─── Infrastructure tests (unchanged, already parallel) ───
infrastructure-tests:
name: Infrastructure Tests
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '24', cache: 'npm' }
- run: npm ci
- run: npm run build
- name: Run Infrastructure Tests
run: |
if [ -d "tests/infrastructure" ] && [ "$(ls -A tests/infrastructure/*.test.ts 2>/dev/null)" ]; then
timeout 480 npm run test:infrastructure
else
echo "No infrastructure tests found"
fi
env:
NODE_OPTIONS: '--max-old-space-size=768'
- name: Run Regression Tests
run: |
if [ -d "tests/regression" ] && [ "$(find tests/regression -name '*.test.ts' 2>/dev/null | head -1)" ]; then
timeout 480 npm run test:regression
else
echo "No regression tests found"
fi
env:
NODE_OPTIONS: '--max-old-space-size=512'
# ─── Postgres integration tests (unchanged) ───
integration-tests:
name: Postgres Integration Tests
runs-on: ubuntu-latest
timeout-minutes: 10
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_DB: aqe_test
POSTGRES_USER: aqe_test
POSTGRES_PASSWORD: aqe_test
ports:
- 15432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '24', cache: 'npm' }
- run: npm ci
- run: npm run build
- name: Run Postgres integration tests
run: timeout 480 npm run test:integration:pg
env:
POSTGRES_URL: postgresql://aqe_test:aqe_test@localhost:15432/aqe_test
NODE_OPTIONS: '--max-old-space-size=1024'
# ─── Performance gates (after journey shards complete) ───
performance-gates:
name: Performance Gates
runs-on: ubuntu-latest
needs: [journey-governance, journey-ruvector-coordination, journey-root, journey-remaining, contract-tests, code-intelligence-tests]
if: success()
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '24', cache: 'npm' }
- run: npm ci
- run: npm run build
- name: Run Performance Gates
id: perf-gates
run: |
timeout 480 npm run performance:gate > perf-report.md 2>&1 || PERF_EXIT=$?
echo "PERF_EXIT_CODE=${PERF_EXIT:-0}" >> $GITHUB_OUTPUT
cat perf-report.md
- uses: actions/upload-artifact@v4
if: always()
with:
name: performance-report
path: perf-report.md
retention-days: 30
- name: Check performance gate results
run: |
EXIT_CODE="${{ steps.perf-gates.outputs.PERF_EXIT_CODE }}"
if [ "$EXIT_CODE" -eq 0 ]; then
echo "All performance gates passed"
elif [ "$EXIT_CODE" -eq 2 ]; then
echo "Performance gates passed with warnings"
else
echo "Performance gates failed (exit code: $EXIT_CODE)"
if [ "$EXIT_CODE" -eq 1 ]; then
exit 1
fi
fi
# ─── Coverage analysis (PRs only, after journey shards) ───
coverage:
name: Coverage Analysis
runs-on: ubuntu-latest
needs: [journey-governance, journey-ruvector-coordination, journey-root, journey-remaining]
timeout-minutes: 10
if: success() && github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '24', cache: 'npm' }
- run: npm ci
- run: npm run build
- name: Run tests with coverage (excluding browser E2E)
run: bash scripts/ci-vitest-run.sh --coverage --reporter=verbose
env:
NODE_OPTIONS: '--max-old-space-size=1024'
CI: 'true'
- uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-report
path: coverage/
retention-days: 7
- name: Check coverage thresholds
run: |
if [ -f "coverage/coverage-summary.json" ]; then
COVERAGE=$(cat coverage/coverage-summary.json | jq '.total.lines.pct')
echo "Line coverage: ${COVERAGE}%"
if (( $(echo "$COVERAGE < 80" | bc -l) )); then
echo "Coverage below 80% threshold"
else
echo "Coverage meets threshold"
fi
fi
# ─── Test dashboard (summary) ───
dashboard:
name: Test Dashboard
runs-on: ubuntu-latest
needs: [journey-governance, journey-ruvector-coordination, journey-root, journey-remaining, contract-tests, code-intelligence-tests, infrastructure-tests]
if: always()
timeout-minutes: 2
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '24' }
- name: Generate Dashboard
run: node scripts/test-dashboard.cjs
- name: Generate Migration Metrics
run: |
echo "# CI Test Metrics" > ci-metrics.md
echo "" >> ci-metrics.md
echo "**Date**: $(date -u +"%Y-%m-%d %H:%M:%S UTC")" >> ci-metrics.md
echo "**Commit**: ${{ github.sha }}" >> ci-metrics.md
echo "" >> ci-metrics.md
total_files=$(find tests -name "*.test.ts" 2>/dev/null | wc -l || echo "0")
total_lines=$(find tests -name "*.test.ts" -exec wc -l {} + 2>/dev/null | tail -1 | awk '{print $1}' || echo "0")
large_files=$(find tests -name "*.test.ts" -exec wc -l {} \; 2>/dev/null | awk '$1 > 600' | wc -l || echo "0")
skipped=$(grep -r "describe.skip\|it.skip\|test.skip" tests --include="*.test.ts" 2>/dev/null | wc -l || echo "0")
echo "## Current State" >> ci-metrics.md
echo "- Total test files: $total_files (target: 50)" >> ci-metrics.md
echo "- Total lines: $total_lines (target: 40,000)" >> ci-metrics.md
echo "- Files > 600 lines: $large_files (target: 0)" >> ci-metrics.md
echo "- Skipped tests: $skipped (target: 0)" >> ci-metrics.md
echo "" >> ci-metrics.md
baseline_files=426
baseline_lines=208253
files_reduced=$((baseline_files - total_files))
lines_reduced=$((baseline_lines - total_lines))
echo "## Progress from Baseline" >> ci-metrics.md
echo "- Files reduced: $files_reduced (-$((files_reduced * 100 / baseline_files))%)" >> ci-metrics.md
echo "- Lines reduced: $lines_reduced (-$((lines_reduced * 100 / baseline_lines))%)" >> ci-metrics.md
cat ci-metrics.md
- uses: actions/upload-artifact@v4
with:
name: ci-metrics
path: ci-metrics.md
retention-days: 30
- name: Comment on PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const metrics = fs.readFileSync('ci-metrics.md', 'utf8');
const body = `## Test Suite Metrics
${metrics}
---
*Generated by Optimized CI*`;
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const botComment = comments.find(c =>
c.user?.type === 'Bot' && c.body?.includes('Test Suite Metrics')
);
if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body
});
}