Files
2026-06-06 10:40:48 +08:00

183 lines
6.8 KiB
YAML

name: Release Candidate Pipeline
# ═══════════════════════════════════════════════════════
# Release Candidate Pipeline — Active Memory
# ═══════════════════════════════════════════════════════
#
# Flow:
# Step 1) Run Tests
# Step 2) Build RC Payload
# Step 3) RC Checklist (Gate 1)
# Step 4) Baseline Compare (Gate 2)
# Step 5) Release Gate (Final Decision)
# Step 6) Generate RC Report
# Step 7) Upload Artifact
# Step 8) Post-Release (only if OPEN)
#
# Exit Codes:
# Checklist: PASS/WARN → 0, FAIL → 1
# Baseline: PASS/WARN → 0, FAIL → 1
# Gate: OPEN → 0, CLOSED → 1
# Report: success → 0, invalid → 1
#
# Docs: docs/README.md
# ═══════════════════════════════════════════════════════
on:
workflow_dispatch:
inputs:
readiness_status:
description: 'Readiness 状态 (pass/warn/fail)'
required: false
default: 'pass'
rollout_action:
description: '阶段晋级建议 (promote/freeze/rollback/activate_kill_switch)'
required: false
default: 'promote'
rollout_stage:
description: '当前灰度阶段 (0-100)'
required: false
default: '100'
rollback_recommendation:
description: '回滚建议 (keep_enabled/disable_replacement/activate_kill_switch)'
required: false
default: 'keep_enabled'
circuit_state:
description: '断路器状态 (closed/open/half_open)'
required: false
default: 'closed'
strict_mode:
description: 'Strict 模式'
required: false
default: 'false'
type: choice
options:
- 'true'
- 'false'
require_manual_approval:
description: '要求手动审批 (PR-17)'
required: false
default: 'false'
type: choice
options:
- 'true'
- 'false'
jobs:
rc-pipeline:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
# ─── Step 1: Run Tests ───
- name: Run unit tests
id: tests
run: |
if node --test test/baseline/test-*.test.mjs 2>&1 | tee test-output.log; then
echo "tests_passed=true" >> $GITHUB_OUTPUT
echo "tests_failed=0" >> $GITHUB_OUTPUT
else
echo "tests_passed=false" >> $GITHUB_OUTPUT
echo "tests_failed=1" >> $GITHUB_OUTPUT
fi
continue-on-error: true
# ─── Step 2: Build RC Payload ───
- name: Compose RC payload
run: |
cat > test-summary.json << 'EOF'
{ "total": 0, "failed": ${{ steps.tests.outputs.tests_failed || 0 }}, "regressions": 0 }
EOF
cat > readiness.json << 'EOF'
{ "status": "${{ inputs.readiness_status || 'pass' }}", "safeToReplace": true }
EOF
cat > stage.json << 'EOF'
{ "action": "${{ inputs.rollout_action || 'promote' }}", "currentStage": ${{ inputs.rollout_stage || 100 }} }
EOF
cat > rollback.json << 'EOF'
{ "recommendation": "${{ inputs.rollback_recommendation || 'keep_enabled' }}" }
EOF
cat > circuit.json << 'EOF'
{ "state": "${{ inputs.circuit_state || 'closed' }}" }
EOF
# ─── Step 3: RC Checklist (Gate 1) ───
- name: RC Checklist
id: checklist
run: |
STRICT_FLAG=""
if [ "${{ inputs.strict_mode }}" = "true" ]; then STRICT_FLAG="--strict"; fi
node scripts/active-memory-release-candidate-check.mjs \
--ci \
--readiness readiness.json \
--stage stage.json \
--rollback rollback.json \
--circuit circuit.json \
--tests test-summary.json \
$STRICT_FLAG > checklist-result.json
# ─── Step 4: Baseline Compare (Gate 2) ───
- name: Baseline Compare
id: baseline
run: |
echo '{
"baseline": {"blocCount":10,"cycleCount":3,"candidateCount":391,"activeCandidateCount":220,"archivedCandidateCount":171},
"candidate": {"blocCount":10,"cycleCount":3,"candidateCount":391,"activeCandidateCount":220,"archivedCandidateCount":171}
}' | node scripts/active-memory-release-candidate-baseline-compare.mjs > baseline-result.json
# ─── Step 5: Release Gate (Final Decision) ───
- name: Release Gate
id: gate
run: |
MANUAL_FLAG=""
if [ "${{ inputs.require_manual_approval }}" = "true" ]; then MANUAL_FLAG="--require-manual-approval"; fi
node -e "
const checklist = require('./checklist-result.json');
const baseline = require('./baseline-result.json');
process.stdout.write(JSON.stringify({ checklist, baselineCompare: baseline }));
" | node scripts/active-memory-release-gate.mjs $MANUAL_FLAG > gate-result.json
STATUS=$(node -e "console.log(require('./gate-result.json').decision)")
echo "Release Gate: $STATUS"
# ─── Step 6: Generate RC Report ───
- name: Generate RC Report
if: always()
run: |
node -e "
const checklist = require('./checklist-result.json');
const baseline = require('./baseline-result.json');
const gate = require('./gate-result.json');
const { generateReleaseCandidateReport } = require('./src/active-memory/generate-release-candidate-report.mjs');
process.stdout.write(generateReleaseCandidateReport(checklist, baseline, gate));
" > rc-report.md 2>/dev/null || \
node -e "
const checklist = require('./checklist-result.json');
const { generateReleaseCandidateReport } = require('./src/active-memory/generate-release-candidate-report.mjs');
process.stdout.write(generateReleaseCandidateReport(checklist));
" > rc-report.md
echo "📄 RC Report generated:"
cat rc-report.md
- name: Upload RC Report
if: always()
uses: actions/upload-artifact@v4
with:
name: rc-report
path: rc-report.md
# ─── Step 7: Post-Gate (only if OPEN) ───
- name: Post-Release
if: success()
run: |
echo "✅ Release Gate: OPEN"
echo " 后续可接入:deploy、publish、notify 等步骤。"