@metasession.co/devaudit-cli 0.1.60 → 0.1.62
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 +17 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/scripts/upload-evidence.sh +12 -0
- package/sdlc/files/_common/skills/e2e-test-engineer/SKILL.md +24 -0
- package/sdlc/files/_common/skills/sdlc-implementer/SKILL.md +33 -0
- package/sdlc/files/ci/ci.yml.template +55 -0
- package/sdlc/files/ci/compliance-evidence.yml.template +10 -6
- package/sdlc/files/ci/feature-e2e.yml.template +138 -0
- package/sdlc/files/ci/post-deploy-prod.yml.template +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metasession.co/devaudit-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.62",
|
|
4
4
|
"description": "DevAudit CLI — installs, syncs, and operates the Metasession SDLC across consumer projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@clack/prompts": "^0.8.2",
|
|
36
|
-
"@metasession.co/devaudit-plugin-sdk": "^0.1.
|
|
36
|
+
"@metasession.co/devaudit-plugin-sdk": "^0.1.62",
|
|
37
37
|
"ajv": "^8.20.0",
|
|
38
38
|
"commander": "^12.1.0",
|
|
39
39
|
"consola": "^3.2.3",
|
|
@@ -32,6 +32,11 @@
|
|
|
32
32
|
# as `gateStatus`; unknown values are
|
|
33
33
|
# silently dropped server-side.
|
|
34
34
|
# DevAudit-Installer#96.
|
|
35
|
+
# --sdlc-stage <1-5> SDLC stage that produced this artefact:
|
|
36
|
+
# 1 plan, 2 implement/test, 3 compile-evidence,
|
|
37
|
+
# 4 submit-for-review, 5 deploy. Forwarded as
|
|
38
|
+
# `sdlcStage`; unknown to older portals (ignored
|
|
39
|
+
# server-side, no error).
|
|
35
40
|
#
|
|
36
41
|
# Required environment variables:
|
|
37
42
|
# DEVAUDIT_BASE_URL e.g. https://meta-comply-production.up.railway.app
|
|
@@ -72,6 +77,7 @@ EVIDENCE_CATEGORY=""
|
|
|
72
77
|
RELEASE_TITLE=""
|
|
73
78
|
CHANGE_TYPE=""
|
|
74
79
|
GATE_STATUS=""
|
|
80
|
+
SDLC_STAGE=""
|
|
75
81
|
# Repeatable `--meta-key key=value` accumulator. Each pair gets merged
|
|
76
82
|
# into the metadata JSON sent to the portal. Used by the screenshot
|
|
77
83
|
# upload loop to pass `origin=feature|regression` from the per-PNG
|
|
@@ -96,6 +102,7 @@ while [ "$#" -gt 0 ]; do
|
|
|
96
102
|
# ran-and-failed != never-ran. Unknown values dropped server-side.
|
|
97
103
|
# DevAudit-Installer#96.
|
|
98
104
|
--gate-status) GATE_STATUS="$2"; shift 2 ;;
|
|
105
|
+
--sdlc-stage) SDLC_STAGE="$2"; shift 2 ;;
|
|
99
106
|
# --meta-key key=value (repeatable). Merged into the metadata JSON
|
|
100
107
|
# before posting. Validates the `key=value` shape; rejects bare
|
|
101
108
|
# keys without `=`.
|
|
@@ -119,6 +126,10 @@ if [ -n "$RELEASE_VERSION" ] && [ -z "$EVIDENCE_CATEGORY" ]; then
|
|
|
119
126
|
echo "Error: --category is required when --release is specified (gate validation)"
|
|
120
127
|
exit 1
|
|
121
128
|
fi
|
|
129
|
+
if [ -n "$SDLC_STAGE" ] && ! [[ "$SDLC_STAGE" =~ ^[1-5]$ ]]; then
|
|
130
|
+
echo "Error: --sdlc-stage must be an integer 1-5 (got: $SDLC_STAGE)"
|
|
131
|
+
exit 1
|
|
132
|
+
fi
|
|
122
133
|
|
|
123
134
|
if [ -z "${DEVAUDIT_BASE_URL:-}" ]; then
|
|
124
135
|
echo "Error: DEVAUDIT_BASE_URL environment variable is required"
|
|
@@ -277,6 +288,7 @@ for FILE in "${FILES[@]}"; do
|
|
|
277
288
|
[ -n "$RELEASE_TITLE" ] && CURL_ARGS+=(-F "releaseTitle=${RELEASE_TITLE}")
|
|
278
289
|
[ -n "$CHANGE_TYPE" ] && CURL_ARGS+=(-F "changeType=${CHANGE_TYPE}")
|
|
279
290
|
[ -n "$GATE_STATUS" ] && CURL_ARGS+=(-F "gateStatus=${GATE_STATUS}")
|
|
291
|
+
[ -n "$SDLC_STAGE" ] && CURL_ARGS+=(-F "sdlcStage=${SDLC_STAGE}")
|
|
280
292
|
|
|
281
293
|
ATTEMPT=1
|
|
282
294
|
BACKOFF=$INITIAL_BACKOFF_SECONDS
|
|
@@ -189,6 +189,30 @@ For **visual regression** specifically:
|
|
|
189
189
|
|
|
190
190
|
Do additions, updates, and (approved) deletions in the same change so the suite stays internally consistent.
|
|
191
191
|
|
|
192
|
+
### Phase 5½ — Evidence wiring validation
|
|
193
|
+
|
|
194
|
+
Before running the suite (Phase 6), verify that the evidence traceability wiring is in place. A spec can pass Phase 6's "AC covered" check by having a correct assertion without ever calling `evidenceShot()` or tagging `@requirement` — producing zero portal evidence and letting the release reach UAT with no traceable screenshots (DevAudit-Installer #170, #169).
|
|
195
|
+
|
|
196
|
+
**For each in-scope REQ and its ACs from Phase 2's scenario table:**
|
|
197
|
+
|
|
198
|
+
1. **Check `@requirement` annotation.** Grep the authored/modified spec files for `@requirement REQ-XXX` tags. Every spec file that covers an in-scope REQ must carry at least one `@requirement REQ-XXX` annotation so the CI's `detect-req` step and the portal's evidence-by-requirement view can find it.
|
|
199
|
+
|
|
200
|
+
2. **Check `evidenceShot()` calls.** For each UI spec covering an in-scope REQ, grep for at least one `evidenceShot(page, 'REQ-XXX', <ac>, ...)` call per AC. The call must be placed **at the assertion that proves the AC**, before any further interaction or navigation. API-only specs that don't have a visual surface are exempt — note the exemption in the test-execution-summary.
|
|
201
|
+
|
|
202
|
+
3. **Check test title annotations.** Each test for an in-scope REQ must carry a `[REQ-XXX]` tag in the test title or `test.info().annotations` so the Playwright JSON reporter emits the REQ association. The portal uses this to map test results to requirements.
|
|
203
|
+
|
|
204
|
+
**If any check fails:**
|
|
205
|
+
|
|
206
|
+
Halt and report the gap to the user:
|
|
207
|
+
> Evidence wiring incomplete for REQ-XXX:
|
|
208
|
+
> - Missing `@requirement REQ-XXX` annotation in `e2e/<area>/foo.spec.ts`
|
|
209
|
+
> - Missing `evidenceShot()` call for AC2 in `e2e/<area>/bar.spec.ts`
|
|
210
|
+
> - Missing `[REQ-XXX]` tag in test title for "should filter by status"
|
|
211
|
+
>
|
|
212
|
+
> These must be fixed before running the suite — without them the portal will show zero screenshots and zero tagged tests for this REQ, and the CI gate (#169) will block the release.
|
|
213
|
+
|
|
214
|
+
Do **not** proceed to Phase 6 until all gaps are resolved. The user may choose to skip an AC (e.g. API-only, transport-only) — that's valid, but it must be an explicit decision recorded in the test-execution-summary, not an omission.
|
|
215
|
+
|
|
192
216
|
### Phase 6 — Execute and report
|
|
193
217
|
|
|
194
218
|
Run the suite. Strategy:
|
|
@@ -59,6 +59,39 @@ The only pauses in the whole workflow are the explicitly-named checkpoints:
|
|
|
59
59
|
|
|
60
60
|
Everything else is silent continuation. The rule is **opt-in-to-pause, not opt-out-of-pause**. If you find yourself stopping after a sub-skill's "Return to the running `sdlc-implementer` context" line and waiting for the operator to ask _"is anything happening?"_ — that is the bug this section exists to prevent. Keep going.
|
|
61
61
|
|
|
62
|
+
## Scope-expansion halt gate (devaudit-installer#171)
|
|
63
|
+
|
|
64
|
+
The change-request loop's scope-expansion halt rule (see [`references/change-request-loop.md`](./references/change-request-loop.md) §"If the change-request is fundamentally a different REQ") only fires during Phase 5 — after a UAT reviewer clicks "Request Changes" and the portal state is `uat_changes_requested`. In practice, scope-expanding requests also arrive from the user at any point after Phase 1:
|
|
65
|
+
|
|
66
|
+
- During Phase 2 (implementation) — user spots a related improvement and asks the agent to add it
|
|
67
|
+
- During Phase 3 (evidence compile) — user asks for an additional evidence artefact not in the plan
|
|
68
|
+
- During Phase 4 (UAT submission) — user exercises the deployed app and asks for a behaviour change
|
|
69
|
+
- Between UAT submission and approval — same scenario
|
|
70
|
+
- After approval but before merge — user makes a last-minute request
|
|
71
|
+
|
|
72
|
+
In all these cases, the portal state is NOT `uat_changes_requested`, so the change-request loop's halt rule never fires. The agent implements the out-of-scope change without flagging it.
|
|
73
|
+
|
|
74
|
+
**This gate fires on every user work request while a REQ is active, in any phase.**
|
|
75
|
+
|
|
76
|
+
When the user asks for a change that goes beyond the current REQ's acceptance criteria:
|
|
77
|
+
|
|
78
|
+
1. **Read the current REQ's `test-scope.md`** (or `implementation-plan.md` § Acceptance Criteria if no test-scope exists yet) to identify the defined ACs.
|
|
79
|
+
|
|
80
|
+
2. **Check whether the user's request maps to an existing AC.** If yes — proceed (or enter the change-request loop if in Phase 5).
|
|
81
|
+
|
|
82
|
+
3. **If no — halt with the scope-expansion message:**
|
|
83
|
+
|
|
84
|
+
> This request adds behaviour outside REQ-XXX's acceptance criteria (AC1–ACn defined in test-scope.md). Recommend filing a separate issue for \<subject\> and shipping REQ-XXX as originally scoped.
|
|
85
|
+
|
|
86
|
+
4. **Wait for the user to confirm one of:**
|
|
87
|
+
- **(a) File a separate issue** (new REQ) — ship the current REQ as-is. The agent continues with the original scope.
|
|
88
|
+
- **(b) Amend REQ-XXX's scope** — explicitly expand `test-scope.md` / `implementation-plan.md`, update the plan, invalidate existing evidence, and re-walk Stage 3. This option carries a warning: *"Amending scope after evidence is compiled (Stage 3+) invalidates the existing test-execution-summary, screenshots, and UAT verification. All Stage 3 evidence must be re-compiled."*
|
|
89
|
+
- **(c) Abandon the request** — do nothing, continue with the original scope.
|
|
90
|
+
|
|
91
|
+
**Do not implement the out-of-scope change before the user picks (a), (b), or (c).** The inertia trap is real: the agent is mid-flow, the codebase is open, and the request sounds reasonable. The gate exists to interrupt that inertia — STOP, surface the scope gap, and let the user decide.
|
|
92
|
+
|
|
93
|
+
This gate is distinct from Phase 2 step 4's "any deviation from the plan must be noted" rule: that covers implementation *approach* deviations (how to build something the plan says to build), not *scope* expansions (building something the plan doesn't say to build at all).
|
|
94
|
+
|
|
62
95
|
## SDLC navigability — LAST/NEXT status sticky (devaudit#131)
|
|
63
96
|
|
|
64
97
|
Long-running SDLC issues accumulate dozens of comments across multiple Claude Code sessions. The operator returning to the thread should be able to answer two questions in under five seconds:
|
|
@@ -412,6 +412,7 @@ jobs:
|
|
|
412
412
|
FLAGS="--git-sha ${{ github.sha }} --ci-run-id ${{ github.run_id }} --branch ${{ github.ref_name }}"
|
|
413
413
|
FLAGS="${FLAGS} --release ${{ needs.register-release.outputs.version }} --create-release-if-missing"
|
|
414
414
|
FLAGS="${FLAGS} --environment uat"
|
|
415
|
+
FLAGS="${FLAGS} --sdlc-stage 2"
|
|
415
416
|
|
|
416
417
|
# Track failures across all uploads so we can fail the job at the
|
|
417
418
|
# end with the full picture. Previously each upload used
|
|
@@ -548,6 +549,8 @@ jobs:
|
|
|
548
549
|
# compliance/pending-releases/. Failures now bump UPLOAD_FAILURES
|
|
549
550
|
# so the step exits non-zero and the release PR turns red.
|
|
550
551
|
SHOT_REQS=()
|
|
552
|
+
ZERO_Screenshot_REQS=()
|
|
553
|
+
EVIDENCE_GAPS=0
|
|
551
554
|
if [ -d compliance/pending-releases ]; then
|
|
552
555
|
for TICKET in compliance/pending-releases/RELEASE-TICKET-REQ-*.md; do
|
|
553
556
|
[ -f "$TICKET" ] || continue
|
|
@@ -579,6 +582,12 @@ jobs:
|
|
|
579
582
|
REQ_SHOTS=(ci-evidence/compliance/evidence/"$REQ"/screenshots/*.png)
|
|
580
583
|
if [ "${#REQ_SHOTS[@]}" -eq 0 ]; then
|
|
581
584
|
echo "No per-AC screenshots for ${REQ} (none captured by evidenceShot this run)"
|
|
585
|
+
# devaudit-installer#169 — track REQs with zero screenshots
|
|
586
|
+
# so the evidence-completeness gate below can fail the job
|
|
587
|
+
# if the REQ also has zero tagged tests in the Playwright
|
|
588
|
+
# report. Previously this was a silent continue — the release
|
|
589
|
+
# proceeded to UAT with no traceable evidence.
|
|
590
|
+
ZERO_Screenshot_REQS+=("$REQ")
|
|
582
591
|
continue
|
|
583
592
|
fi
|
|
584
593
|
echo "Uploading ${#REQ_SHOTS[@]} screenshot(s) for ${REQ}"
|
|
@@ -625,6 +634,52 @@ jobs:
|
|
|
625
634
|
fi
|
|
626
635
|
shopt -u nullglob
|
|
627
636
|
|
|
637
|
+
# devaudit-installer#169 — Evidence-completeness gate.
|
|
638
|
+
# For each in-scope REQ that had zero screenshots, check whether
|
|
639
|
+
# the Playwright JSON report contains at least one test tagged
|
|
640
|
+
# with that REQ. If a REQ has zero screenshots AND zero tagged
|
|
641
|
+
# tests, the release has no traceable evidence and must not
|
|
642
|
+
# proceed to UAT. Previously this was a silent pass — the release
|
|
643
|
+
# arrived in UAT review with "SCREENSHOTS: none" and "TESTS: no
|
|
644
|
+
# tests in report tagged with this REQ" on the portal.
|
|
645
|
+
if [ "${#ZERO_Screenshot_REQS[@]}" -gt 0 ]; then
|
|
646
|
+
for REQ in "${ZERO_Screenshot_REQS[@]}"; do
|
|
647
|
+
TAGGED_COUNT=0
|
|
648
|
+
if [ -f ci-evidence/e2e-results.json ]; then
|
|
649
|
+
TAGGED_COUNT=$(python3 -c "
|
|
650
|
+
import json, sys
|
|
651
|
+
with open('ci-evidence/e2e-results.json') as f:
|
|
652
|
+
data = json.load(f)
|
|
653
|
+
def count(specs):
|
|
654
|
+
n = 0
|
|
655
|
+
for s in specs:
|
|
656
|
+
tags = []
|
|
657
|
+
for a in s.get('annotations', []):
|
|
658
|
+
tags.append(a.get('type','') + ':' + a.get('description',''))
|
|
659
|
+
for t in s.get('tags', []):
|
|
660
|
+
tags.append(t)
|
|
661
|
+
title = s.get('title', '')
|
|
662
|
+
if '${REQ}' in title or any('${REQ}' in t for t in tags):
|
|
663
|
+
n += 1
|
|
664
|
+
n += count(s.get('suites', []))
|
|
665
|
+
return n
|
|
666
|
+
print(count(data.get('suites', [])))
|
|
667
|
+
" 2>/dev/null || echo "0")
|
|
668
|
+
fi
|
|
669
|
+
if [ "$TAGGED_COUNT" -eq 0 ]; then
|
|
670
|
+
echo "::error::Evidence completeness gate: ${REQ} has zero tagged tests AND zero per-AC screenshots. The release cannot proceed to UAT with no traceable evidence. Ensure specs carry @requirement ${REQ} annotations and evidenceShot() calls (skill Phase 5½, issue #170)."
|
|
671
|
+
EVIDENCE_GAPS=$((EVIDENCE_GAPS + 1))
|
|
672
|
+
else
|
|
673
|
+
echo "${REQ} has zero screenshots but ${TAGGED_COUNT} tagged test(s) — evidence-completeness gate passed (screenshot gap is non-blocking)"
|
|
674
|
+
fi
|
|
675
|
+
done
|
|
676
|
+
fi
|
|
677
|
+
|
|
678
|
+
if [ "$EVIDENCE_GAPS" -gt 0 ]; then
|
|
679
|
+
echo "::error::${EVIDENCE_GAPS} in-scope REQ(s) have zero traceable evidence (no tagged tests, no screenshots) — release cannot pass UAT review"
|
|
680
|
+
exit 1
|
|
681
|
+
fi
|
|
682
|
+
|
|
628
683
|
# NOTE: committed compliance docs (planning category: RTM/test-plan/
|
|
629
684
|
# test-cases, release tickets, and per-requirement
|
|
630
685
|
# compliance/evidence/REQ-*/ folders) are intentionally NOT uploaded
|
|
@@ -237,7 +237,7 @@ jobs:
|
|
|
237
237
|
# untagged docs commit must not sweep every in-scope REQ into a date
|
|
238
238
|
# release. Note: upload-evidence.sh keeps the LAST --release seen.
|
|
239
239
|
FLAGS="--git-sha ${{ github.sha }} --ci-run-id ${{ github.run_id }} --branch ${{ github.ref_name }}"
|
|
240
|
-
FLAGS="${FLAGS} --create-release-if-missing --environment uat"
|
|
240
|
+
FLAGS="${FLAGS} --create-release-if-missing --environment uat --sdlc-stage 3"
|
|
241
241
|
DERIVED_RELEASE="${{ steps.version.outputs.version }}"
|
|
242
242
|
|
|
243
243
|
# Derive change_type for the bare-date (housekeeping) DERIVED_RELEASE:
|
|
@@ -586,9 +586,9 @@ jobs:
|
|
|
586
586
|
PRIOR_EVENT="${{ github.event.workflow_run.event }}"
|
|
587
587
|
PRIOR_BRANCH="${{ github.event.workflow_run.head_branch }}"
|
|
588
588
|
case "$PRIOR_EVENT" in
|
|
589
|
-
pull_request) TIER=critical ;;
|
|
590
|
-
push) TIER=regression ;;
|
|
591
|
-
*) TIER="${PRIOR_EVENT}" ;;
|
|
589
|
+
pull_request) TIER=critical; STAGE=2 ;;
|
|
590
|
+
push) TIER=regression; STAGE=5 ;;
|
|
591
|
+
*) TIER="${PRIOR_EVENT}"; STAGE="" ;;
|
|
592
592
|
esac
|
|
593
593
|
|
|
594
594
|
# Common flags for upload-evidence.sh. Branch + SHA come from
|
|
@@ -597,6 +597,7 @@ jobs:
|
|
|
597
597
|
--git-sha ${{ github.event.workflow_run.head_sha }} \
|
|
598
598
|
--ci-run-id ${{ github.event.workflow_run.id }} \
|
|
599
599
|
--branch ${PRIOR_BRANCH}"
|
|
600
|
+
[ -n "$STAGE" ] && FLAGS="${FLAGS} --sdlc-stage ${STAGE}"
|
|
600
601
|
|
|
601
602
|
# In-scope REQs from pending release tickets at the triggering
|
|
602
603
|
# SHA. Fall back to `_compliance-docs` if no tickets present so
|
|
@@ -634,10 +635,13 @@ jobs:
|
|
|
634
635
|
# facing artefact (the formatted run summary). Skipped if
|
|
635
636
|
# the report directory is missing (e.g. an upstream
|
|
636
637
|
# infrastructure failure aborted before reporting).
|
|
637
|
-
if [ -
|
|
638
|
+
if [ -d e2e-artifacts/playwright-report ]; then
|
|
639
|
+
(cd e2e-artifacts && zip -qr playwright-report.zip playwright-report/) 2>/dev/null || true
|
|
640
|
+
fi
|
|
641
|
+
if [ -f e2e-artifacts/playwright-report.zip ]; then
|
|
638
642
|
if bash scripts/upload-evidence.sh \
|
|
639
643
|
{{PROJECT_SLUG}} "$REQ" test_report \
|
|
640
|
-
e2e-artifacts/playwright-report
|
|
644
|
+
e2e-artifacts/playwright-report.zip \
|
|
641
645
|
--category test_report ${FLAGS} --release "${DERIVED_RELEASE}" \
|
|
642
646
|
--meta-key "tier=${TIER}"
|
|
643
647
|
then
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Feature-branch in-scope E2E (DEVAUDIT-003, issue #174)
|
|
2
|
+
#
|
|
3
|
+
# Generated by `devaudit install` / `devaudit update` from sdlc-config.json.
|
|
4
|
+
# Runs the E2E specs tagged with the branch's REQ on PRs to the integration
|
|
5
|
+
# branch, so regression-tier bugs surface during the feature cycle instead of
|
|
6
|
+
# at the release gate. Uploads the report as stage-2, origin=feature evidence
|
|
7
|
+
# so it does NOT co-mingle with the stage-3/5 regression runs and is NOT
|
|
8
|
+
# counted as release/UAT evidence by the portal.
|
|
9
|
+
name: Feature In-Scope E2E
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
pull_request:
|
|
13
|
+
branches: [develop]
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
detect-req:
|
|
17
|
+
name: Detect REQ from branch
|
|
18
|
+
runs-on: {{RUNNER}}
|
|
19
|
+
outputs:
|
|
20
|
+
req_id: ${{ steps.detect.outputs.req_id }}
|
|
21
|
+
has_tests: ${{ steps.detect.outputs.has_tests }}
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v6
|
|
24
|
+
- name: Parse REQ from branch name
|
|
25
|
+
id: detect
|
|
26
|
+
run: |
|
|
27
|
+
BRANCH="${{ github.head_ref }}"
|
|
28
|
+
if [[ $BRANCH =~ (REQ-[0-9]+) ]]; then
|
|
29
|
+
REQ_ID="${BASH_REMATCH[1]}"
|
|
30
|
+
echo "req_id=$REQ_ID" >> "$GITHUB_OUTPUT"
|
|
31
|
+
if grep -rl "@requirement $REQ_ID" e2e/ --include="*.spec.ts" >/dev/null 2>&1; then
|
|
32
|
+
echo "has_tests=true" >> "$GITHUB_OUTPUT"
|
|
33
|
+
else
|
|
34
|
+
echo "has_tests=false" >> "$GITHUB_OUTPUT"
|
|
35
|
+
fi
|
|
36
|
+
else
|
|
37
|
+
echo "req_id=none" >> "$GITHUB_OUTPUT"
|
|
38
|
+
echo "has_tests=false" >> "$GITHUB_OUTPUT"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
run-feature-e2e:
|
|
42
|
+
name: Run in-scope E2E
|
|
43
|
+
needs: detect-req
|
|
44
|
+
if: needs.detect-req.outputs.has_tests == 'true'
|
|
45
|
+
runs-on: {{RUNNER}}
|
|
46
|
+
|
|
47
|
+
services:
|
|
48
|
+
{{DATABASE_SERVICE}}:
|
|
49
|
+
image: {{DATABASE_IMAGE}}
|
|
50
|
+
ports:
|
|
51
|
+
- {{DATABASE_PORT}}
|
|
52
|
+
|
|
53
|
+
env:
|
|
54
|
+
{{DATABASE_ENV}}
|
|
55
|
+
{{APP_ENV}}
|
|
56
|
+
DEVAUDIT_BASE_URL_VAR: ${{ vars.DEVAUDIT_BASE_URL }}
|
|
57
|
+
DEVAUDIT_API_KEY: ${{ secrets.DEVAUDIT_API_KEY }}
|
|
58
|
+
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v6
|
|
61
|
+
with:
|
|
62
|
+
fetch-depth: 0
|
|
63
|
+
- uses: actions/setup-node@v4
|
|
64
|
+
with:
|
|
65
|
+
node-version: {{NODE_VERSION}}
|
|
66
|
+
- name: Install dependencies (skip if lockfile unchanged)
|
|
67
|
+
run: |
|
|
68
|
+
LOCK_HASH=$(sha256sum package-lock.json | cut -d' ' -f1)
|
|
69
|
+
if [ -f node_modules/.lock-hash ] && [ "$(cat node_modules/.lock-hash)" = "$LOCK_HASH" ]; then
|
|
70
|
+
echo "node_modules up to date — skipping npm ci"
|
|
71
|
+
else
|
|
72
|
+
npm ci
|
|
73
|
+
echo "$LOCK_HASH" > node_modules/.lock-hash
|
|
74
|
+
fi
|
|
75
|
+
- name: Install Playwright (skip if already cached)
|
|
76
|
+
run: npx playwright install --with-deps chromium 2>/dev/null || npx playwright install chromium
|
|
77
|
+
|
|
78
|
+
{{DATABASE_URI_STEP}}
|
|
79
|
+
{{E2E_SETUP_STEP}}
|
|
80
|
+
- name: Kill stale dev server
|
|
81
|
+
run: lsof -ti:3000 | xargs kill -9 2>/dev/null || true
|
|
82
|
+
|
|
83
|
+
{{E2E_DEV_SERVER_STEP}}
|
|
84
|
+
|
|
85
|
+
- name: Wait for dev server
|
|
86
|
+
run: npx wait-on http://localhost:3000 --timeout 120000
|
|
87
|
+
|
|
88
|
+
# Compute the set of e2e spec files added on this branch (relative
|
|
89
|
+
# to the merge base). The evidenceShot helper reads E2E_NEW_SPECS
|
|
90
|
+
# at capture time and tags each screenshot `origin=feature` when
|
|
91
|
+
# its spec appears in the list, else `origin=regression`.
|
|
92
|
+
- name: Compute new-spec list for E2E origin tagging
|
|
93
|
+
run: |
|
|
94
|
+
BASE_REF="${{ github.base_ref || 'develop' }}"
|
|
95
|
+
BASE_SHA=$(git merge-base "origin/${BASE_REF}" HEAD 2>/dev/null || git rev-parse HEAD)
|
|
96
|
+
NEW_SPECS=$(git diff --name-only --diff-filter=A "${BASE_SHA}...HEAD" \
|
|
97
|
+
-- 'tests/e2e/**/*.spec.ts' 'tests/e2e/**/*.spec.tsx' \
|
|
98
|
+
'e2e/**/*.spec.ts' 'e2e/**/*.spec.tsx' 2>/dev/null || true)
|
|
99
|
+
{
|
|
100
|
+
echo "E2E_NEW_SPECS<<EOF"
|
|
101
|
+
echo "$NEW_SPECS"
|
|
102
|
+
echo "EOF"
|
|
103
|
+
} >> "$GITHUB_ENV"
|
|
104
|
+
if [ -n "$NEW_SPECS" ]; then
|
|
105
|
+
echo "New e2e specs on this branch (origin=feature for captures):"
|
|
106
|
+
echo "$NEW_SPECS" | sed 's/^/ /'
|
|
107
|
+
else
|
|
108
|
+
echo "No new e2e specs detected — all captures will tag origin=regression."
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
{{E2E_FEATURE_TEST_STEP}}
|
|
112
|
+
- name: Upload feature E2E evidence (stage 2, origin=feature)
|
|
113
|
+
if: always()
|
|
114
|
+
run: |
|
|
115
|
+
REQ_ID="${{ needs.detect-req.outputs.req_id }}"
|
|
116
|
+
CONFIG_URL=""
|
|
117
|
+
if [ -f sdlc-config.json ]; then
|
|
118
|
+
CONFIG_URL=$(jq -r '.devaudit.base_url // empty' sdlc-config.json 2>/dev/null || true)
|
|
119
|
+
fi
|
|
120
|
+
BASE="${CONFIG_URL:-$DEVAUDIT_BASE_URL_VAR}"
|
|
121
|
+
if [ -z "$BASE" ] || [ -z "$DEVAUDIT_API_KEY" ]; then
|
|
122
|
+
echo "::warning::DevAudit not configured — skipping feature E2E upload."
|
|
123
|
+
exit 0
|
|
124
|
+
fi
|
|
125
|
+
export DEVAUDIT_BASE_URL="${BASE%/}"
|
|
126
|
+
if [ -d playwright-report ]; then
|
|
127
|
+
zip -qr playwright-report.zip playwright-report/ 2>/dev/null || true
|
|
128
|
+
fi
|
|
129
|
+
if [ -f playwright-report.zip ]; then
|
|
130
|
+
bash scripts/upload-evidence.sh \
|
|
131
|
+
{{PROJECT_SLUG}} "$REQ_ID" test_report playwright-report.zip \
|
|
132
|
+
--category test_report --release "$REQ_ID" --create-release-if-missing \
|
|
133
|
+
--environment uat --sdlc-stage 2 \
|
|
134
|
+
--git-sha "${{ github.event.pull_request.head.sha }}" \
|
|
135
|
+
--ci-run-id "${{ github.run_id }}" --branch "${{ github.head_ref }}" \
|
|
136
|
+
--meta-key "origin=feature" \
|
|
137
|
+
|| echo "::warning::feature E2E report upload failed"
|
|
138
|
+
fi
|
|
@@ -164,7 +164,7 @@ jobs:
|
|
|
164
164
|
bash scripts/upload-evidence.sh \
|
|
165
165
|
"${PROJECT_SLUG}" "${VERSION}" test_report prod-smoke-results.json \
|
|
166
166
|
--release "${VERSION}" --create-release-if-missing --environment production \
|
|
167
|
-
--category test_report --git-sha "${GIT_SHA}" --ci-run-id "${CI_RUN}" --branch main \
|
|
167
|
+
--category test_report --sdlc-stage 5 --git-sha "${GIT_SHA}" --ci-run-id "${CI_RUN}" --branch main \
|
|
168
168
|
|| echo "Warning: smoke upload failed for ${VERSION}"
|
|
169
169
|
fi
|
|
170
170
|
# Carry the release ticket into the production environment so the
|
|
@@ -179,7 +179,7 @@ jobs:
|
|
|
179
179
|
bash scripts/upload-evidence.sh \
|
|
180
180
|
"${PROJECT_SLUG}" "${VERSION}" compliance_document "$TICKET" \
|
|
181
181
|
--release "${VERSION}" --create-release-if-missing --environment production \
|
|
182
|
-
--category release_artifact --git-sha "${GIT_SHA}" --ci-run-id "${CI_RUN}" --branch main \
|
|
182
|
+
--category release_artifact --sdlc-stage 5 --git-sha "${GIT_SHA}" --ci-run-id "${CI_RUN}" --branch main \
|
|
183
183
|
|| echo "Warning: ticket upload failed for ${VERSION}"
|
|
184
184
|
else
|
|
185
185
|
echo "No RELEASE-TICKET-${VERSION}.md found — skipping ticket (date-versioned or archived)."
|