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 等步骤。"