Files
rust_browser/_bmad/tea/workflows/testarch/ci/github-actions-template.yaml
Zachary D. Rowitsch 931f17b70e
All checks were successful
ci / fast (linux) (push) Successful in 6m46s
Add BMAD framework, planning artifacts, and architecture decision document
Install BMAD workflow framework with agent commands and templates.
Create product brief, PRD, project context, and architecture decision
document covering networking/persistence strategy, JS engine evolution
path, threading model, web_api scaling, system integration, and
tab/process model. Add generated project documentation (architecture
overview, component inventory, development guide, source tree analysis).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:13:06 -04:00

329 lines
10 KiB
YAML

# GitHub Actions CI/CD Pipeline for Test Execution
# Generated by BMad TEA Agent - Test Architect Module
# Optimized for: Parallel Sharding, Burn-In Loop
# Stack: {test_stack_type} | Framework: {test_framework}
#
# Variables to customize per project:
# INSTALL_CMD - dependency install command (e.g., npm ci, pnpm install --frozen-lockfile, yarn --frozen-lockfile)
# TEST_CMD - main test command (e.g., npm run test:e2e, npm test, npx vitest)
# LINT_CMD - lint command (e.g., npm run lint)
# BROWSER_INSTALL - browser install command (frontend/fullstack only; omit for backend)
# BROWSER_CACHE_PATH - browser cache path (frontend/fullstack only; omit for backend)
name: Test Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
# Weekly burn-in on Sundays at 2 AM UTC
- cron: "0 2 * * 0"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# Lint stage - Code quality checks
lint:
name: Lint
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- name: Determine Node version
id: node-version
run: |
if [ -f .nvmrc ]; then
echo "value=$(cat .nvmrc)" >> "$GITHUB_OUTPUT"
echo "Using Node from .nvmrc"
else
echo "value=24" >> "$GITHUB_OUTPUT"
echo "Using default Node 24 (current LTS)"
fi
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ steps.node-version.outputs.value }}
cache: "npm"
- name: Install dependencies
run: npm ci # Replace with INSTALL_CMD
- name: Run linter
run: npm run lint # Replace with LINT_CMD
# Test stage - Parallel execution with sharding
test:
name: Test (Shard ${{ matrix.shard }})
runs-on: ubuntu-latest
timeout-minutes: 30
needs: lint
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- name: Determine Node version
id: node-version
run: |
if [ -f .nvmrc ]; then
echo "value=$(cat .nvmrc)" >> "$GITHUB_OUTPUT"
echo "Using Node from .nvmrc"
else
echo "value=22" >> "$GITHUB_OUTPUT"
echo "Using default Node 22 (current LTS)"
fi
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ steps.node-version.outputs.value }}
cache: "npm"
- name: Cache Playwright browsers
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-playwright-
- name: Install dependencies
run: npm ci # Replace with INSTALL_CMD
# Frontend/Fullstack only — remove this step for backend-only stacks
- name: Install Playwright browsers
run: npx playwright install --with-deps chromium # Replace with BROWSER_INSTALL
- name: Run tests (shard ${{ matrix.shard }}/4)
run: npm run test:e2e -- --shard=${{ matrix.shard }}/4 # Replace with TEST_CMD + shard args
- name: Upload test results
if: failure()
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.shard }}
path: |
test-results/
playwright-report/
retention-days: 30
# Burn-in stage - Flaky test detection
burn-in:
name: Burn-In (Flaky Detection)
runs-on: ubuntu-latest
timeout-minutes: 60
needs: test
# Only run burn-in on PRs to main/develop or on schedule
if: github.event_name == 'pull_request' || github.event_name == 'schedule'
steps:
- uses: actions/checkout@v4
- name: Determine Node version
id: node-version
run: |
if [ -f .nvmrc ]; then
echo "value=$(cat .nvmrc)" >> "$GITHUB_OUTPUT"
echo "Using Node from .nvmrc"
else
echo "value=22" >> "$GITHUB_OUTPUT"
echo "Using default Node 22 (current LTS)"
fi
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ steps.node-version.outputs.value }}
cache: "npm"
# Frontend/Fullstack only — remove this step for backend-only stacks
- name: Cache Playwright browsers
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright # Replace with BROWSER_CACHE_PATH
key: ${{ runner.os }}-playwright-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
run: npm ci # Replace with INSTALL_CMD
# Frontend/Fullstack only — remove this step for backend-only stacks
- name: Install Playwright browsers
run: npx playwright install --with-deps chromium # Replace with BROWSER_INSTALL
# Note: Burn-in targets UI flakiness. For backend-only stacks, remove this job entirely.
- name: Run burn-in loop (10 iterations)
run: |
echo "🔥 Starting burn-in loop - detecting flaky tests"
for i in {1..10}; do
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔥 Burn-in iteration $i/10"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
npm run test:e2e || exit 1 # Replace with TEST_CMD
done
echo "✅ Burn-in complete - no flaky tests detected"
- name: Upload burn-in failure artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: burn-in-failures
path: |
test-results/
playwright-report/
retention-days: 30
# Report stage - Aggregate and publish results
report:
name: Test Report
runs-on: ubuntu-latest
needs: [test, burn-in]
if: always()
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Generate summary
run: |
echo "## Test Execution Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Status**: ${{ needs.test.result }}" >> $GITHUB_STEP_SUMMARY
echo "- **Burn-in**: ${{ needs.burn-in.result }}" >> $GITHUB_STEP_SUMMARY
echo "- **Shards**: 4" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.burn-in.result }}" == "failure" ]; then
echo "⚠️ **Flaky tests detected** - Review burn-in artifacts" >> $GITHUB_STEP_SUMMARY
fi
# ============================================================================
# EXTENSION PATTERNS — Script Injection Prevention
# ============================================================================
# When extending this template into reusable workflows, manual dispatch
# workflows, or composite actions, NEVER use ${{ inputs.* }} directly in
# run: blocks. Always pass through env: intermediaries.
#
# KEY PRINCIPLE: Inputs must be DATA, not COMMANDS.
# Pass inputs through env: and interpolate as quoted arguments into fixed
# commands. NEVER accept command-shaped inputs (e.g., install-command,
# test-command) that get executed as shell code — even through env:.
#
# --- Reusable Workflow (workflow_call) ---
#
# on:
# workflow_call:
# inputs:
# test-grep:
# description: 'Test grep filter (data only — not a command)'
# type: string
# required: false
# default: ''
# base-ref:
# description: 'Base branch for diff'
# type: string
# required: false
# default: 'main'
# burn-in-count:
# description: 'Number of burn-in iterations'
# type: string
# required: false
# default: '10'
#
# jobs:
# test:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# # Fixed command — not derived from inputs
# - name: Install dependencies
# run: npm ci
# # ✅ SAFE — input is DATA passed as an argument to a fixed command
# - name: Run tests
# env:
# TEST_GREP: ${{ inputs.test-grep }}
# run: |
# # Security: inputs passed through env: to prevent script injection
# if [ -n "$TEST_GREP" ]; then
# npx playwright test --grep "$TEST_GREP"
# else
# npx playwright test
# fi
#
# --- Manual Dispatch (workflow_dispatch) ---
#
# on:
# workflow_dispatch:
# inputs:
# test-grep:
# description: 'Test grep filter (data only — not a command)'
# type: string
# required: false
# environment:
# description: 'Target environment'
# type: choice
# options: [staging, production]
#
# jobs:
# run-tests:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# # ✅ SAFE — input is DATA interpolated into a fixed command
# - name: Run selected tests
# env:
# TEST_GREP: ${{ inputs.test-grep }}
# run: |
# # Security: inputs passed through env: to prevent script injection
# npx playwright test --grep "$TEST_GREP"
#
# --- Composite Action (action.yml) ---
#
# inputs:
# test-grep:
# description: 'Test grep filter (data only — not a command)'
# required: false
# default: ''
# burn-in-count:
# description: 'Number of burn-in iterations'
# required: false
# default: '10'
#
# runs:
# using: composite
# steps:
# # ✅ SAFE — inputs are DATA arguments to fixed commands
# - name: Run burn-in
# shell: bash
# env:
# TEST_GREP: ${{ inputs.test-grep }}
# BURN_IN_COUNT: ${{ inputs.burn-in-count }}
# run: |
# # Security: inputs passed through env: to prevent script injection
# for i in $(seq 1 "$BURN_IN_COUNT"); do
# echo "Burn-in iteration $i/$BURN_IN_COUNT"
# npx playwright test --grep "$TEST_GREP" || exit 1
# done
#
# ❌ NEVER DO THIS:
# # Direct ${{ inputs.* }} in run: — GitHub expression injection
# - run: npx playwright test --grep "${{ inputs.test-grep }}"
#
# # Executing input-derived env var as a command — still command injection
# - env:
# CMD: ${{ inputs.test-command }}
# run: $CMD
# ============================================================================