@metasession.co/devaudit-cli 0.1.53 → 0.1.54
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 +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/scripts/upload-evidence.sh +41 -1
- package/sdlc/files/_common/skills/adr-author/SKILL.md +1 -1
- package/sdlc/files/_common/skills/requirements-aligner/SKILL.md +1 -1
- package/sdlc/files/_common/skills/risk-register-keeper/SKILL.md +1 -1
- package/sdlc/files/_common/skills/sdlc-implementer/SKILL.md +21 -1
- package/sdlc/files/ci/ci.yml.template +40 -14
- package/sdlc/files/ci/compliance-evidence.yml.template +238 -16
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.54",
|
|
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.54",
|
|
37
37
|
"commander": "^12.1.0",
|
|
38
38
|
"consola": "^3.2.3",
|
|
39
39
|
"env-paths": "^3.0.0",
|
|
@@ -133,6 +133,42 @@ fi
|
|
|
133
133
|
# Strip any trailing slash so we don't double-up later.
|
|
134
134
|
DEVAUDIT_BASE_URL="${DEVAUDIT_BASE_URL%/}"
|
|
135
135
|
|
|
136
|
+
# --- Base-URL drift check (devaudit-installer#143) ---
|
|
137
|
+
# When the portal moves host (e.g. devaudit.metasession.co → devaudit.ai)
|
|
138
|
+
# Cloudflare / the origin replies 301/302 with a Location header pointing
|
|
139
|
+
# at the new host. Every consumer's CI that still uses the old base URL
|
|
140
|
+
# fails every upload until DEVAUDIT_BASE_URL is rotated. We don't want
|
|
141
|
+
# the failure mode to be a silent "evidence didn't upload" — surface the
|
|
142
|
+
# drift loudly at the top of the run so the operator knows to rotate the
|
|
143
|
+
# secret. (We still upload via `curl -L` so the run itself succeeds; the
|
|
144
|
+
# warning is the nudge to fix the secret, not a hard stop.)
|
|
145
|
+
probe_base_url_drift() {
|
|
146
|
+
# `${var:-}` so `set -u` doesn't trip if curl isn't installed or the
|
|
147
|
+
# network is offline. Probe with -I (HEAD); fall back to GET if HEAD
|
|
148
|
+
# is rejected by the upstream proxy. 5s connect + 10s overall is
|
|
149
|
+
# plenty for a redirect-only probe — we never read a body.
|
|
150
|
+
local probe_url="${DEVAUDIT_BASE_URL}/api/health"
|
|
151
|
+
local resp
|
|
152
|
+
resp=$(curl -sI -o /dev/null --max-time 10 --connect-timeout 5 \
|
|
153
|
+
-w "%{http_code} %{redirect_url}" "$probe_url" 2>/dev/null || true)
|
|
154
|
+
local code="${resp%% *}"
|
|
155
|
+
local redirect_url="${resp#* }"
|
|
156
|
+
if [[ "$code" =~ ^30[1278]$ ]] && [ -n "$redirect_url" ] && [ "$redirect_url" != " " ]; then
|
|
157
|
+
local old_host new_host
|
|
158
|
+
old_host=$(printf '%s' "$DEVAUDIT_BASE_URL" | sed -E 's|^https?://([^/]+).*|\1|')
|
|
159
|
+
new_host=$(printf '%s' "$redirect_url" | sed -E 's|^https?://([^/]+).*|\1|')
|
|
160
|
+
if [ -n "$new_host" ] && [ "$old_host" != "$new_host" ]; then
|
|
161
|
+
echo "WARNING: DEVAUDIT_BASE_URL host '${old_host}' redirects to '${new_host}'."
|
|
162
|
+
echo " Rotate the DEVAUDIT_BASE_URL secret in your CI environment to"
|
|
163
|
+
echo " the new host to avoid silent breakage. (Uploads will still"
|
|
164
|
+
echo " succeed this run — curl follows the redirect — but the"
|
|
165
|
+
echo " underlying secret should be updated.)"
|
|
166
|
+
echo " Ref: https://github.com/metasession-dev/DevAudit-Installer/issues/143"
|
|
167
|
+
fi
|
|
168
|
+
fi
|
|
169
|
+
}
|
|
170
|
+
probe_base_url_drift
|
|
171
|
+
|
|
136
172
|
# --- Build metadata JSON ---
|
|
137
173
|
# Assemble entries first; only emit `{ ... }` if at least one field is
|
|
138
174
|
# set. Each entry is a `"key":"value"` JSON pair with the value
|
|
@@ -213,8 +249,12 @@ for FILE in "${FILES[@]}"; do
|
|
|
213
249
|
fi
|
|
214
250
|
FILE_SIZE=$(stat -c%s "$FILE" 2>/dev/null || stat -f%z "$FILE")
|
|
215
251
|
echo -n "Uploading ${FILENAME}... "
|
|
252
|
+
# `-L` follows 3xx redirects (devaudit-installer#143). The portal host
|
|
253
|
+
# has moved before (devaudit.metasession.co → devaudit.ai); without -L
|
|
254
|
+
# every consumer's CI silently fails on a stale base URL. `--max-redirs 3`
|
|
255
|
+
# bounds the follow so a misconfigured redirect loop can't hang CI.
|
|
216
256
|
CURL_ARGS=(
|
|
217
|
-
-X POST "$UPLOAD_URL"
|
|
257
|
+
-X POST -L --max-redirs 3 "$UPLOAD_URL"
|
|
218
258
|
-H "Authorization: Bearer ${DEVAUDIT_API_KEY}"
|
|
219
259
|
-F "file=@${FILE}"
|
|
220
260
|
-F "projectSlug=${PROJECT_SLUG}"
|
|
@@ -206,7 +206,7 @@ I have reviewed the ADR-worthiness verdict above and confirm:
|
|
|
206
206
|
|
|
207
207
|
**Step 2 — Tag for upload.** The CI's `compliance-evidence.yml` uploads this file as `evidence_type=architecture_decision` (added to META-COMPLY's `EVIDENCE_TYPE_REGISTRY` in the paired sub-PR). The framework-coverage matrix maps this to clauses per `framework-registry-auditor`'s review — see the META-COMPLY-side PR for the final clause attributions (v1 may ship orphan-by-design if the auditor rejects proposed mappings; see [`requirements-aligner`](../requirements-aligner/SKILL.md) for the precedent).
|
|
208
208
|
|
|
209
|
-
**Step 3 —
|
|
209
|
+
**Step 3 — Return to the running `sdlc-implementer` context.** The skill's job ends at the artefact + the operator sign-off. The orchestrator immediately continues with the rest of Stage 3 inline — no pause, no operator nudge needed. (Skills run in the same invocation context; control returns synchronously when this skill exits. See `sdlc-implementer/SKILL.md` § *Sub-skill return semantics*.)
|
|
210
210
|
|
|
211
211
|
### Phase 3 — Per-REQ ad-hoc audit
|
|
212
212
|
|
|
@@ -132,7 +132,7 @@ I have reviewed the AC-to-SRS-item traces above and confirm:
|
|
|
132
132
|
|
|
133
133
|
**Step 2 — Tag for upload.** The CI's `compliance-evidence.yml` uploads this file as `evidence_type=srs_alignment` (added to META-COMPLY's `EVIDENCE_TYPE_REGISTRY` in the paired sub-PR). The framework-coverage matrix then closes `ISO29119.3.4` (Test Plan — requirements traceability) and `SOC2.CC2.1` (Communication of policies — when paired with INSTRUCTIONS.md) for this REQ.
|
|
134
134
|
|
|
135
|
-
**Step 3 —
|
|
135
|
+
**Step 3 — Return to the running `sdlc-implementer` context.** The skill's job ends at the artefact + the operator sign-off. The orchestrator immediately continues with the rest of Stage 3 (security summary, evidence upload, release ticket) inline — no pause, no operator nudge needed. (Skills run in the same invocation context; control returns synchronously when this skill exits. See `sdlc-implementer/SKILL.md` § *Sub-skill return semantics*.)
|
|
136
136
|
|
|
137
137
|
### Phase 3 — Per-REQ ad-hoc audit
|
|
138
138
|
|
|
@@ -163,7 +163,7 @@ I have reviewed the risk register entries above and confirm:
|
|
|
163
163
|
|
|
164
164
|
**Step 2 — Tag for upload.** The CI's `compliance-evidence.yml` uploads this file as `evidence_type=risk_assessment` (added to META-COMPLY's `EVIDENCE_TYPE_REGISTRY` in the paired sub-PR). The framework-coverage matrix attribution depends on `framework-registry-auditor`'s review — see the META-COMPLY-side PR for final clause closures.
|
|
165
165
|
|
|
166
|
-
**Step 3 —
|
|
166
|
+
**Step 3 — Return to the running `sdlc-implementer` context.** The skill's job ends at the artefact + the operator sign-off. The orchestrator immediately continues with the rest of Stage 3 inline — no pause, no operator nudge needed. (Skills run in the same invocation context; control returns synchronously when this skill exits. See `sdlc-implementer/SKILL.md` § *Sub-skill return semantics*.)
|
|
167
167
|
|
|
168
168
|
### Phase 4 — `solo_with_gap` approval check
|
|
169
169
|
|
|
@@ -45,6 +45,20 @@ The orchestrator MUST invoke `e2e-test-engineer` for end-to-end and visual-regre
|
|
|
45
45
|
|
|
46
46
|
Unit-test and integration-test work stays with this skill until a counterpart unit-test skill ships. The full sub-skill call graph lives at [`references/call-graph.md`](./references/call-graph.md).
|
|
47
47
|
|
|
48
|
+
## Sub-skill return semantics (devaudit-installer#144)
|
|
49
|
+
|
|
50
|
+
**Sub-skills return findings synchronously; do not wait for operator confirmation between sub-skill returns.** Invoking a Skill loads its instructions into this same invocation context — there is no separate agent, no separate process. When a sub-skill emits its final summary and stops, this orchestrator's next step runs immediately, in the same turn if possible.
|
|
51
|
+
|
|
52
|
+
The literal phrasing _"Returns to the running `sdlc-implementer` context"_ at the tail of each sub-skill (`requirements-aligner`, `adr-author`, `risk-register-keeper`, and any future siblings) does **not** mean "pause and wait for the operator to nudge you" — it means "you have control again; keep going with the parent workflow." A chain of three sub-skill calls in Phase 1 (steps 6 → 7 → 8) or Phase 3 (steps 1 → 2 → 3) is a single flowing sequence; do not stop between them.
|
|
53
|
+
|
|
54
|
+
The only pauses in the whole workflow are the explicitly-named checkpoints:
|
|
55
|
+
|
|
56
|
+
- **Phase 1 step 11** — pause for human approval **iff** risk class is HIGH or CRITICAL (or `--require-plan-approval` is set).
|
|
57
|
+
- **Phase 4 step 5** — hard stop, release PR opened, awaiting UAT review on the portal.
|
|
58
|
+
- **Phase 5** — invoked separately by the user (`resume REQ-XXX`).
|
|
59
|
+
|
|
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
|
+
|
|
48
62
|
## SDLC navigability — LAST/NEXT status sticky (devaudit#131)
|
|
49
63
|
|
|
50
64
|
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:
|
|
@@ -204,13 +218,19 @@ _Workflow tweak (CI artifact upload, gate timeout bump, etc.)_
|
|
|
204
218
|
|
|
205
219
|
Reached from Phase 0 for non-tracked change-types. The skill drives this end-to-end; the only difference from the tracked cycle is the absence of _ceremony_, not the absence of _guidance_. It pauses only where a human is genuinely required (PR review, merge).
|
|
206
220
|
|
|
221
|
+
**CI trigger shape — read once before step 7.** The DevAudit-Installer-generated `ci.yml.template` defaults to **post-merge-only** triggers (`push: branches: [<integration>]`, no `pull_request:` trigger). On these projects there will be **no PR-time checks** to wait for — review + merge is the gate, and the post-merge CI run on the integration branch is the actual quality gate. A consumer who has explicitly added a `pull_request:` trigger has PR-time CI in addition. The skill must adapt step 7's wording to whichever shape the project uses; never poll a PR for checks that the template doesn't trigger.
|
|
222
|
+
|
|
207
223
|
1. **Branch off `$INTEGRATION_BRANCH`** with a housekeeping prefix — `chore/…`, `docs/…`, `ci/…`, `build/…`, `test/…`, or `compliance/…` for a doc-only change against an existing REQ.
|
|
208
224
|
2. **Make the change**, single-purpose. If it turns out to touch runtime behaviour in `app/` / `lib/`, stop and reclassify as tracked — the commit-type rule is the backstop.
|
|
209
225
|
3. **Run all gates locally** (`npm run lint`, `npx tsc --noEmit`, the test suite, `semgrep`, `npm audit` — or the stack-adapter equivalents). Trivial ≠ unverified; never `--no-verify`.
|
|
210
226
|
4. **Commit** with a housekeeping type and **no** `REQ-XXX` — `docs:` / `chore:` / `ci:` / `build:` / `test:` / `revert:` are exempt from the `[REQ-XXX]` rule; a `compliance:` doc-only change references the existing REQ. `Co-Authored-By: Claude` if AI-assisted.
|
|
211
227
|
5. **Push and open the PR** into `$INTEGRATION_BRANCH` (`gh pr create --base "$INTEGRATION_BRANCH" --head <branch>`). CI runs the same quality gates; `compliance-validation.yml` finds no `REQ-XXX` and skips artifact validation.
|
|
212
228
|
6. **For `ci:` changes, verify-via-dispatch before merging.** `gh workflow run <workflow.yml> --ref <branch>` fires the modified workflow against the PR branch. If the change broke a step, the dispatch run fails loudly and you fix-forward _before_ the merge ships the broken gate to `$INTEGRATION_BRANCH`. This is the cheapest insurance against silent CI regressions — a `ci:` change that breaks a gate is most damaging _after_ it lands.
|
|
213
|
-
7. **Report honest status
|
|
229
|
+
7. **Report honest status — adapt to the project's CI trigger shape (devaudit-installer#145).** Check whether `.github/workflows/ci.yml` has a `pull_request:` trigger.
|
|
230
|
+
- **PR-time CI present** — wait for CI to settle, name any failing check, fix and re-push. Never announce "ready" while a required check is red.
|
|
231
|
+
- **Post-merge-only CI (the DevAudit-Installer default — `push: branches: [<integration>]` with no `pull_request:` trigger)** — say so explicitly in the LAST/NEXT sticky: _"no PR-time checks will fire; review + merge is the gate; CI runs post-merge on `$INTEGRATION_BRANCH`."_ Don't poll the PR for checks that won't arrive. The post-merge run (CI Pipeline + Compliance Evidence Upload on the integration branch) is the actual gate; address it via fix-forward if it fails.
|
|
232
|
+
|
|
233
|
+
Either way, never bypass a gate (no `--no-verify`, no `--admin` merge of a red required check); the only difference is **where** you wait for the gate to fire — before merge vs. after merge.
|
|
214
234
|
8. **Guide review → merge.** A human still reviews the PR (separation of duties). There is **no** portal release approval, no UAT four-eyes, no Production gate, and no close-out. Merge once CI is green and the reviewer approves.
|
|
215
235
|
9. **Done.** A housekeeping push produces at most a bare-date release (`vYYYY.MM.DD`) with no approval gate; a doc-only push attaches its docs to the existing `REQ-XXX` release. No further action required — report completion and stop.
|
|
216
236
|
|
|
@@ -484,8 +484,21 @@ jobs:
|
|
|
484
484
|
# moment each acceptance criterion is demonstrated, NOT the Playwright
|
|
485
485
|
# report's trailing/failure capture. evidenceType `screenshot` →
|
|
486
486
|
# image/png renders inline. Only when a pending release ticket defines
|
|
487
|
-
# the in-scope REQ(s); skipped on ordinary dev pushes.
|
|
488
|
-
#
|
|
487
|
+
# the in-scope REQ(s); skipped on ordinary dev pushes.
|
|
488
|
+
#
|
|
489
|
+
# devaudit-installer#147 — per-REQ glob scoping + UPLOAD_FAILURES
|
|
490
|
+
# accounting. Previously the SHOTS glob was project-wide
|
|
491
|
+
# (`ci-evidence/compliance/evidence/*/screenshots/*.png`) and the
|
|
492
|
+
# for-REQ loop uploaded that cross-product against every in-scope
|
|
493
|
+
# REQ. Out-of-scope REQs' legacy PNGs whose filenames don't match
|
|
494
|
+
# `REQ-XXX-AC<n>-<slug>.png` then 400'd at the portal — and the
|
|
495
|
+
# `|| echo "::warning::..."` swallowed the failure so the step
|
|
496
|
+
# reported SUCCESS while screenshots were quietly missing. Now each
|
|
497
|
+
# in-scope REQ globs its OWN screenshots/ directory; out-of-scope
|
|
498
|
+
# legacy folders are intentionally ignored. To re-upload an older
|
|
499
|
+
# REQ's screenshots, add its release ticket back under
|
|
500
|
+
# compliance/pending-releases/. Failures now bump UPLOAD_FAILURES
|
|
501
|
+
# so the step exits non-zero and the release PR turns red.
|
|
489
502
|
SHOT_REQS=()
|
|
490
503
|
if [ -d compliance/pending-releases ]; then
|
|
491
504
|
for TICKET in compliance/pending-releases/RELEASE-TICKET-REQ-*.md; do
|
|
@@ -494,14 +507,8 @@ jobs:
|
|
|
494
507
|
done
|
|
495
508
|
fi
|
|
496
509
|
shopt -s nullglob
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
# copies under compliance/evidence/ are redundant here — globbing both
|
|
500
|
-
# uploaded every image twice (deduped on display, but wasteful + rate-limit
|
|
501
|
-
# pressure) and swept in legacy screenshots from unrelated past releases.
|
|
502
|
-
SHOTS=(ci-evidence/compliance/evidence/*/screenshots/*.png)
|
|
503
|
-
if [ "${#SHOT_REQS[@]}" -gt 0 ] && [ "${#SHOTS[@]}" -gt 0 ]; then
|
|
504
|
-
echo "Uploading ${#SHOTS[@]} evidence screenshot(s) for: ${SHOT_REQS[*]}"
|
|
510
|
+
if [ "${#SHOT_REQS[@]}" -gt 0 ]; then
|
|
511
|
+
echo "Uploading per-AC evidence screenshots for: ${SHOT_REQS[*]}"
|
|
505
512
|
SHOT_TMP="$(mktemp -d)"
|
|
506
513
|
for REQ in "${SHOT_REQS[@]}"; do
|
|
507
514
|
# Per-REQ release metadata for the portal (no-clobbered on existing rows):
|
|
@@ -518,7 +525,16 @@ jobs:
|
|
|
518
525
|
REQ_META=()
|
|
519
526
|
[ -n "$REQ_TITLE" ] && REQ_META+=(--release-title "$REQ_TITLE")
|
|
520
527
|
[ -n "$REQ_CT" ] && REQ_META+=(--change-type "$REQ_CT")
|
|
521
|
-
|
|
528
|
+
# devaudit-installer#147 — per-REQ scoped glob. ONLY this REQ's
|
|
529
|
+
# own screenshots/ directory; legacy folders for REQs not in
|
|
530
|
+
# SHOT_REQS are intentionally ignored.
|
|
531
|
+
REQ_SHOTS=(ci-evidence/compliance/evidence/"$REQ"/screenshots/*.png)
|
|
532
|
+
if [ "${#REQ_SHOTS[@]}" -eq 0 ]; then
|
|
533
|
+
echo "No per-AC screenshots for ${REQ} (none captured by evidenceShot this run)"
|
|
534
|
+
continue
|
|
535
|
+
fi
|
|
536
|
+
echo "Uploading ${#REQ_SHOTS[@]} screenshot(s) for ${REQ}"
|
|
537
|
+
for PNG in "${REQ_SHOTS[@]}"; do
|
|
522
538
|
# The basename is the canonical evidenceShot filename
|
|
523
539
|
# `REQ-XXX-AC<n>-<slug>.png`. The portal validates this
|
|
524
540
|
# shape on upload — anything else is rejected with 400.
|
|
@@ -540,12 +556,22 @@ jobs:
|
|
|
540
556
|
fi
|
|
541
557
|
fi
|
|
542
558
|
|
|
543
|
-
bash scripts/upload-evidence.sh \
|
|
559
|
+
if bash scripts/upload-evidence.sh \
|
|
544
560
|
{{PROJECT_SLUG}} "$REQ" screenshot "$NAMED" \
|
|
545
561
|
--category test_report ${FLAGS} --release "$REQ" \
|
|
546
562
|
"${REQ_META[@]}" \
|
|
547
|
-
"${ORIGIN_META[@]}"
|
|
548
|
-
|
|
563
|
+
"${ORIGIN_META[@]}"
|
|
564
|
+
then
|
|
565
|
+
:
|
|
566
|
+
else
|
|
567
|
+
# devaudit-installer#147 — feed the same UPLOAD_FAILURES
|
|
568
|
+
# counter that gate uploads use, so a screenshot
|
|
569
|
+
# rejected by the portal's filename validator (or any
|
|
570
|
+
# other failure mode) turns the step RED instead of
|
|
571
|
+
# silently warning.
|
|
572
|
+
echo "::warning::evidence screenshot upload failed: ${PNG} -> ${REQ}"
|
|
573
|
+
UPLOAD_FAILURES=$((UPLOAD_FAILURES + 1))
|
|
574
|
+
fi
|
|
549
575
|
done
|
|
550
576
|
done
|
|
551
577
|
fi
|
|
@@ -25,14 +25,29 @@ on:
|
|
|
25
25
|
branches: [develop]
|
|
26
26
|
paths:
|
|
27
27
|
- 'compliance/**'
|
|
28
|
+
# devaudit-installer#149 — listen for completion of the E2E Regression
|
|
29
|
+
# workflow so the critical-tier run on the release PR + the full
|
|
30
|
+
# regression on post-merge to main both upload their JSON results +
|
|
31
|
+
# HTML report to the portal under the right release. Without this hook
|
|
32
|
+
# the UAT four-eyes reviewer only sees smoke-tier evidence from the
|
|
33
|
+
# feature PR's develop merge — the broader sweep against the
|
|
34
|
+
# about-to-be-promoted integration code never reaches the portal.
|
|
35
|
+
workflow_run:
|
|
36
|
+
workflows: ['E2E Regression']
|
|
37
|
+
types: [completed]
|
|
28
38
|
|
|
29
39
|
concurrency:
|
|
30
|
-
group: ${{ github.workflow }}-${{ github.ref }}
|
|
40
|
+
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.workflow_run.id || '' }}
|
|
31
41
|
cancel-in-progress: true
|
|
32
42
|
|
|
33
43
|
jobs:
|
|
34
44
|
upload-compliance-evidence:
|
|
35
45
|
name: Upload Compliance Evidence
|
|
46
|
+
# devaudit-installer#149 — only the push/dispatch paths run the
|
|
47
|
+
# compliance-doc upload. The workflow_run path is handled by the
|
|
48
|
+
# sibling job below; running both on a workflow_run event would
|
|
49
|
+
# double-upload every compliance doc.
|
|
50
|
+
if: github.event_name != 'workflow_run'
|
|
36
51
|
runs-on: {{RUNNER}}
|
|
37
52
|
# Permissions are needed because the "Auto-generate housekeeping stubs"
|
|
38
53
|
# step pushes a new branch + opens a PR via `gh pr create` when the
|
|
@@ -412,22 +427,58 @@ jobs:
|
|
|
412
427
|
REQ_META_ARGS=$(req_meta_args "$REQ_ID")
|
|
413
428
|
for ARTIFACT in "$REQ_DIR"*.md; do
|
|
414
429
|
[ -f "$ARTIFACT" ] || continue
|
|
415
|
-
# Per-REQ
|
|
416
|
-
#
|
|
417
|
-
#
|
|
418
|
-
#
|
|
419
|
-
#
|
|
420
|
-
#
|
|
421
|
-
#
|
|
422
|
-
#
|
|
430
|
+
# Per-REQ basename → (evidence_type, evidence_category) routing.
|
|
431
|
+
# The bare default (compliance_document, planning) is the
|
|
432
|
+
# historical catch-all; the named cases route specific
|
|
433
|
+
# artefacts to their dedicated evidence types so the portal's
|
|
434
|
+
# framework-coverage matrix attributes them correctly:
|
|
435
|
+
#
|
|
436
|
+
# - test-execution-summary.md / test-summary-report.md
|
|
437
|
+
# → test_report : ISO 29119-3 §3.5.6 Test Completion
|
|
438
|
+
# Report per release cycle. Satisfies the portal's
|
|
439
|
+
# Test Reports gate with per-release evidence
|
|
440
|
+
# instead of the project-level evergreen TSR (which
|
|
441
|
+
# from v0.1.32 downgrades to compliance_document).
|
|
442
|
+
# DevAudit-Installer#101.
|
|
443
|
+
#
|
|
444
|
+
# - srs-alignment.md
|
|
445
|
+
# → srs_alignment : output of the requirements-aligner
|
|
446
|
+
# skill at Stage 3. Orphan-by-design at v1 per
|
|
447
|
+
# META-COMPLY framework-registry-auditor review;
|
|
448
|
+
# surfaces in Documents tab + audit-pack export.
|
|
449
|
+
# DevAudit-Installer#119.
|
|
450
|
+
#
|
|
451
|
+
# - architecture-decision.md
|
|
452
|
+
# → architecture_decision : output of the adr-author
|
|
453
|
+
# skill at Stage 3. Closes ISO 27001 A.8.25 (Secure
|
|
454
|
+
# development life cycle) via the dedicated type
|
|
455
|
+
# predicate. DevAudit-Installer#120.
|
|
456
|
+
#
|
|
457
|
+
# - risk-assessment.md
|
|
458
|
+
# → risk_assessment : output of the risk-register-keeper
|
|
459
|
+
# skill at Stage 3. Closes SOC 2 CC3.2 (Risk
|
|
460
|
+
# identification and assessment) via the dedicated
|
|
461
|
+
# type predicate. DevAudit-Installer#121.
|
|
462
|
+
#
|
|
463
|
+
# Until this routing existed the new artefacts uploaded as
|
|
464
|
+
# compliance_document, which matches the project-baseline
|
|
465
|
+
# docs predicate but NOT the per-REQ Tier 3 clause
|
|
466
|
+
# predicates that expect the dedicated types — so the matrix
|
|
467
|
+
# reported MISSING / PARTIAL for SOC2.CC3.2 + ISO27001.A.8.25
|
|
468
|
+
# despite the files being present. DevAudit-Installer#146.
|
|
423
469
|
BASENAME=$(basename "$ARTIFACT")
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
470
|
+
case "$BASENAME" in
|
|
471
|
+
test-execution-summary.md|test-summary-report.md)
|
|
472
|
+
EVTYPE=test_report; EVCAT=test_report ;;
|
|
473
|
+
srs-alignment.md)
|
|
474
|
+
EVTYPE=srs_alignment; EVCAT=planning ;;
|
|
475
|
+
architecture-decision.md)
|
|
476
|
+
EVTYPE=architecture_decision; EVCAT=planning ;;
|
|
477
|
+
risk-assessment.md)
|
|
478
|
+
EVTYPE=risk_assessment; EVCAT=planning ;;
|
|
479
|
+
*)
|
|
480
|
+
EVTYPE=compliance_document; EVCAT=planning ;;
|
|
481
|
+
esac
|
|
431
482
|
echo "Uploading: ${REQ_ID}/${BASENAME} (${EVTYPE})"
|
|
432
483
|
eval "bash scripts/upload-evidence.sh \
|
|
433
484
|
{{PROJECT_SLUG}} \"${REQ_ID}\" ${EVTYPE} \"$ARTIFACT\" \
|
|
@@ -440,3 +491,174 @@ jobs:
|
|
|
440
491
|
|
|
441
492
|
- name: Summary
|
|
442
493
|
run: echo "Compliance evidence uploaded for ${{ steps.version.outputs.version }}"
|
|
494
|
+
|
|
495
|
+
# devaudit-installer#149 — upload the E2E Regression artefacts to the
|
|
496
|
+
# portal so the critical-tier run on the release PR + the full
|
|
497
|
+
# regression on post-merge to main both land as portal evidence (not
|
|
498
|
+
# just GitHub Actions artifacts).
|
|
499
|
+
#
|
|
500
|
+
# Fires only on `workflow_run` events. The triggering workflow is the
|
|
501
|
+
# consumer's `E2E Regression` (project-owned per the v0.1.53 3-tier
|
|
502
|
+
# gating model) which writes:
|
|
503
|
+
# - e2e-regression-results.json (Playwright JSON reporter)
|
|
504
|
+
# - playwright-report/ (HTML report)
|
|
505
|
+
# - test-results/ (per-spec traces / videos / screenshots)
|
|
506
|
+
# to GitHub Actions artifact storage under the name `e2e-regression-report`.
|
|
507
|
+
# We download that artifact via the workflow_run.id, derive the release
|
|
508
|
+
# version against the triggering run's head_sha, then upload the
|
|
509
|
+
# canonical artefacts as `evidence_type=e2e_result` + `test_report`
|
|
510
|
+
# against each in-scope REQ.
|
|
511
|
+
upload-e2e-regression-evidence:
|
|
512
|
+
name: Upload E2E Regression Evidence
|
|
513
|
+
if: github.event_name == 'workflow_run'
|
|
514
|
+
runs-on: {{RUNNER}}
|
|
515
|
+
# actions: read is required so `actions/download-artifact@v4` with
|
|
516
|
+
# `run-id` can read another workflow's artifacts. Without it the
|
|
517
|
+
# download step fails with a 404 even when the artifact exists.
|
|
518
|
+
permissions:
|
|
519
|
+
contents: read
|
|
520
|
+
actions: read
|
|
521
|
+
env:
|
|
522
|
+
DEVAUDIT_BASE_URL_VAR: ${{ vars.DEVAUDIT_BASE_URL }}
|
|
523
|
+
DEVAUDIT_API_KEY: ${{ secrets.DEVAUDIT_API_KEY }}
|
|
524
|
+
steps:
|
|
525
|
+
- uses: actions/checkout@v4
|
|
526
|
+
with:
|
|
527
|
+
# Check out the SHA the E2E Regression ran against — that
|
|
528
|
+
# determines the release version + the in-scope REQs via the
|
|
529
|
+
# pending release tickets at that snapshot, not whatever
|
|
530
|
+
# default branch currently points to.
|
|
531
|
+
ref: ${{ github.event.workflow_run.head_sha }}
|
|
532
|
+
fetch-depth: 0
|
|
533
|
+
|
|
534
|
+
- name: Resolve DevAudit base URL
|
|
535
|
+
id: resolve
|
|
536
|
+
run: |
|
|
537
|
+
CONFIG_URL=""
|
|
538
|
+
if [ -f sdlc-config.json ]; then
|
|
539
|
+
CONFIG_URL=$(jq -r '.devaudit.base_url // empty' sdlc-config.json 2>/dev/null || true)
|
|
540
|
+
fi
|
|
541
|
+
if [ -n "$CONFIG_URL" ]; then
|
|
542
|
+
BASE="$CONFIG_URL"
|
|
543
|
+
elif [ -n "$DEVAUDIT_BASE_URL_VAR" ]; then
|
|
544
|
+
BASE="$DEVAUDIT_BASE_URL_VAR"
|
|
545
|
+
else
|
|
546
|
+
echo "::warning::No DevAudit base URL configured — skipping E2E evidence upload."
|
|
547
|
+
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
548
|
+
exit 0
|
|
549
|
+
fi
|
|
550
|
+
if [ -z "${DEVAUDIT_API_KEY}" ]; then
|
|
551
|
+
echo "::warning::DEVAUDIT_API_KEY not set — skipping E2E evidence upload."
|
|
552
|
+
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
553
|
+
exit 0
|
|
554
|
+
fi
|
|
555
|
+
echo "skip=false" >> "$GITHUB_OUTPUT"
|
|
556
|
+
echo "DEVAUDIT_BASE_URL=${BASE%/}" >> "$GITHUB_ENV"
|
|
557
|
+
|
|
558
|
+
- name: Download E2E Regression artifact
|
|
559
|
+
if: steps.resolve.outputs.skip != 'true'
|
|
560
|
+
uses: actions/download-artifact@v4
|
|
561
|
+
with:
|
|
562
|
+
name: e2e-regression-report
|
|
563
|
+
path: e2e-artifacts/
|
|
564
|
+
run-id: ${{ github.event.workflow_run.id }}
|
|
565
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
566
|
+
continue-on-error: true
|
|
567
|
+
|
|
568
|
+
- name: Derive release version
|
|
569
|
+
if: steps.resolve.outputs.skip != 'true'
|
|
570
|
+
id: version
|
|
571
|
+
run: |
|
|
572
|
+
chmod +x scripts/derive-release-version.sh 2>/dev/null || true
|
|
573
|
+
VERSION=$(./scripts/derive-release-version.sh)
|
|
574
|
+
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
575
|
+
echo "Resolved release version: ${VERSION}"
|
|
576
|
+
|
|
577
|
+
- name: Upload E2E Regression evidence to DevAudit
|
|
578
|
+
if: steps.resolve.outputs.skip != 'true'
|
|
579
|
+
run: |
|
|
580
|
+
set -euo pipefail
|
|
581
|
+
DERIVED_RELEASE="${{ steps.version.outputs.version }}"
|
|
582
|
+
|
|
583
|
+
# Tier metadata for the portal (the operator filters/groups by
|
|
584
|
+
# this on the release detail). pull_request → critical;
|
|
585
|
+
# push to main → regression; otherwise dispatch/schedule.
|
|
586
|
+
PRIOR_EVENT="${{ github.event.workflow_run.event }}"
|
|
587
|
+
PRIOR_BRANCH="${{ github.event.workflow_run.head_branch }}"
|
|
588
|
+
case "$PRIOR_EVENT" in
|
|
589
|
+
pull_request) TIER=critical ;;
|
|
590
|
+
push) TIER=regression ;;
|
|
591
|
+
*) TIER="${PRIOR_EVENT}" ;;
|
|
592
|
+
esac
|
|
593
|
+
|
|
594
|
+
# Common flags for upload-evidence.sh. Branch + SHA come from
|
|
595
|
+
# the triggering run, not from this workflow_run dispatch.
|
|
596
|
+
FLAGS="--create-release-if-missing --environment uat \
|
|
597
|
+
--git-sha ${{ github.event.workflow_run.head_sha }} \
|
|
598
|
+
--ci-run-id ${{ github.event.workflow_run.id }} \
|
|
599
|
+
--branch ${PRIOR_BRANCH}"
|
|
600
|
+
|
|
601
|
+
# In-scope REQs from pending release tickets at the triggering
|
|
602
|
+
# SHA. Fall back to `_compliance-docs` if no tickets present so
|
|
603
|
+
# the artefact at least lands somewhere visible.
|
|
604
|
+
REQS=()
|
|
605
|
+
if [ -d compliance/pending-releases ]; then
|
|
606
|
+
for TICKET in compliance/pending-releases/RELEASE-TICKET-REQ-*.md; do
|
|
607
|
+
[ -f "$TICKET" ] || continue
|
|
608
|
+
REQS+=("$(basename "$TICKET" .md | sed 's/^RELEASE-TICKET-//')")
|
|
609
|
+
done
|
|
610
|
+
fi
|
|
611
|
+
if [ "${#REQS[@]}" -eq 0 ]; then
|
|
612
|
+
REQS=(_compliance-docs)
|
|
613
|
+
fi
|
|
614
|
+
echo "Uploading E2E ${TIER}-tier evidence to: ${REQS[*]} (release: ${DERIVED_RELEASE})"
|
|
615
|
+
|
|
616
|
+
UPLOAD_FAILURES=0
|
|
617
|
+
for REQ in "${REQS[@]}"; do
|
|
618
|
+
# 1. JSON reporter output → e2e_result. The single canonical
|
|
619
|
+
# machine-readable artefact for the run.
|
|
620
|
+
if [ -f e2e-artifacts/e2e-regression-results.json ]; then
|
|
621
|
+
if bash scripts/upload-evidence.sh \
|
|
622
|
+
{{PROJECT_SLUG}} "$REQ" e2e_result \
|
|
623
|
+
e2e-artifacts/e2e-regression-results.json \
|
|
624
|
+
--category test_report ${FLAGS} --release "${DERIVED_RELEASE}" \
|
|
625
|
+
--meta-key "tier=${TIER}"
|
|
626
|
+
then
|
|
627
|
+
:
|
|
628
|
+
else
|
|
629
|
+
echo "::warning::e2e_result upload failed for ${REQ}"
|
|
630
|
+
UPLOAD_FAILURES=$((UPLOAD_FAILURES + 1))
|
|
631
|
+
fi
|
|
632
|
+
fi
|
|
633
|
+
# 2. Playwright HTML report index → test_report. Operator-
|
|
634
|
+
# facing artefact (the formatted run summary). Skipped if
|
|
635
|
+
# the report directory is missing (e.g. an upstream
|
|
636
|
+
# infrastructure failure aborted before reporting).
|
|
637
|
+
if [ -f e2e-artifacts/playwright-report/index.html ]; then
|
|
638
|
+
if bash scripts/upload-evidence.sh \
|
|
639
|
+
{{PROJECT_SLUG}} "$REQ" test_report \
|
|
640
|
+
e2e-artifacts/playwright-report/index.html \
|
|
641
|
+
--category test_report ${FLAGS} --release "${DERIVED_RELEASE}" \
|
|
642
|
+
--meta-key "tier=${TIER}"
|
|
643
|
+
then
|
|
644
|
+
:
|
|
645
|
+
else
|
|
646
|
+
echo "::warning::playwright HTML report upload failed for ${REQ}"
|
|
647
|
+
UPLOAD_FAILURES=$((UPLOAD_FAILURES + 1))
|
|
648
|
+
fi
|
|
649
|
+
fi
|
|
650
|
+
done
|
|
651
|
+
|
|
652
|
+
# Per-spec failure screenshots from test-results/ are NOT
|
|
653
|
+
# uploaded: Playwright names them `test-failed-1.png` /
|
|
654
|
+
# `test-finished-1.png`, which the portal's filename
|
|
655
|
+
# validator (REQ-XXX-AC<n>-<slug>.png) rejects. Use the
|
|
656
|
+
# evidenceShot(page, REQ, AC, slug) helper from the
|
|
657
|
+
# e2e-test-engineer skill for per-AC named captures; those
|
|
658
|
+
# upload via ci.yml's per-REQ screenshot loop.
|
|
659
|
+
|
|
660
|
+
if [ "$UPLOAD_FAILURES" -gt 0 ]; then
|
|
661
|
+
echo "::error::${UPLOAD_FAILURES} E2E evidence upload(s) failed — portal release matrix may be incomplete"
|
|
662
|
+
exit 1
|
|
663
|
+
fi
|
|
664
|
+
echo "E2E ${TIER}-tier evidence uploaded for ${#REQS[@]} requirement(s)"
|