@metasession.co/devaudit-cli 0.3.12 → 0.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +53 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/scripts/upload-evidence.sh +55 -1
- package/sdlc/files/_common/0-project-setup.md +10 -4
- package/sdlc/files/_common/Periodic_Security_Review_Schedule.md +5 -5
- package/sdlc/files/_common/README_TEMPLATE.md +8 -8
- package/sdlc/files/_common/Test_Architecture.md +15 -15
- package/sdlc/files/_common/Test_Plan_TEMPLATE.md +13 -13
- package/sdlc/files/_common/Test_Policy.md +2 -2
- package/sdlc/files/_common/Test_Strategy.md +10 -10
- package/sdlc/files/_common/governance/ai-disclosure.md.template +3 -3
- package/sdlc/files/_common/governance/dpia.md.template +3 -3
- package/sdlc/files/_common/governance/incident-report.md.template +3 -3
- package/sdlc/files/_common/governance/nil-incident-report.md.template +1 -1
- package/sdlc/files/_common/governance/periodic-review.md.template +3 -3
- package/sdlc/files/_common/governance/risk-register.md.template +1 -1
- package/sdlc/files/_common/governance/ropa.md.template +2 -2
- package/sdlc/files/_common/joining-an-existing-project.md +4 -4
- package/sdlc/files/_common/scripts/check-host-deployment.sh +107 -0
- package/sdlc/files/_common/scripts/check-host-deployment.test.sh +119 -0
- package/sdlc/files/_common/scripts/check-release-pr-scope.sh +95 -0
- package/sdlc/files/_common/scripts/check-release-pr-scope.test.sh +137 -0
- package/sdlc/files/_common/scripts/close-out-release.sh +175 -54
- package/sdlc/files/_common/scripts/close-out-release.test.sh +90 -0
- package/sdlc/files/_common/scripts/extract-release-metadata.sh +27 -0
- package/sdlc/files/_common/scripts/extract-release-metadata.test.sh +13 -1
- package/sdlc/files/_common/scripts/generate-bundled-changes.sh +302 -64
- package/sdlc/files/_common/scripts/generate-bundled-changes.test.sh +128 -82
- package/sdlc/files/_common/scripts/generate-housekeeping-release-ticket.sh +18 -9
- package/sdlc/files/_common/scripts/generate-housekeeping-release-ticket.test.sh +66 -0
- package/sdlc/files/_common/scripts/generate-security-summary.sh +14 -5
- package/sdlc/files/_common/scripts/generate-security-summary.test.sh +57 -0
- package/sdlc/files/_common/scripts/prepare-release-pr.sh +44 -4
- package/sdlc/files/_common/scripts/render-test-cycles.sh +227 -0
- package/sdlc/files/_common/scripts/render-test-cycles.test.sh +107 -0
- package/sdlc/files/_common/scripts/report-test-cycle.sh +344 -0
- package/sdlc/files/_common/scripts/submit-bundle-manifest.sh +100 -0
- package/sdlc/files/_common/scripts/submit-bundle-manifest.test.sh +138 -0
- package/sdlc/files/_common/scripts/upload-evidence.sh +24 -0
- package/sdlc/files/_common/scripts/validate-compliance-artifacts.sh +16 -0
- package/sdlc/files/_common/scripts/validate-compliance-artifacts.test.sh +14 -0
- package/sdlc/files/_common/scripts/validate-test-summary.test.sh +4 -4
- package/sdlc/files/_common/skills/adr-author/SKILL.md +2 -2
- package/sdlc/files/_common/skills/e2e-test-engineer/SKILL.md +2 -2
- package/sdlc/files/_common/skills/e2e-test-engineer/references/bootstrap.md +5 -5
- package/sdlc/files/_common/skills/governance-doc-author/references/incident-classification.md +1 -1
- package/sdlc/files/_common/skills/requirements-aligner/SKILL.md +4 -4
- package/sdlc/files/_common/skills/sdlc-implementer/SKILL.md +21 -2
- package/sdlc/files/_common/skills/sdlc-implementer/references/change-request-loop.md +1 -1
- package/sdlc/files/_common/skills/sdlc-implementer/references/compliance-constraints.md +7 -14
- package/sdlc/files/ci/ci.yml.template +82 -28
- package/sdlc/files/ci/close-out-release.yml.template +7 -4
- package/sdlc/files/ci/compliance-evidence.yml.template +204 -20
- package/sdlc/files/ci/feature-e2e.yml.template +84 -1
- package/sdlc/files/ci/post-deploy-prod.yml.template +59 -4
- package/sdlc/files/ci/python/ci.yml.template +60 -12
- package/sdlc/files/ci/quality-gates-provenance.yml.template +123 -28
- package/sdlc/package.json +1 -1
- package/sdlc/src/bin/devaudit-sdlc.js +44 -2
- package/sdlc/src/blueprints/1-plan-requirement.raw.md +6 -1
- package/sdlc/src/blueprints/2-implement-and-test.raw.md +1 -1
- package/sdlc/src/blueprints/3-compile-evidence.raw.md +34 -20
- package/sdlc/src/blueprints/4-submit-for-review.raw.md +2 -2
- package/sdlc/src/blueprints/5-deploy-main.raw.md +2 -2
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
#
|
|
6
6
|
# Single consolidated job. Order: install → ruff → mypy → semgrep → pip-audit → pytest → build.
|
|
7
7
|
#
|
|
8
|
-
# PRs to
|
|
9
|
-
#
|
|
8
|
+
# PRs to {{INTEGRATION_BRANCH}} run Quality Gates before integration. PRs to
|
|
9
|
+
# {{RELEASE_BRANCH}} are handled by quality-gates-provenance.yml: provenance-only
|
|
10
|
+
# for release PRs, full CI rerouted for hotfix/* PRs.
|
|
10
11
|
# Compliance validation runs separately on PRs (compliance-validation.yml).
|
|
11
12
|
|
|
12
13
|
name: CI Pipeline
|
|
@@ -14,9 +15,9 @@ name: CI Pipeline
|
|
|
14
15
|
on:
|
|
15
16
|
workflow_dispatch:
|
|
16
17
|
pull_request:
|
|
17
|
-
branches: [
|
|
18
|
+
branches: [{{INTEGRATION_BRANCH}}]
|
|
18
19
|
push:
|
|
19
|
-
branches: [
|
|
20
|
+
branches: [{{INTEGRATION_BRANCH}}]
|
|
20
21
|
paths-ignore:
|
|
21
22
|
{{PATHS_IGNORE}} - 'sdlc-config.json'
|
|
22
23
|
|
|
@@ -190,7 +191,7 @@ jobs:
|
|
|
190
191
|
# PRs to develop should report Quality Gates without mutating DevAudit
|
|
191
192
|
# releases or evidence. Release registration remains a develop-push /
|
|
192
193
|
# manual-dispatch side effect.
|
|
193
|
-
if: ${{ github.event_name != 'pull_request' }}
|
|
194
|
+
if: ${{ github.event_name != 'pull_request' && github.ref_name == '{{INTEGRATION_BRANCH}}' }}
|
|
194
195
|
outputs:
|
|
195
196
|
version: ${{ steps.version.outputs.version }}
|
|
196
197
|
env:
|
|
@@ -314,7 +315,7 @@ jobs:
|
|
|
314
315
|
# evidence — `status=failed` is itself the audit trail. `!cancelled()`
|
|
315
316
|
# still guards against partial state on operator-cancel.
|
|
316
317
|
# DevAudit-Installer#96.
|
|
317
|
-
if: ${{ always() && !cancelled() && needs.register-release.result == 'success' }}
|
|
318
|
+
if: ${{ always() && !cancelled() && github.ref_name == '{{INTEGRATION_BRANCH}}' && needs.register-release.result == 'success' }}
|
|
318
319
|
env:
|
|
319
320
|
DEVAUDIT_BASE_URL_VAR: ${{ vars.DEVAUDIT_BASE_URL }}
|
|
320
321
|
DEVAUDIT_API_KEY: ${{ secrets.DEVAUDIT_API_KEY }}
|
|
@@ -356,11 +357,33 @@ jobs:
|
|
|
356
357
|
- name: Generate and upload gate evidence
|
|
357
358
|
if: env.DEVAUDIT_BASE_URL != ''
|
|
358
359
|
run: |
|
|
359
|
-
chmod +x scripts/upload-evidence.sh 2>/dev/null || true
|
|
360
|
+
chmod +x scripts/upload-evidence.sh scripts/report-test-cycle.sh 2>/dev/null || true
|
|
360
361
|
FLAGS="--git-sha ${{ github.sha }} --ci-run-id ${{ github.run_id }} --branch ${{ github.ref_name }}"
|
|
361
362
|
FLAGS="${FLAGS} --release ${{ needs.register-release.outputs.version }} --create-release-if-missing"
|
|
362
363
|
FLAGS="${FLAGS} --environment uat"
|
|
363
|
-
FLAGS="${FLAGS} --
|
|
364
|
+
FLAGS="${FLAGS} --sdlc-stage 2"
|
|
365
|
+
PRIMARY_LINEAGE_FLAGS=(--test-cycle "${{ github.run_id }}")
|
|
366
|
+
CYCLE_OUT="$(mktemp)"
|
|
367
|
+
bash scripts/report-test-cycle.sh start \
|
|
368
|
+
--project-slug {{PROJECT_SLUG}} \
|
|
369
|
+
--release "${{ needs.register-release.outputs.version }}" \
|
|
370
|
+
--sdlc-stage 2 \
|
|
371
|
+
--environment uat \
|
|
372
|
+
--cycle-kind quality_gate \
|
|
373
|
+
--provider github_actions \
|
|
374
|
+
--external-run-id "${{ github.run_id }}" \
|
|
375
|
+
--external-run-attempt "${{ github.run_attempt }}" \
|
|
376
|
+
--idempotency-key "github:${{ github.repository }}:quality-gates-python.quality_gate:${{ github.run_id }}:${{ github.run_attempt }}:2:${{ needs.register-release.outputs.version }}" \
|
|
377
|
+
--commit-sha "${{ github.sha }}" \
|
|
378
|
+
--branch "${{ github.ref_name }}" \
|
|
379
|
+
--workflow-name "Quality Gates" \
|
|
380
|
+
--workflow-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
|
|
381
|
+
--output-file "$CYCLE_OUT"
|
|
382
|
+
. "$CYCLE_OUT"
|
|
383
|
+
rm -f "$CYCLE_OUT"
|
|
384
|
+
if [ "${cycle_supported:-false}" = "true" ] && [ -n "${cycle_record_id:-}" ]; then
|
|
385
|
+
PRIMARY_LINEAGE_FLAGS+=(--evidence-scope cycle --test-cycle-record-id "${cycle_record_id}")
|
|
386
|
+
fi
|
|
364
387
|
|
|
365
388
|
UPLOAD_FAILURES=0
|
|
366
389
|
upload() {
|
|
@@ -394,7 +417,7 @@ jobs:
|
|
|
394
417
|
STATUS_TESTS=$(gate_status "$(jq -r '.tests // "skipped"' "$OUTCOMES_FILE")")
|
|
395
418
|
upload gate-outcomes.json \
|
|
396
419
|
{{PROJECT_SLUG}} _compliance-docs gate_outcome "$OUTCOMES_FILE" \
|
|
397
|
-
--category ci_pipeline ${FLAGS}
|
|
420
|
+
--category ci_pipeline ${FLAGS} "${PRIMARY_LINEAGE_FLAGS[@]}"
|
|
398
421
|
fi
|
|
399
422
|
|
|
400
423
|
mkdir -p {{WORKING_DIR_PREFIX}}ci-evidence
|
|
@@ -405,14 +428,14 @@ jobs:
|
|
|
405
428
|
if [ -f {{WORKING_DIR_PREFIX}}ci-evidence/sast-results.json ]; then
|
|
406
429
|
upload sast-results.json \
|
|
407
430
|
{{PROJECT_SLUG}} _compliance-docs sast_report {{WORKING_DIR_PREFIX}}ci-evidence/sast-results.json \
|
|
408
|
-
--category security_scan --gate-status "$STATUS_SAST" ${FLAGS}
|
|
431
|
+
--category security_scan --gate-status "$STATUS_SAST" ${FLAGS} "${PRIMARY_LINEAGE_FLAGS[@]}"
|
|
409
432
|
fi
|
|
410
433
|
|
|
411
434
|
# Precise evidence_type=dependency_audit (matches the node template).
|
|
412
435
|
if [ -f {{WORKING_DIR_PREFIX}}ci-evidence/dependency-audit.json ]; then
|
|
413
436
|
upload dependency-audit.json \
|
|
414
437
|
{{PROJECT_SLUG}} _compliance-docs dependency_audit {{WORKING_DIR_PREFIX}}ci-evidence/dependency-audit.json \
|
|
415
|
-
--category security_scan --gate-status "$STATUS_DEPAUDIT" ${FLAGS}
|
|
438
|
+
--category security_scan --gate-status "$STATUS_DEPAUDIT" ${FLAGS} "${PRIMARY_LINEAGE_FLAGS[@]}"
|
|
416
439
|
fi
|
|
417
440
|
|
|
418
441
|
# pytest junit.xml is the Python equivalent of e2e-results.json —
|
|
@@ -421,7 +444,7 @@ jobs:
|
|
|
421
444
|
if [ -f {{WORKING_DIR_PREFIX}}ci-evidence/junit.xml ]; then
|
|
422
445
|
upload junit.xml \
|
|
423
446
|
{{PROJECT_SLUG}} _compliance-docs e2e_result {{WORKING_DIR_PREFIX}}ci-evidence/junit.xml \
|
|
424
|
-
--category e2e_result --gate-status "$STATUS_TESTS" ${FLAGS}
|
|
447
|
+
--category e2e_result --gate-status "$STATUS_TESTS" ${FLAGS} "${PRIMARY_LINEAGE_FLAGS[@]}"
|
|
425
448
|
fi
|
|
426
449
|
|
|
427
450
|
# Project-level Test Summary Report — intentional compliance_document
|
|
@@ -523,5 +546,30 @@ jobs:
|
|
|
523
546
|
exit 1
|
|
524
547
|
fi
|
|
525
548
|
|
|
549
|
+
- name: Complete primary quality-gate cycle
|
|
550
|
+
if: always() && env.DEVAUDIT_BASE_URL != ''
|
|
551
|
+
run: |
|
|
552
|
+
case "${{ job.status }}" in
|
|
553
|
+
success) OUTCOME=passed ;;
|
|
554
|
+
cancelled) OUTCOME=cancelled ;;
|
|
555
|
+
*) OUTCOME=failed ;;
|
|
556
|
+
esac
|
|
557
|
+
chmod +x scripts/report-test-cycle.sh 2>/dev/null || true
|
|
558
|
+
bash scripts/report-test-cycle.sh complete \
|
|
559
|
+
--project-slug {{PROJECT_SLUG}} \
|
|
560
|
+
--release "${{ needs.register-release.outputs.version }}" \
|
|
561
|
+
--sdlc-stage 2 \
|
|
562
|
+
--environment uat \
|
|
563
|
+
--cycle-kind quality_gate \
|
|
564
|
+
--provider github_actions \
|
|
565
|
+
--external-run-id "${{ github.run_id }}" \
|
|
566
|
+
--external-run-attempt "${{ github.run_attempt }}" \
|
|
567
|
+
--idempotency-key "github:${{ github.repository }}:quality-gates-python.quality_gate:${{ github.run_id }}:${{ github.run_attempt }}:2:${{ needs.register-release.outputs.version }}" \
|
|
568
|
+
--commit-sha "${{ github.sha }}" \
|
|
569
|
+
--branch "${{ github.ref_name }}" \
|
|
570
|
+
--workflow-name "Quality Gates" \
|
|
571
|
+
--workflow-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
|
|
572
|
+
--outcome "$OUTCOME"
|
|
573
|
+
|
|
526
574
|
- name: Summary
|
|
527
575
|
run: echo "Evidence uploaded for ${{ needs.register-release.outputs.version }} (UAT)"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
# Quality Gates Provenance —
|
|
1
|
+
# Quality Gates Provenance — routes PRs to the release branch into the correct
|
|
2
|
+
# Quality Gates path.
|
|
2
3
|
#
|
|
3
4
|
# Generated by `devaudit install` / `devaudit update` from sdlc-config.json.
|
|
4
5
|
# Do not edit manually — re-run the CLI (`devaudit update`) to regenerate.
|
|
@@ -9,10 +10,11 @@
|
|
|
9
10
|
# Quality Gates result. When that happens, neither existing workflow fires on
|
|
10
11
|
# the PR to main, so the release PR sits with missing/ambiguous protection.
|
|
11
12
|
#
|
|
12
|
-
# This workflow fires on PRs to
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
13
|
+
# This workflow fires on PRs to {{RELEASE_BRANCH}} and chooses between two modes:
|
|
14
|
+
# - release PR (`{{INTEGRATION_BRANCH}} -> {{RELEASE_BRANCH}}`): same-SHA provenance
|
|
15
|
+
# check only
|
|
16
|
+
# - hotfix PR (`hotfix/* -> {{RELEASE_BRANCH}}`): dispatch the real ci.yml gates on
|
|
17
|
+
# the hotfix branch and proxy that result back onto the PR
|
|
16
18
|
#
|
|
17
19
|
# The job name is exactly `Quality Gates` to satisfy existing branch-protection
|
|
18
20
|
# rules without introducing a new required-check name.
|
|
@@ -22,14 +24,102 @@ name: CI Pipeline
|
|
|
22
24
|
on:
|
|
23
25
|
workflow_dispatch:
|
|
24
26
|
pull_request:
|
|
25
|
-
branches: [
|
|
27
|
+
branches: [{{RELEASE_BRANCH}}]
|
|
26
28
|
|
|
27
29
|
jobs:
|
|
30
|
+
release-scope-integrity:
|
|
31
|
+
name: Release Scope Integrity
|
|
32
|
+
if: ${{ !startsWith(github.event.pull_request.head.ref, 'hotfix/') && !startsWith(github.event.pull_request.head.ref, 'fix/hotfix-') }}
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v6
|
|
36
|
+
with:
|
|
37
|
+
ref: ${{ github.event.pull_request.head.sha }}
|
|
38
|
+
fetch-depth: 0
|
|
39
|
+
|
|
40
|
+
- name: Check release PR metadata against derived scope
|
|
41
|
+
env:
|
|
42
|
+
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
43
|
+
PR_BODY: ${{ github.event.pull_request.body }}
|
|
44
|
+
HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
|
45
|
+
run: |
|
|
46
|
+
chmod +x scripts/check-release-pr-scope.sh 2>/dev/null || true
|
|
47
|
+
bash scripts/check-release-pr-scope.sh
|
|
48
|
+
|
|
28
49
|
quality-gates:
|
|
29
50
|
name: Quality Gates
|
|
30
51
|
runs-on: ubuntu-latest
|
|
31
52
|
steps:
|
|
53
|
+
- name: Classify PR path
|
|
54
|
+
id: classify
|
|
55
|
+
env:
|
|
56
|
+
HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
|
57
|
+
BASE_REF: ${{ github.event.pull_request.base.ref }}
|
|
58
|
+
run: |
|
|
59
|
+
case "$HEAD_REF" in
|
|
60
|
+
hotfix/*|fix/hotfix-*)
|
|
61
|
+
echo "kind=hotfix" >> "$GITHUB_OUTPUT"
|
|
62
|
+
;;
|
|
63
|
+
*)
|
|
64
|
+
echo "kind=release" >> "$GITHUB_OUTPUT"
|
|
65
|
+
;;
|
|
66
|
+
esac
|
|
67
|
+
echo "base_ref=${BASE_REF}" >> "$GITHUB_OUTPUT"
|
|
68
|
+
echo "head_ref=${HEAD_REF}" >> "$GITHUB_OUTPUT"
|
|
69
|
+
|
|
70
|
+
- name: Dispatch ci.yml Quality Gates for hotfix PR
|
|
71
|
+
if: steps.classify.outputs.kind == 'hotfix'
|
|
72
|
+
env:
|
|
73
|
+
GH_TOKEN: ${{ github.token }}
|
|
74
|
+
HEAD_REF: ${{ steps.classify.outputs.head_ref }}
|
|
75
|
+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
76
|
+
run: |
|
|
77
|
+
BEFORE_JSON=$(gh run list \
|
|
78
|
+
--workflow "CI Pipeline" \
|
|
79
|
+
--branch "$HEAD_REF" \
|
|
80
|
+
--event workflow_dispatch \
|
|
81
|
+
--limit 50 \
|
|
82
|
+
--json databaseId,headSha)
|
|
83
|
+
|
|
84
|
+
gh workflow run ci.yml --ref "$HEAD_REF"
|
|
85
|
+
|
|
86
|
+
RUN_ID=""
|
|
87
|
+
for ATTEMPT in $(seq 1 20); do
|
|
88
|
+
AFTER_JSON=$(gh run list \
|
|
89
|
+
--workflow "CI Pipeline" \
|
|
90
|
+
--branch "$HEAD_REF" \
|
|
91
|
+
--event workflow_dispatch \
|
|
92
|
+
--limit 50 \
|
|
93
|
+
--json databaseId,headSha)
|
|
94
|
+
RUN_ID=$(BEFORE_JSON="$BEFORE_JSON" AFTER_JSON="$AFTER_JSON" HEAD_SHA="$HEAD_SHA" python3 - <<'PY'
|
|
95
|
+
import json
|
|
96
|
+
import os
|
|
97
|
+
|
|
98
|
+
before = {str(item["databaseId"]) for item in json.loads(os.environ["BEFORE_JSON"])}
|
|
99
|
+
head_sha = os.environ["HEAD_SHA"]
|
|
100
|
+
for item in json.loads(os.environ["AFTER_JSON"]):
|
|
101
|
+
if item.get("headSha") == head_sha and str(item["databaseId"]) not in before:
|
|
102
|
+
print(item["databaseId"])
|
|
103
|
+
break
|
|
104
|
+
PY
|
|
105
|
+
)
|
|
106
|
+
if [ -n "$RUN_ID" ]; then
|
|
107
|
+
break
|
|
108
|
+
fi
|
|
109
|
+
echo "Waiting for dispatched ci.yml run to appear for ${HEAD_REF}..."
|
|
110
|
+
sleep 10
|
|
111
|
+
done
|
|
112
|
+
|
|
113
|
+
if [ -z "$RUN_ID" ]; then
|
|
114
|
+
echo "::error::Unable to locate the dispatched ci.yml run for hotfix branch ${HEAD_REF}."
|
|
115
|
+
exit 1
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
echo "Watching dispatched ci.yml run ${RUN_ID} for hotfix branch ${HEAD_REF}..."
|
|
119
|
+
gh run watch "$RUN_ID" --exit-status
|
|
120
|
+
|
|
32
121
|
- name: Verify prior Quality Gates success on head SHA
|
|
122
|
+
if: steps.classify.outputs.kind != 'hotfix'
|
|
33
123
|
env:
|
|
34
124
|
GH_TOKEN: ${{ github.token }}
|
|
35
125
|
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
@@ -37,26 +127,31 @@ jobs:
|
|
|
37
127
|
run: |
|
|
38
128
|
echo "Checking for prior successful 'Quality Gates' on SHA: $HEAD_SHA"
|
|
39
129
|
echo "Excluding current workflow run: $CURRENT_RUN_ID"
|
|
40
|
-
|
|
41
|
-
# Query GitHub API for check runs on this SHA with name "Quality Gates"
|
|
42
130
|
CURRENT_RUN_URL="/actions/runs/${CURRENT_RUN_ID}"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
131
|
+
MAX_ATTEMPTS=10
|
|
132
|
+
WAIT_SECONDS=30
|
|
133
|
+
|
|
134
|
+
for ATTEMPT in $(seq 1 $MAX_ATTEMPTS); do
|
|
135
|
+
RESPONSE=$(gh api \
|
|
136
|
+
"/repos/${{ github.repository }}/commits/${HEAD_SHA}/check-runs?name=Quality%20Gates" \
|
|
137
|
+
--jq ".check_runs[]
|
|
138
|
+
| select(.status == \"completed\" and .conclusion == \"success\")
|
|
139
|
+
| select((.details_url // \"\") | contains(\"${CURRENT_RUN_URL}\") | not)")
|
|
140
|
+
|
|
141
|
+
if [ -n "$RESPONSE" ]; then
|
|
142
|
+
echo "Found prior successful Quality Gates result:"
|
|
143
|
+
echo "$RESPONSE" | head -5
|
|
144
|
+
echo ""
|
|
145
|
+
echo "Provenance verified — this SHA passed develop-side Quality Gates."
|
|
146
|
+
exit 0
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
if [ "$ATTEMPT" -lt "$MAX_ATTEMPTS" ]; then
|
|
150
|
+
echo "Develop-side Quality Gates for SHA ${HEAD_SHA} not successful yet. Waiting ${WAIT_SECONDS}s (${ATTEMPT}/${MAX_ATTEMPTS})..."
|
|
151
|
+
sleep "$WAIT_SECONDS"
|
|
152
|
+
fi
|
|
153
|
+
done
|
|
154
|
+
|
|
155
|
+
echo "::error::No successful develop-side Quality Gates result found for SHA ${HEAD_SHA} after waiting $((MAX_ATTEMPTS * WAIT_SECONDS / 60)) minutes."
|
|
156
|
+
echo "::error::Push this exact commit through the normal develop flow first, or wait for the in-flight develop pipeline to finish and rerun this check."
|
|
157
|
+
exit 1
|
package/sdlc/package.json
CHANGED
|
@@ -16,6 +16,15 @@ const DEFAULT_FLAKY_PATTERNS = [
|
|
|
16
16
|
'flaky',
|
|
17
17
|
'retry',
|
|
18
18
|
];
|
|
19
|
+
const DEFAULT_INTEGRATION_BRANCH = 'develop';
|
|
20
|
+
const DEFAULT_RELEASE_BRANCH = 'main';
|
|
21
|
+
const REQUIRED_RELEASE_CHECKS = [
|
|
22
|
+
'Quality Gates',
|
|
23
|
+
'Release Scope Integrity',
|
|
24
|
+
'Compliance Validation',
|
|
25
|
+
'DevAudit Release Approval',
|
|
26
|
+
'E2E Regression Suite',
|
|
27
|
+
];
|
|
19
28
|
const RELEASE_APPROVAL_STATUSES = new Set([
|
|
20
29
|
'uat_approved',
|
|
21
30
|
'release_approved',
|
|
@@ -84,6 +93,11 @@ function isReleaseGateCheck(check) {
|
|
|
84
93
|
return haystack.includes('release approval') || haystack.includes('uat approval');
|
|
85
94
|
}
|
|
86
95
|
|
|
96
|
+
function isReleasePromotionPr(pr, options) {
|
|
97
|
+
return normalizeState(pr.baseRefName) === normalizeState(options.releaseBranch || DEFAULT_RELEASE_BRANCH)
|
|
98
|
+
&& normalizeState(pr.headRefName) === normalizeState(options.integrationBranch || DEFAULT_INTEGRATION_BRANCH);
|
|
99
|
+
}
|
|
100
|
+
|
|
87
101
|
function isFlakyCheck(check, patterns) {
|
|
88
102
|
const haystack = `${check.name || ''} ${check.workflow || ''}`.toLowerCase();
|
|
89
103
|
return patterns.some(pattern => haystack.includes(pattern));
|
|
@@ -313,6 +327,14 @@ function classifyPrStatus(pr, checks, portalStatus, watch, options) {
|
|
|
313
327
|
const failingChecks = checks.filter(check => isFailingState(check.state || check.bucket));
|
|
314
328
|
const pendingChecks = checks.filter(check => isPendingState(check.state || check.bucket));
|
|
315
329
|
const releaseGate = checks.find(isReleaseGateCheck) || null;
|
|
330
|
+
const isReleasePr = isReleasePromotionPr(pr || {}, options);
|
|
331
|
+
const releaseChecks = isReleasePr
|
|
332
|
+
? checks.filter(check => REQUIRED_RELEASE_CHECKS.includes(String(check.name || '')))
|
|
333
|
+
: [];
|
|
334
|
+
const missingReleaseChecks = isReleasePr
|
|
335
|
+
? REQUIRED_RELEASE_CHECKS.filter(required => !releaseChecks.some(check => String(check.name || '') === required))
|
|
336
|
+
: [];
|
|
337
|
+
const pendingReleaseChecks = releaseChecks.filter(check => isPendingState(check.state || check.bucket));
|
|
316
338
|
|
|
317
339
|
if (normalizeState(pr.state) !== 'open') {
|
|
318
340
|
return {
|
|
@@ -362,6 +384,22 @@ function classifyPrStatus(pr, checks, portalStatus, watch, options) {
|
|
|
362
384
|
};
|
|
363
385
|
}
|
|
364
386
|
|
|
387
|
+
if (isReleasePr && missingReleaseChecks.length > 0) {
|
|
388
|
+
return {
|
|
389
|
+
kind: 'waiting',
|
|
390
|
+
exitCode: 2,
|
|
391
|
+
summary: `PR #${watch.prNumber} is still waiting for required release checks to report: ${missingReleaseChecks.join(', ')}.`,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (isReleasePr && pendingReleaseChecks.length > 0) {
|
|
396
|
+
return {
|
|
397
|
+
kind: 'waiting',
|
|
398
|
+
exitCode: 2,
|
|
399
|
+
summary: `PR #${watch.prNumber} still has required release checks running: ${pendingReleaseChecks.map(formatCheckName).join(', ')}.`,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
365
403
|
for (const check of failingChecks) {
|
|
366
404
|
if (!isReleaseGateCheck(check) && isFlakyCheck(check, options.flakyPatterns || DEFAULT_FLAKY_PATTERNS)) {
|
|
367
405
|
const runId = extractRunId(check.link);
|
|
@@ -405,7 +443,9 @@ function classifyPrStatus(pr, checks, portalStatus, watch, options) {
|
|
|
405
443
|
return {
|
|
406
444
|
kind: 'ready',
|
|
407
445
|
exitCode: 0,
|
|
408
|
-
summary:
|
|
446
|
+
summary: isReleasePr
|
|
447
|
+
? `PR #${watch.prNumber} is approved and all required release checks reached terminal success. Ready for merge.`
|
|
448
|
+
: `PR #${watch.prNumber} is approved and all observed checks are green. Ready for merge.`,
|
|
409
449
|
};
|
|
410
450
|
}
|
|
411
451
|
|
|
@@ -454,6 +494,8 @@ async function watchPullRequest() {
|
|
|
454
494
|
|
|
455
495
|
const options = {
|
|
456
496
|
flakyPatterns: flakyPatterns.length > 0 ? flakyPatterns : DEFAULT_FLAKY_PATTERNS,
|
|
497
|
+
integrationBranch: getOption('integration-branch') || DEFAULT_INTEGRATION_BRANCH,
|
|
498
|
+
releaseBranch: getOption('release-branch') || DEFAULT_RELEASE_BRANCH,
|
|
457
499
|
};
|
|
458
500
|
|
|
459
501
|
let pollsRemaining = once ? 1 : maxPolls;
|
|
@@ -463,7 +505,7 @@ async function watchPullRequest() {
|
|
|
463
505
|
let pr;
|
|
464
506
|
let checks;
|
|
465
507
|
try {
|
|
466
|
-
pr = runGhJson(['pr', 'view', String(prNumber), '--repo', repo, '--json', 'state,isDraft,reviewDecision']);
|
|
508
|
+
pr = runGhJson(['pr', 'view', String(prNumber), '--repo', repo, '--json', 'state,isDraft,reviewDecision,baseRefName,headRefName']);
|
|
467
509
|
checks = runGhJson(['pr', 'checks', String(prNumber), '--repo', repo, '--json', 'name,state,link,workflow,bucket']) || [];
|
|
468
510
|
if (!Array.isArray(checks)) checks = [];
|
|
469
511
|
} catch (error) {
|
|
@@ -70,6 +70,11 @@ Open `compliance/RTM.md`, Part B. The issue provides full context; the RTM is a
|
|
|
70
70
|
| REQ-XXX | #NNN | [LOW/MEDIUM/HIGH] | compliance/evidence/REQ-XXX/ | DRAFT | -- | -- |
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
If any RTM cell content needs literal pipe characters, escape them as `\|`
|
|
74
|
+
rather than writing bare `|`. This matters for regex or enum examples inside
|
|
75
|
+
status/notes text because markdownlint otherwise misreads the row as having
|
|
76
|
+
extra columns.
|
|
77
|
+
|
|
73
78
|
The auditor reads one row and follows the links: Issue for context and rationale, evidence directory for test artifacts, PR for code changes.
|
|
74
79
|
|
|
75
80
|
### Step 5: Create Evidence Directory
|
|
@@ -379,7 +384,7 @@ cat > compliance/evidence/REQ-XXX/test-plan.md << 'EOF'
|
|
|
379
384
|
|
|
380
385
|
## Functional Test Mapping
|
|
381
386
|
| Acceptance Criterion | Test File | Test Name |
|
|
382
|
-
|
|
387
|
+
| --------------------- | ----------- | ----------- |
|
|
383
388
|
| [From test-scope.md] | [spec file] | [test name] |
|
|
384
389
|
|
|
385
390
|
## Non-Functional Tests (MEDIUM/HIGH)
|
|
@@ -246,7 +246,7 @@ For LOW-risk docs/tooling/script-only changes or environments without the requir
|
|
|
246
246
|
#### Exit Criteria
|
|
247
247
|
|
|
248
248
|
| Gate | Threshold |
|
|
249
|
-
|
|
249
|
+
| --- | --- |
|
|
250
250
|
| TypeScript | 0 errors |
|
|
251
251
|
| SAST (high/critical) | 0 findings |
|
|
252
252
|
| Dependencies (high/critical) | 0 vulnerabilities |
|
|
@@ -36,7 +36,7 @@ If you are an AI agent reading this file, you are strictly prohibited from manua
|
|
|
36
36
|
**Markdown stays in git. Binary and JSON evidence goes to DevAudit.**
|
|
37
37
|
|
|
38
38
|
| Artifact | Store in | Why |
|
|
39
|
-
|
|
39
|
+
| ---------- | ---------- | ----- |
|
|
40
40
|
| `compliance/RTM.md` | Git | Source of truth, version history, PR-reviewable |
|
|
41
41
|
| `compliance/evidence/REQ-XXX/test-scope.md` | Git | Planning artifact, reviewed in PRs |
|
|
42
42
|
| `compliance/evidence/REQ-XXX/implementation-plan.md` | Git | Design decisions artifact (MEDIUM/HIGH risk), reviewed in PRs |
|
|
@@ -60,7 +60,7 @@ CI uploads are scoped to a **release record** in DevAudit, keyed by `(project, v
|
|
|
60
60
|
The templated workflows derive the release version from the **latest commit on the branch** via `scripts/derive-release-version.sh`:
|
|
61
61
|
|
|
62
62
|
| Commit shape | Release version |
|
|
63
|
-
|
|
63
|
+
| --- | --- |
|
|
64
64
|
| Subject `[REQ-037] feat(...)` | `REQ-037` |
|
|
65
65
|
| Subject `feat(...)` + body `Ref: REQ-037` | `REQ-037` |
|
|
66
66
|
| Subject contains multiple tags (e.g. `[REQ-037][REQ-038]`) | First match wins → `REQ-037` |
|
|
@@ -75,7 +75,7 @@ If you need a separate release container for a sub-piece of work — e.g. carvin
|
|
|
75
75
|
A release is classified by its version pattern. **Most of this doc walks the tracked path** (a REQ-XXX-tagged release for a single requirement). For develop pushes that don't carry a REQ tag — typically `docs:`, `chore:`, `ci:`, `build:`, `test:`, `compliance:`, `revert:` — the version-deriver falls back to a bare date and the release is **housekeeping**.
|
|
76
76
|
|
|
77
77
|
| Shape | Version pattern | Triggered by | Per-REQ ceremony |
|
|
78
|
-
|
|
78
|
+
| --- | --- | --- | --- |
|
|
79
79
|
| **Tracked** | `REQ-037`, `REQ-046-FIX`, etc. | A `feat`/`fix`/`refactor`/`perf` commit (REQ tag required by commitlint) | Yes — full Steps 1-9 |
|
|
80
80
|
| **Housekeeping** | `v2026.06.04` (bare date, optionally `.N`) | A push containing only REQ-exempt commit types | **No** per-REQ artefacts; the portal auto-skips test-scope, test-plan, implementation-plan, and test-execution-summary completeness checks |
|
|
81
81
|
|
|
@@ -84,7 +84,7 @@ A release is classified by its version pattern. **Most of this doc walks the tra
|
|
|
84
84
|
Housekeeping releases **skip** the per-requirement evidence (no REQ → no `compliance/evidence/REQ-XXX/` folder) but **still produce two release-scoped artefacts**:
|
|
85
85
|
|
|
86
86
|
| Artefact | Tracked path | Housekeeping path | Auto-generated by CI? |
|
|
87
|
-
|
|
87
|
+
| --- | --- | --- | --- |
|
|
88
88
|
| Release ticket | `compliance/pending-releases/RELEASE-TICKET-REQ-XXX.md` (Step 8) | `compliance/pending-releases/RELEASE-TICKET-<version>.md` (e.g. `RELEASE-TICKET-v2026.06.04.md`) | **Yes** — `generate-housekeeping-release-ticket.sh` runs after `derive-release-version.sh` and emits a stub the operator reviews + signs off |
|
|
89
89
|
| Security summary | `compliance/evidence/REQ-XXX/security-summary.md` (Step 4) | `compliance/security-summary-<version>.md` at the compliance root (release-scoped, not REQ-scoped) | **Yes** — `generate-security-summary.sh` scrapes the SAST + dep-audit gate JSON and emits a stub with an operator sign-off block |
|
|
90
90
|
|
|
@@ -168,7 +168,7 @@ Records the design-time decisions before listing run results — what was tested
|
|
|
168
168
|
## Gate Results
|
|
169
169
|
|
|
170
170
|
| Gate | Result | Details |
|
|
171
|
-
|
|
171
|
+
| ------ | -------- | --------- |
|
|
172
172
|
| TypeScript | PASS | 0 errors |
|
|
173
173
|
| SAST | PASS | [N] findings ([N] baseline) |
|
|
174
174
|
| Dependency Audit | PASS | [N] unaccepted high/critical |
|
|
@@ -180,7 +180,7 @@ Records the design-time decisions before listing run results — what was tested
|
|
|
180
180
|
Required when E2E gate result is `SKIPPED`. Optional when no accepted skips exist.
|
|
181
181
|
|
|
182
182
|
| Spec | Test | REQ/AC | Classification | Resolution attempted | Approved by | Rationale |
|
|
183
|
-
|
|
183
|
+
| ------ | ------ | -------- | --------------- | --------------------- | ------------- | ---------- |
|
|
184
184
|
| e2e/foo.spec.ts | "AC3: bar dialog" | REQ-XXX/AC3 | intentional_non_applicability | N/A — API-only change | [operator] | API-only change, no UI surface |
|
|
185
185
|
|
|
186
186
|
Rules:
|
|
@@ -191,11 +191,25 @@ Rules:
|
|
|
191
191
|
|
|
192
192
|
## Test Cycles
|
|
193
193
|
|
|
194
|
-
|
|
195
|
-
|-------|--------|-------------|------------|----------|------|
|
|
196
|
-
| #1 | [run_id] | [PASS/FAIL] | [N/N] | [N%] | [YYYY-MM-DD] |
|
|
194
|
+
Prefer the first-class release-journey / cycle API when the portal exposes it. Generate the table from the API response rather than inferring cycles from uploaded files:
|
|
197
195
|
|
|
198
|
-
|
|
196
|
+
```bash
|
|
197
|
+
# Example:
|
|
198
|
+
curl -s -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
|
|
199
|
+
"${DEVAUDIT_BASE_URL%/}/api/projects/<slug>/releases/REQ-XXX/journey" \
|
|
200
|
+
> /tmp/release-journey.json
|
|
201
|
+
bash scripts/render-test-cycles.sh /tmp/release-journey.json
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The helper renders a first-class table when `.cycles[]` is present, and falls back to legacy `testCycleId` grouping when the portal has not yet rolled out cycle records.
|
|
205
|
+
|
|
206
|
+
| Source Release | SDLC Stage | Cycle | Kind | Outcome | Workflow / Run | Related Evidence | Incident / Remediation | Date |
|
|
207
|
+
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
208
|
+
| REQ-XXX | 2 implement_test | #1 | quality_gate | passed | [Quality Gates](https://github.com/example/repo/actions/runs/123) (run 123, attempt 1) | quality-gates.json | None | 2026-07-16 11:03:00 |
|
|
209
|
+
| REQ-XXX | 2 implement_test | #2 | e2e | passed | [E2E Regression](https://github.com/example/repo/actions/runs/124) (run 124, attempt 2) | e2e-results.json, screenshots.zip | Incident #501 fixed in PR #507 | 2026-07-16 11:19:00 |
|
|
210
|
+
| REQ-089 | 4 uat_review | #1 | uat | passed | [UAT Review](https://devaudit.example/releases/REQ-089) | uat-checklist.md | None | 2026-07-16 12:02:00 |
|
|
211
|
+
|
|
212
|
+
**Final assessment:** [First-class cycle records show all stage cycles passed / N cycles failed — see incidents]
|
|
199
213
|
|
|
200
214
|
## Bundled Release Context
|
|
201
215
|
|
|
@@ -224,13 +238,13 @@ Required when `compliance/pending-releases/BUNDLED-CHANGES-REQ-XXX.md` exists fo
|
|
|
224
238
|
## Test Plan Coverage
|
|
225
239
|
|
|
226
240
|
| Acceptance Criterion | Status | Test |
|
|
227
|
-
|
|
241
|
+
| --------------------- | -------- | ------ |
|
|
228
242
|
| [From test-plan.md] | PASS | `[spec-file]::[test-name]` |
|
|
229
243
|
|
|
230
244
|
## Evidence Locations
|
|
231
245
|
|
|
232
246
|
| Evidence | Location |
|
|
233
|
-
|
|
247
|
+
| ---------- | ---------- |
|
|
234
248
|
| E2E results | DevAudit: [project]/REQ-XXX/e2e-results.json |
|
|
235
249
|
| SAST results | DevAudit: [project]/REQ-XXX/sast-results.json |
|
|
236
250
|
| Dependency audit | DevAudit: [project]/REQ-XXX/dependency-audit.json |
|
|
@@ -359,7 +373,7 @@ No incidents or defects were discovered during the test cycle for release `<vers
|
|
|
359
373
|
## Sign-off
|
|
360
374
|
|
|
361
375
|
| Role | Name | Date |
|
|
362
|
-
|
|
376
|
+
| --- | --- | --- |
|
|
363
377
|
| Test lead | REPLACE — assign | REPLACE |
|
|
364
378
|
| Engineering lead | REPLACE — assign | REPLACE |
|
|
365
379
|
EOF
|
|
@@ -480,7 +494,7 @@ The test-scope artefact carries an **SRS-ID cross-reference table** so each test
|
|
|
480
494
|
## SRS coverage
|
|
481
495
|
|
|
482
496
|
| Test (file) | AC | SRS item |
|
|
483
|
-
|
|
497
|
+
| --- | --- | --- |
|
|
484
498
|
| e2e/admin-order-flow.spec.ts | AC1 | REQ-ORDER-005 |
|
|
485
499
|
| services/order-service.test.ts | AC2 | REQ-INV-010 |
|
|
486
500
|
| e2e/incident-dashboard.spec.ts | AC9 | REQ-OPS-001 |
|
|
@@ -538,7 +552,7 @@ Required when `compliance/pending-releases/BUNDLED-CHANGES-REQ-XXX.md` exists or
|
|
|
538
552
|
## AI Contributors
|
|
539
553
|
|
|
540
554
|
| Tool | Version | Session | Commits | Date Range |
|
|
541
|
-
|
|
555
|
+
| ------ | --------- | --------- | --------- | ------------ |
|
|
542
556
|
| [tool / none] | [version] | [session id] | [N] | [YYYY-MM-DD to YYYY-MM-DD] |
|
|
543
557
|
|
|
544
558
|
**Handoffs:** [None / list — see `ai-agent-handoff.md` if applicable]
|
|
@@ -557,13 +571,13 @@ Required when `compliance/pending-releases/BUNDLED-CHANGES-REQ-XXX.md` exists or
|
|
|
557
571
|
|
|
558
572
|
## Test Evidence
|
|
559
573
|
| Test Type | Count | Passed | Failed | Evidence Location |
|
|
560
|
-
|
|
574
|
+
| ----------- | ------- | -------- | -------- | ------------------- |
|
|
561
575
|
| E2E (Playwright) | [N] | [N] | 0 | DevAudit portal: [PROJECT_SLUG]/REQ-XXX |
|
|
562
576
|
| Unit | [N] | [N] | 0 | DevAudit portal: [PROJECT_SLUG]/REQ-XXX |
|
|
563
577
|
|
|
564
578
|
## Security Evidence
|
|
565
579
|
| Check | Result | Evidence Location |
|
|
566
|
-
|
|
580
|
+
| ------- | -------- | ------------------- |
|
|
567
581
|
| SAST | 0 high/critical | DevAudit portal: [PROJECT_SLUG]/REQ-XXX |
|
|
568
582
|
| Dependency Audit | 0 high/critical | DevAudit portal: [PROJECT_SLUG]/REQ-XXX |
|
|
569
583
|
| Access Control | [PASS/N/A] | Git: `compliance/evidence/REQ-XXX/security-summary.md` |
|
|
@@ -584,7 +598,7 @@ Required when `compliance/pending-releases/BUNDLED-CHANGES-REQ-XXX.md` exists or
|
|
|
584
598
|
## Post-Deploy Actions
|
|
585
599
|
|
|
586
600
|
| Type | Script / Command | Target | Required | Notes |
|
|
587
|
-
|
|
601
|
+
| ------ | ----------------- | -------- | ---------- | ------- |
|
|
588
602
|
| — | None | — | — | No post-deploy actions required |
|
|
589
603
|
|
|
590
604
|
<!-- Replace the "None" row above with actual actions if this release requires them:
|
|
@@ -612,7 +626,7 @@ Required when `compliance/pending-releases/BUNDLED-CHANGES-REQ-XXX.md` exists or
|
|
|
612
626
|
|
|
613
627
|
## Audit Trail
|
|
614
628
|
| Date | Action | Actor | Notes |
|
|
615
|
-
|
|
629
|
+
| ------ | -------- | ------- | ------- |
|
|
616
630
|
| [date] | Requirement created | [who] | Risk: [level] |
|
|
617
631
|
| [date] | Implementation completed | [who] | [details] |
|
|
618
632
|
| [date] | AI code reviewed | [reviewer] | [files] |
|
|
@@ -794,7 +808,7 @@ The script:
|
|
|
794
808
|
Required environment variables for the scripted path:
|
|
795
809
|
|
|
796
810
|
| Var | What it is | Where to set |
|
|
797
|
-
|
|
811
|
+
| --- | --- | --- |
|
|
798
812
|
| `DEVAUDIT_USER_TOKEN` | Personal Access Token (`mctok_…`) attributed to the running user | Issue at `/settings/tokens`; store as a repo secret for CI or `.env` for local |
|
|
799
813
|
| `DEVAUDIT_API_KEY` | Project-scoped API key (existing) | Already set for evidence uploads |
|
|
800
814
|
| `DEVAUDIT_BASE_URL` | DevAudit URL | Resolved by CI templates; locally read from `sdlc-config.json devaudit.base_url` |
|
|
@@ -121,7 +121,7 @@ gh pr create --base main --head develop --title "type: description" --body "$(ca
|
|
|
121
121
|
## Test Results (Local — Comprehensive)
|
|
122
122
|
|
|
123
123
|
| Gate | Result | Details |
|
|
124
|
-
|
|
124
|
+
| ------ | -------- | --------- |
|
|
125
125
|
| E2E Tests | [N]/[N] passed | Spec files: [list spec files that ran] |
|
|
126
126
|
| TypeScript | 0 errors | `npx tsc --noEmit` |
|
|
127
127
|
| SAST | 0 high/critical | [N] rules scanned, [N] files |
|
|
@@ -148,7 +148,7 @@ CI runs automatically on this PR. The following gates must pass before merge:
|
|
|
148
148
|
|
|
149
149
|
### Where to Find Test Results
|
|
150
150
|
| Source | Location | What It Shows |
|
|
151
|
-
|
|
151
|
+
| -------- | ---------- | --------------- |
|
|
152
152
|
| **CI status** | Green/red icons on PR commits | Pass/fail for each gate (independent, tamper-resistant) |
|
|
153
153
|
| **CI E2E comment** | PR comments (automated) | E2E pass/fail with commit SHA |
|
|
154
154
|
| **DevAudit evidence** | [View evidence on DevAudit](https://[DevAudit-URL]/projects/[PROJECT_SLUG]/requirements/REQ-XXX) | Playwright report, SAST results, dependency audit |
|