@metasession.co/devaudit-cli 0.3.18 → 0.3.19
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 +35 -34
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/scripts/upload-evidence.sh +23 -19
- package/sdlc/files/_common/governance/incident-report.md.template +6 -1
- package/sdlc/files/_common/governance/nil-incident-report.md.template +5 -2
- package/sdlc/files/_common/scripts/check-release-pr-scope.sh +8 -1
- package/sdlc/files/_common/scripts/check-release-pr-scope.test.sh +58 -10
- package/sdlc/files/_common/scripts/check-self-hosted-runner.sh +103 -0
- package/sdlc/files/_common/scripts/check-self-hosted-runner.test.sh +74 -0
- package/sdlc/files/_common/scripts/record-uat-execution.sh +195 -0
- package/sdlc/files/_common/scripts/record-uat-execution.test.sh +144 -0
- package/sdlc/files/_common/scripts/render-test-executions.sh +146 -0
- package/sdlc/files/_common/scripts/{render-test-cycles.test.sh → render-test-executions.test.sh} +12 -18
- package/sdlc/files/_common/scripts/{report-test-cycle.sh → report-test-execution.sh} +66 -74
- package/sdlc/files/_common/scripts/report-test-execution.test.sh +177 -0
- package/sdlc/files/_common/scripts/submit-for-uat-review.sh +21 -0
- package/sdlc/files/_common/scripts/upload-evidence.sh +23 -19
- package/sdlc/files/_common/scripts/validate-commits.sh +6 -4
- package/sdlc/files/_common/scripts/validate-commits.test.sh +15 -0
- package/sdlc/files/_common/skills/e2e-test-engineer/SKILL.md +9 -7
- package/sdlc/files/_common/skills/e2e-test-engineer/references/e2e-regression-3-tier.yml +4 -1
- package/sdlc/files/_common/skills/sdlc-implementer/SKILL.md +16 -14
- package/sdlc/files/ci/ci.yml.template +54 -44
- package/sdlc/files/ci/compliance-evidence.yml.template +135 -46
- package/sdlc/files/ci/feature-e2e.yml.template +17 -17
- package/sdlc/files/ci/incident-export.yml.template +18 -4
- package/sdlc/files/ci/post-deploy-prod.yml.template +33 -30
- package/sdlc/files/ci/python/ci.yml.template +14 -14
- package/sdlc/files/ci/quality-gates-provenance.yml.template +3 -0
- package/sdlc/package.json +1 -1
- package/sdlc/src/blueprints/3-compile-evidence.raw.md +10 -10
- package/sdlc/src/blueprints/4-submit-for-review.raw.md +1 -1
- package/sdlc/src/blueprints/5-deploy-main.raw.md +1 -1
- package/sdlc/src/blueprints/implementing-an-sdlc-issue.raw.md +3 -3
- package/sdlc/files/_common/scripts/render-test-cycles.sh +0 -227
|
@@ -57,7 +57,18 @@ jobs:
|
|
|
57
57
|
# tag each captured screenshot as feature vs regression.
|
|
58
58
|
fetch-depth: 0
|
|
59
59
|
|
|
60
|
-
- name:
|
|
60
|
+
- name: Validate self-hosted runner prerequisites
|
|
61
|
+
env:
|
|
62
|
+
DEVAUDIT_RUNNER_ENVIRONMENT: ${{ runner.environment }}
|
|
63
|
+
run: |
|
|
64
|
+
if [ -f scripts/check-self-hosted-runner.sh ]; then
|
|
65
|
+
chmod +x scripts/check-self-hosted-runner.sh
|
|
66
|
+
bash scripts/check-self-hosted-runner.sh
|
|
67
|
+
else
|
|
68
|
+
echo "::warning::scripts/check-self-hosted-runner.sh missing; run devaudit update to sync runner preflight."
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
- name: Start authoritative quality-gate execution
|
|
61
72
|
if: github.event_name != 'pull_request' && github.ref_name == '{{INTEGRATION_BRANCH}}' && needs.register-release.outputs.version != 'skip'
|
|
62
73
|
run: |
|
|
63
74
|
BASE=""
|
|
@@ -65,13 +76,13 @@ jobs:
|
|
|
65
76
|
BASE=$(jq -r '.devaudit.base_url // empty' sdlc-config.json 2>/dev/null || true)
|
|
66
77
|
fi
|
|
67
78
|
[ -n "$BASE" ] || BASE="$DEVAUDIT_BASE_URL_VAR"
|
|
68
|
-
[ -n "$BASE" ] || { echo "::warning::No DevAudit URL;
|
|
79
|
+
[ -n "$BASE" ] || { echo "::warning::No DevAudit URL; test execution lifecycle disabled"; exit 0; }
|
|
69
80
|
echo "DEVAUDIT_BASE_URL=${BASE%/}" >> "$GITHUB_ENV"
|
|
70
|
-
chmod +x scripts/report-test-
|
|
71
|
-
bash scripts/report-test-
|
|
81
|
+
chmod +x scripts/report-test-execution.sh scripts/report-release-check.sh 2>/dev/null || true
|
|
82
|
+
bash scripts/report-test-execution.sh start \
|
|
72
83
|
--project-slug {{PROJECT_SLUG}} \
|
|
73
84
|
--release "${{ needs.register-release.outputs.version }}" \
|
|
74
|
-
--sdlc-stage 2 --environment ci --
|
|
85
|
+
--sdlc-stage 2 --environment ci --suite-kind quality_gate \
|
|
75
86
|
--provider github_actions --external-run-id "${{ github.run_id }}" \
|
|
76
87
|
--external-run-attempt "${{ github.run_attempt }}" \
|
|
77
88
|
--external-job-id "quality-gates" \
|
|
@@ -323,11 +334,11 @@ jobs:
|
|
|
323
334
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
324
335
|
echo "Release version: ${VERSION}"
|
|
325
336
|
|
|
326
|
-
- name: Ensure release exists for gate-
|
|
337
|
+
- name: Ensure release exists for gate-execution lineage
|
|
327
338
|
if: env.DEVAUDIT_BASE_URL != '' && steps.version.outputs.version != 'skip'
|
|
328
339
|
run: |
|
|
329
|
-
# First-class
|
|
330
|
-
# stage-2
|
|
340
|
+
# First-class test execution APIs need an exact release row before the
|
|
341
|
+
# stage-2 execution can start. This keeps failed gate runs visible even
|
|
331
342
|
# when later evidence generation fails.
|
|
332
343
|
chmod +x scripts/upload-evidence.sh 2>/dev/null || true
|
|
333
344
|
bash scripts/upload-evidence.sh \
|
|
@@ -501,17 +512,17 @@ jobs:
|
|
|
501
512
|
- name: Generate and upload gate evidence
|
|
502
513
|
if: env.DEVAUDIT_BASE_URL != ''
|
|
503
514
|
run: |
|
|
504
|
-
chmod +x scripts/upload-evidence.sh scripts/report-test-
|
|
515
|
+
chmod +x scripts/upload-evidence.sh scripts/report-test-execution.sh 2>/dev/null || true
|
|
505
516
|
FLAGS="--git-sha ${{ github.sha }} --ci-run-id ${{ github.run_id }} --branch ${{ github.ref_name }}"
|
|
506
517
|
FLAGS="${FLAGS} --release ${{ needs.register-release.outputs.version }} --create-release-if-missing"
|
|
507
518
|
FLAGS="${FLAGS} --environment uat"
|
|
508
519
|
FLAGS="${FLAGS} --sdlc-stage 2"
|
|
509
|
-
PRIMARY_LINEAGE_FLAGS=(--test-
|
|
510
|
-
|
|
511
|
-
bash scripts/report-test-
|
|
520
|
+
PRIMARY_LINEAGE_FLAGS=(--test-execution "${{ github.run_id }}")
|
|
521
|
+
EXECUTION_OUT="$(mktemp)"
|
|
522
|
+
bash scripts/report-test-execution.sh start \
|
|
512
523
|
--project-slug {{PROJECT_SLUG}} \
|
|
513
524
|
--release "${{ needs.register-release.outputs.version }}" \
|
|
514
|
-
--sdlc-stage 2 --environment ci --
|
|
525
|
+
--sdlc-stage 2 --environment ci --suite-kind quality_gate \
|
|
515
526
|
--provider github_actions --external-run-id "${{ github.run_id }}" \
|
|
516
527
|
--external-run-attempt "${{ github.run_attempt }}" \
|
|
517
528
|
--external-job-id "quality-gates" \
|
|
@@ -519,11 +530,11 @@ jobs:
|
|
|
519
530
|
--commit-sha "${{ github.sha }}" --branch "${{ github.ref_name }}" \
|
|
520
531
|
--workflow-name "Quality Gates" \
|
|
521
532
|
--workflow-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
|
|
522
|
-
--output-file "$
|
|
523
|
-
. "$
|
|
524
|
-
rm -f "$
|
|
525
|
-
if [ "${
|
|
526
|
-
PRIMARY_LINEAGE_FLAGS+=(--evidence-scope
|
|
533
|
+
--output-file "$EXECUTION_OUT"
|
|
534
|
+
. "$EXECUTION_OUT"
|
|
535
|
+
rm -f "$EXECUTION_OUT"
|
|
536
|
+
if [ "${execution_supported:-false}" = "true" ] && [ -n "${execution_record_id:-}" ]; then
|
|
537
|
+
PRIMARY_LINEAGE_FLAGS+=(--evidence-scope execution --test-execution-record-id "${execution_record_id}")
|
|
527
538
|
fi
|
|
528
539
|
|
|
529
540
|
# Track failures across all uploads so we can fail the job at the
|
|
@@ -646,17 +657,20 @@ jobs:
|
|
|
646
657
|
--environment uat --sdlc-stage 2
|
|
647
658
|
fi
|
|
648
659
|
|
|
649
|
-
# Per-REQ
|
|
650
|
-
#
|
|
651
|
-
# release
|
|
652
|
-
#
|
|
653
|
-
#
|
|
654
|
-
#
|
|
655
|
-
#
|
|
656
|
-
#
|
|
657
|
-
#
|
|
658
|
-
#
|
|
659
|
-
#
|
|
660
|
+
# Per-REQ E2E JSON attachment only.
|
|
661
|
+
# Gate outcomes above are intentionally uploaded only to the single
|
|
662
|
+
# commit-derived release in FLAGS. A generic integration Quality
|
|
663
|
+
# Gates run is run-level evidence; pending release tickets alone do
|
|
664
|
+
# not make that gate current approval evidence for every REQ. Bundle
|
|
665
|
+
# membership must be represented through an explicit bundle manifest
|
|
666
|
+
# and portal lineage, not by fanning out gate-outcomes.json rows
|
|
667
|
+
# (DevAudit-Installer#438).
|
|
668
|
+
#
|
|
669
|
+
# E2E JSON is different: it may be attached to a REQ only when the
|
|
670
|
+
# report itself contains an executed test tagged for that REQ. A smoke
|
|
671
|
+
# run must never overwrite or obscure a prior targeted feature
|
|
672
|
+
# execution merely because a pending ticket exists
|
|
673
|
+
# (DevAudit-Installer#435).
|
|
660
674
|
has_req_tagged_e2e_result() {
|
|
661
675
|
python3 - "$1" "$2" <<'PY'
|
|
662
676
|
import json, re, sys
|
|
@@ -687,7 +701,7 @@ jobs:
|
|
|
687
701
|
PY
|
|
688
702
|
}
|
|
689
703
|
COMMIT_REQ="${{ needs.register-release.outputs.version }}"
|
|
690
|
-
|
|
704
|
+
E2E_REQS=()
|
|
691
705
|
if [ -d compliance/pending-releases ]; then
|
|
692
706
|
for TICKET in compliance/pending-releases/RELEASE-TICKET-REQ-*.md; do
|
|
693
707
|
[ -f "$TICKET" ] || continue
|
|
@@ -701,21 +715,17 @@ jobs:
|
|
|
701
715
|
if [ "$REQ_ID" = "$COMMIT_REQ" ]; then
|
|
702
716
|
continue
|
|
703
717
|
fi
|
|
704
|
-
|
|
718
|
+
E2E_REQS+=("$REQ_ID")
|
|
705
719
|
done
|
|
706
720
|
fi
|
|
707
|
-
if [ "${#
|
|
708
|
-
echo "
|
|
709
|
-
|
|
721
|
+
if [ "${#E2E_REQS[@]}" -gt 0 ]; then
|
|
722
|
+
echo "Checking tagged E2E JSON attachment candidates: ${E2E_REQS[*]}"
|
|
723
|
+
echo "::notice::Not fanning out gate-outcomes.json to pending REQs; gate evidence remains on ${COMMIT_REQ} unless represented through explicit release lineage."
|
|
724
|
+
for REQ_ID in "${E2E_REQS[@]}"; do
|
|
710
725
|
FANOUT_FLAGS="--git-sha ${{ github.sha }} --ci-run-id ${{ github.run_id }} --branch ${{ github.ref_name }}"
|
|
711
726
|
FANOUT_FLAGS="${FANOUT_FLAGS} --release ${REQ_ID} --create-release-if-missing"
|
|
712
727
|
FANOUT_FLAGS="${FANOUT_FLAGS} --environment uat --sdlc-stage 2"
|
|
713
|
-
FANOUT_FLAGS="${FANOUT_FLAGS} --test-
|
|
714
|
-
if [ -f ci-evidence/gate-outcomes.json ]; then
|
|
715
|
-
upload "gate-outcomes.json -> ${REQ_ID}" \
|
|
716
|
-
{{PROJECT_SLUG}} "${REQ_ID}" gate_outcome ci-evidence/gate-outcomes.json \
|
|
717
|
-
--category ci_pipeline ${FANOUT_FLAGS}
|
|
718
|
-
fi
|
|
728
|
+
FANOUT_FLAGS="${FANOUT_FLAGS} --test-execution ${{ github.run_id }}"
|
|
719
729
|
if [ -f ci-evidence/e2e-results.json ] && has_req_tagged_e2e_result "$REQ_ID" ci-evidence/e2e-results.json; then
|
|
720
730
|
upload "e2e-results.json -> ${REQ_ID}" \
|
|
721
731
|
{{PROJECT_SLUG}} "${REQ_ID}" e2e_result ci-evidence/e2e-results.json \
|
|
@@ -975,7 +985,7 @@ jobs:
|
|
|
975
985
|
exit 1
|
|
976
986
|
fi
|
|
977
987
|
|
|
978
|
-
- name: Complete primary quality-gate
|
|
988
|
+
- name: Complete primary quality-gate execution
|
|
979
989
|
if: always() && env.DEVAUDIT_BASE_URL != ''
|
|
980
990
|
run: |
|
|
981
991
|
case "${{ needs.quality-gates.result }}" in
|
|
@@ -983,13 +993,13 @@ jobs:
|
|
|
983
993
|
cancelled) OUTCOME=cancelled ;;
|
|
984
994
|
*) OUTCOME=failed ;;
|
|
985
995
|
esac
|
|
986
|
-
chmod +x scripts/report-test-
|
|
987
|
-
bash scripts/report-test-
|
|
996
|
+
chmod +x scripts/report-test-execution.sh scripts/report-release-check.sh 2>/dev/null || true
|
|
997
|
+
bash scripts/report-test-execution.sh complete \
|
|
988
998
|
--project-slug {{PROJECT_SLUG}} \
|
|
989
999
|
--release "${{ needs.register-release.outputs.version }}" \
|
|
990
1000
|
--sdlc-stage 2 \
|
|
991
1001
|
--environment ci \
|
|
992
|
-
--
|
|
1002
|
+
--suite-kind quality_gate \
|
|
993
1003
|
--provider github_actions \
|
|
994
1004
|
--external-run-id "${{ github.run_id }}" \
|
|
995
1005
|
--external-run-attempt "${{ github.run_attempt }}" \
|
|
@@ -345,28 +345,99 @@ jobs:
|
|
|
345
345
|
# (operator's choice — both layouts are common).
|
|
346
346
|
upload_governance compliance/periodic-review.md periodic_review
|
|
347
347
|
upload_governance compliance/governance/periodic-review.md periodic_review
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
348
|
+
frontmatter_value() {
|
|
349
|
+
local FILE="$1" KEY="$2"
|
|
350
|
+
awk -v key="$KEY" '
|
|
351
|
+
NR == 1 && $0 == "---" { in_fm=1; next }
|
|
352
|
+
in_fm && $0 == "---" { exit }
|
|
353
|
+
in_fm && $0 ~ "^[[:space:]]*" key ":" {
|
|
354
|
+
sub("^[[:space:]]*" key ":[[:space:]]*", "")
|
|
355
|
+
gsub(/^["'\'' ]+|["'\'' ]+$/, "")
|
|
356
|
+
print
|
|
357
|
+
exit
|
|
358
|
+
}
|
|
359
|
+
' "$FILE" 2>/dev/null || true
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
bundle_manifest_allows_source_release() {
|
|
363
|
+
local SOURCE_RELEASE="$1" MANIFEST=""
|
|
364
|
+
[ -n "$SOURCE_RELEASE" ] || return 1
|
|
365
|
+
for MANIFEST in \
|
|
366
|
+
"compliance/pending-releases/BUNDLED-CHANGES-${DERIVED_RELEASE}.json" \
|
|
367
|
+
"compliance/approved-releases/BUNDLED-CHANGES-${DERIVED_RELEASE}.json" \
|
|
368
|
+
"compliance/superseded-releases/BUNDLED-CHANGES-${DERIVED_RELEASE}.json"; do
|
|
369
|
+
[ -f "$MANIFEST" ] || continue
|
|
370
|
+
if jq -e --arg source "$SOURCE_RELEASE" '
|
|
371
|
+
any(.members[]?;
|
|
372
|
+
.version == $source
|
|
373
|
+
and (
|
|
374
|
+
.evidenceInheritancePolicy.mode == "all_eligible"
|
|
375
|
+
or any(.evidenceInheritancePolicy.scopes[]?; . == "release" or . == "stage" or . == "approval")
|
|
376
|
+
)
|
|
377
|
+
)
|
|
378
|
+
' "$MANIFEST" >/dev/null 2>&1; then
|
|
379
|
+
return 0
|
|
380
|
+
fi
|
|
381
|
+
done
|
|
382
|
+
return 1
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
# Incident reports are selected by frontmatter ownership, not by
|
|
386
|
+
# repository-wide glob attribution. Historical files may stay on disk,
|
|
387
|
+
# but only the incident/no-incident artefact whose source release is
|
|
388
|
+
# the current derived release is uploaded to that release. If the
|
|
389
|
+
# current release has an explicit bundle manifest that grants
|
|
390
|
+
# inheritance for a predecessor, upload the file to the SOURCE release
|
|
391
|
+
# so the portal can inherit by lineage without relabelling ownership.
|
|
367
392
|
upload_incident_report() {
|
|
368
393
|
local FILE="$1"
|
|
369
394
|
if [ ! -f "$FILE" ]; then return 0; fi
|
|
395
|
+
local KIND SOURCE_RELEASE RELEASE_SCOPE SOURCE_ISSUE SEMANTIC_ID CONTENT_HASH TARGET_RELEASE
|
|
396
|
+
KIND="$(frontmatter_value "$FILE" incident_kind)"
|
|
397
|
+
SOURCE_RELEASE="$(frontmatter_value "$FILE" source_release)"
|
|
398
|
+
RELEASE_SCOPE="$(frontmatter_value "$FILE" release_scope)"
|
|
399
|
+
SOURCE_ISSUE="$(frontmatter_value "$FILE" source_issue)"
|
|
400
|
+
SEMANTIC_ID="$(frontmatter_value "$FILE" semantic_id)"
|
|
401
|
+
[ -n "$SEMANTIC_ID" ] || SEMANTIC_ID="$(frontmatter_value "$FILE" incident_id)"
|
|
402
|
+
|
|
403
|
+
case "$KIND" in
|
|
404
|
+
incident|no_incidents_attestation) ;;
|
|
405
|
+
"")
|
|
406
|
+
echo "::error::$(basename "$FILE"): missing required frontmatter incident_kind. Expected incident or no_incidents_attestation."
|
|
407
|
+
return 1 ;;
|
|
408
|
+
*)
|
|
409
|
+
echo "::error::$(basename "$FILE"): invalid incident_kind '${KIND}'. Expected incident or no_incidents_attestation."
|
|
410
|
+
return 1 ;;
|
|
411
|
+
esac
|
|
412
|
+
if [ "$KIND" = "no_incidents_attestation" ] && [ -z "$SOURCE_RELEASE" ]; then
|
|
413
|
+
echo "::error::$(basename "$FILE"): nil incident reports require source_release frontmatter."
|
|
414
|
+
return 1
|
|
415
|
+
fi
|
|
416
|
+
if [ "$KIND" = "incident" ]; then
|
|
417
|
+
if [ -z "$SOURCE_RELEASE" ] && [ -z "$RELEASE_SCOPE" ]; then
|
|
418
|
+
echo "::error::$(basename "$FILE"): incident reports require source_release or release_scope frontmatter."
|
|
419
|
+
return 1
|
|
420
|
+
fi
|
|
421
|
+
if [ -z "$SOURCE_ISSUE" ]; then
|
|
422
|
+
echo "::error::$(basename "$FILE"): incident reports require source_issue frontmatter."
|
|
423
|
+
return 1
|
|
424
|
+
fi
|
|
425
|
+
fi
|
|
426
|
+
if [ -z "$SEMANTIC_ID" ]; then
|
|
427
|
+
echo "::error::$(basename "$FILE"): incident reports require semantic_id or incident_id frontmatter."
|
|
428
|
+
return 1
|
|
429
|
+
fi
|
|
430
|
+
|
|
431
|
+
TARGET_RELEASE="${SOURCE_RELEASE:-$RELEASE_SCOPE}"
|
|
432
|
+
if [ "$TARGET_RELEASE" != "$DERIVED_RELEASE" ]; then
|
|
433
|
+
if bundle_manifest_allows_source_release "$TARGET_RELEASE"; then
|
|
434
|
+
echo "Incident $(basename "$FILE") is inherited by ${DERIVED_RELEASE}; preserving source release ${TARGET_RELEASE}."
|
|
435
|
+
else
|
|
436
|
+
echo "::notice::Skipping $(basename "$FILE"): source release ${TARGET_RELEASE} is not ${DERIVED_RELEASE} and no explicit bundle manifest grants inheritance."
|
|
437
|
+
return 0
|
|
438
|
+
fi
|
|
439
|
+
fi
|
|
440
|
+
|
|
370
441
|
# Completeness gate — check for REPLACE markers in structured sections.
|
|
371
442
|
# §4 Root cause, §5 Impact, §9 Sign-off (populated reports).
|
|
372
443
|
# For nil reports, only check the Sign-off section.
|
|
@@ -399,10 +470,17 @@ jobs:
|
|
|
399
470
|
return 0
|
|
400
471
|
fi
|
|
401
472
|
fi
|
|
402
|
-
|
|
473
|
+
CONTENT_HASH="sha256:$(sha256sum "$FILE" | awk '{print $1}')"
|
|
474
|
+
echo "Uploading governance: $(basename "$FILE") (type=incident_report, source_release=${TARGET_RELEASE})"
|
|
403
475
|
bash scripts/upload-evidence.sh \
|
|
404
476
|
{{PROJECT_SLUG}} _compliance-docs incident_report "$FILE" \
|
|
405
|
-
--category planning ${FLAGS} --release "${
|
|
477
|
+
--category planning ${FLAGS} --release "${TARGET_RELEASE}" \
|
|
478
|
+
--evidence-scope release \
|
|
479
|
+
--meta-key "incident_kind=${KIND}" \
|
|
480
|
+
--meta-key "source_release=${TARGET_RELEASE}" \
|
|
481
|
+
--meta-key "source_issue=${SOURCE_ISSUE:-none}" \
|
|
482
|
+
--meta-key "semantic_id=${SEMANTIC_ID}" \
|
|
483
|
+
--meta-key "content_hash=${CONTENT_HASH}" \
|
|
406
484
|
"${DERIVED_META[@]}" \
|
|
407
485
|
|| echo "Warning: Failed to upload $(basename "$FILE")"
|
|
408
486
|
}
|
|
@@ -738,7 +816,7 @@ jobs:
|
|
|
738
816
|
if: steps.resolve.outputs.skip != 'true' && steps.version.outputs.version != 'skip'
|
|
739
817
|
run: |
|
|
740
818
|
set -euo pipefail
|
|
741
|
-
chmod +x scripts/report-test-
|
|
819
|
+
chmod +x scripts/report-test-execution.sh scripts/report-release-check.sh 2>/dev/null || true
|
|
742
820
|
DERIVED_RELEASE="${{ steps.version.outputs.version }}"
|
|
743
821
|
|
|
744
822
|
# Tier metadata for the portal (the operator filters/groups by
|
|
@@ -746,18 +824,24 @@ jobs:
|
|
|
746
824
|
# push to main → regression; otherwise dispatch/schedule.
|
|
747
825
|
PRIOR_EVENT="${{ github.event.workflow_run.event }}"
|
|
748
826
|
PRIOR_BRANCH="${{ github.event.workflow_run.head_branch }}"
|
|
827
|
+
E2E_ENVIRONMENT=uat
|
|
749
828
|
case "$PRIOR_EVENT" in
|
|
750
|
-
pull_request)
|
|
751
|
-
push)
|
|
752
|
-
|
|
829
|
+
pull_request) TIER=critical; STAGE=2 ;;
|
|
830
|
+
push) TIER=regression; STAGE=5 ;;
|
|
831
|
+
deployment_status) TIER=regression; STAGE=5; E2E_ENVIRONMENT=production ;;
|
|
832
|
+
*) TIER="${PRIOR_EVENT}"; STAGE="" ;;
|
|
753
833
|
esac
|
|
754
834
|
|
|
755
835
|
# Common flags for upload-evidence.sh. Branch + SHA come from
|
|
756
836
|
# the triggering run, not from this workflow_run dispatch.
|
|
757
|
-
FLAGS="--create-release-if-missing --environment
|
|
837
|
+
FLAGS="--create-release-if-missing --environment ${E2E_ENVIRONMENT} \
|
|
758
838
|
--git-sha ${{ github.event.workflow_run.head_sha }} \
|
|
759
839
|
--ci-run-id ${{ github.event.workflow_run.id }} \
|
|
760
|
-
--branch ${PRIOR_BRANCH}
|
|
840
|
+
--branch ${PRIOR_BRANCH} \
|
|
841
|
+
--meta-key source_event=${PRIOR_EVENT} \
|
|
842
|
+
--meta-key source_workflow=E2E_Regression \
|
|
843
|
+
--meta-key source_run_id=${{ github.event.workflow_run.id }} \
|
|
844
|
+
--meta-key source_run_attempt=${{ github.event.workflow_run.run_attempt }}"
|
|
761
845
|
[ -n "$STAGE" ] && FLAGS="${FLAGS} --sdlc-stage ${STAGE}"
|
|
762
846
|
|
|
763
847
|
# In-scope REQs from pending release tickets at the triggering
|
|
@@ -820,6 +904,11 @@ jobs:
|
|
|
820
904
|
DERIVED_RELEASE="${REQS[0]}"
|
|
821
905
|
fi
|
|
822
906
|
|
|
907
|
+
if [ "$PRIOR_EVENT" = "deployment_status" ] && [ "${#REQS[@]}" -eq 0 ]; then
|
|
908
|
+
echo "::error::Deployment-origin E2E evidence requires tagged or in-scope REQ attribution; refusing _compliance-docs fallback."
|
|
909
|
+
exit 1
|
|
910
|
+
fi
|
|
911
|
+
|
|
823
912
|
if [ "${#REQS[@]}" -eq 0 ]; then
|
|
824
913
|
REQS=(_compliance-docs)
|
|
825
914
|
fi
|
|
@@ -839,28 +928,28 @@ jobs:
|
|
|
839
928
|
UPLOAD_FAILURES=0
|
|
840
929
|
for REQ in "${REQS[@]}"; do
|
|
841
930
|
REQ_FAILURES=0
|
|
842
|
-
LINEAGE_FLAGS=(--test-
|
|
843
|
-
|
|
844
|
-
bash scripts/report-test-
|
|
931
|
+
LINEAGE_FLAGS=(--test-execution "${{ github.event.workflow_run.id }}")
|
|
932
|
+
EXECUTION_OUT="$(mktemp)"
|
|
933
|
+
bash scripts/report-test-execution.sh start \
|
|
845
934
|
--project-slug {{PROJECT_SLUG}} \
|
|
846
935
|
--release "${DERIVED_RELEASE}" \
|
|
847
936
|
--sdlc-stage "${STAGE:-2}" \
|
|
848
|
-
--environment
|
|
849
|
-
--
|
|
937
|
+
--environment "${E2E_ENVIRONMENT}" \
|
|
938
|
+
--suite-kind e2e \
|
|
850
939
|
--provider github_actions \
|
|
851
940
|
--external-run-id "${{ github.event.workflow_run.id }}" \
|
|
852
941
|
--external-run-attempt "${{ github.event.workflow_run.run_attempt }}" \
|
|
853
|
-
--idempotency-key "github:${{ github.repository }}:e2e-regression.${REQ}.e2e:${{ github.event.workflow_run.id }}:${{ github.event.workflow_run.run_attempt }}:${STAGE:-2}:${DERIVED_RELEASE}" \
|
|
942
|
+
--idempotency-key "github:${{ github.repository }}:e2e-regression.${REQ}.e2e:${{ github.event.workflow_run.id }}:${{ github.event.workflow_run.run_attempt }}:${STAGE:-2}:${E2E_ENVIRONMENT}:${DERIVED_RELEASE}" \
|
|
854
943
|
--commit-sha "${{ github.event.workflow_run.head_sha }}" \
|
|
855
944
|
--branch "${PRIOR_BRANCH}" \
|
|
856
945
|
--workflow-name "E2E Regression" \
|
|
857
946
|
--workflow-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}" \
|
|
858
947
|
--started-at "${{ github.event.workflow_run.run_started_at }}" \
|
|
859
|
-
--output-file "$
|
|
860
|
-
. "$
|
|
861
|
-
rm -f "$
|
|
862
|
-
if [ "${
|
|
863
|
-
LINEAGE_FLAGS+=(--evidence-scope
|
|
948
|
+
--output-file "$EXECUTION_OUT"
|
|
949
|
+
. "$EXECUTION_OUT"
|
|
950
|
+
rm -f "$EXECUTION_OUT"
|
|
951
|
+
if [ "${execution_supported:-false}" = "true" ] && [ -n "${execution_record_id:-}" ]; then
|
|
952
|
+
LINEAGE_FLAGS+=(--evidence-scope execution --test-execution-record-id "${execution_record_id}")
|
|
864
953
|
fi
|
|
865
954
|
# 1. JSON reporter output → e2e_result. The single canonical
|
|
866
955
|
# machine-readable artefact for the run.
|
|
@@ -942,16 +1031,16 @@ jobs:
|
|
|
942
1031
|
else
|
|
943
1032
|
OUTCOME_REASON="authoritative workflow_run conclusion: ${{ github.event.workflow_run.conclusion }}"
|
|
944
1033
|
fi
|
|
945
|
-
bash scripts/report-test-
|
|
1034
|
+
bash scripts/report-test-execution.sh complete \
|
|
946
1035
|
--project-slug {{PROJECT_SLUG}} \
|
|
947
1036
|
--release "${DERIVED_RELEASE}" \
|
|
948
1037
|
--sdlc-stage "${STAGE:-2}" \
|
|
949
|
-
--environment
|
|
950
|
-
--
|
|
1038
|
+
--environment "${E2E_ENVIRONMENT}" \
|
|
1039
|
+
--suite-kind e2e \
|
|
951
1040
|
--provider github_actions \
|
|
952
1041
|
--external-run-id "${{ github.event.workflow_run.id }}" \
|
|
953
1042
|
--external-run-attempt "${{ github.event.workflow_run.run_attempt }}" \
|
|
954
|
-
--idempotency-key "github:${{ github.repository }}:e2e-regression.${REQ}.e2e:${{ github.event.workflow_run.id }}:${{ github.event.workflow_run.run_attempt }}:${STAGE:-2}:${DERIVED_RELEASE}" \
|
|
1043
|
+
--idempotency-key "github:${{ github.repository }}:e2e-regression.${REQ}.e2e:${{ github.event.workflow_run.id }}:${{ github.event.workflow_run.run_attempt }}:${STAGE:-2}:${E2E_ENVIRONMENT}:${DERIVED_RELEASE}" \
|
|
955
1044
|
--commit-sha "${{ github.event.workflow_run.head_sha }}" \
|
|
956
1045
|
--branch "${PRIOR_BRANCH}" \
|
|
957
1046
|
--workflow-name "E2E Regression" \
|
|
@@ -1012,8 +1101,8 @@ jobs:
|
|
|
1012
1101
|
# evidence). Classification rationale is included in every filed
|
|
1013
1102
|
# issue body so a human can verify and reclassify.
|
|
1014
1103
|
#
|
|
1015
|
-
#
|
|
1016
|
-
# report traces back to the specific test
|
|
1104
|
+
# testExecutionId (CI run ID) cross-references #209 so the incident
|
|
1105
|
+
# report traces back to the specific test execution that found the defect.
|
|
1017
1106
|
|
|
1018
1107
|
if [ ! -f e2e-artifacts/e2e-regression-results.json ]; then
|
|
1019
1108
|
echo "No e2e-regression-results.json found — cannot triage. Skipping incident filing."
|
|
@@ -1200,7 +1289,7 @@ jobs:
|
|
|
1200
1289
|
**Classification:** ${CLASSIFICATION}
|
|
1201
1290
|
**Classification rationale:** ${RATIONALE}
|
|
1202
1291
|
|
|
1203
|
-
**
|
|
1292
|
+
**testExecutionId:** ${CI_RUN_ID}
|
|
1204
1293
|
**Workflow run:** ${WORKFLOW_URL}
|
|
1205
1294
|
**Git SHA:** ${GIT_SHA}
|
|
1206
1295
|
|
|
@@ -1245,7 +1334,7 @@ jobs:
|
|
|
1245
1334
|
**Classification:** ${CLASSIFICATION}
|
|
1246
1335
|
**Classification rationale:** ${RATIONALE}
|
|
1247
1336
|
|
|
1248
|
-
**
|
|
1337
|
+
**testExecutionId:** ${CI_RUN_ID}
|
|
1249
1338
|
**Workflow run:** ${WORKFLOW_URL}
|
|
1250
1339
|
**Git SHA:** ${GIT_SHA}
|
|
1251
1340
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
#
|
|
3
3
|
# Generated by `devaudit install` / `devaudit update` from sdlc-config.json.
|
|
4
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
|
|
5
|
+
# branch, so regression-tier bugs surface during the feature execution instead of
|
|
6
6
|
# at the release gate. Uploads the report as stage-2, origin=feature evidence
|
|
7
7
|
# so it does NOT co-mingle with the stage-3/5 regression runs and is NOT
|
|
8
8
|
# counted as release/UAT evidence by the portal.
|
|
@@ -109,8 +109,8 @@ jobs:
|
|
|
109
109
|
echo "No new e2e specs detected — all captures will tag origin=regression."
|
|
110
110
|
fi
|
|
111
111
|
|
|
112
|
-
- name: Register feature release and start feature E2E
|
|
113
|
-
id:
|
|
112
|
+
- name: Register feature release and start feature E2E execution
|
|
113
|
+
id: exec
|
|
114
114
|
if: always()
|
|
115
115
|
run: |
|
|
116
116
|
REQ_ID="${{ needs.detect-req.outputs.req_id }}"
|
|
@@ -121,24 +121,24 @@ jobs:
|
|
|
121
121
|
fi
|
|
122
122
|
BASE="${CONFIG_URL:-$DEVAUDIT_BASE_URL_VAR}"
|
|
123
123
|
if [ -z "$BASE" ] || [ -z "$DEVAUDIT_API_KEY" ]; then
|
|
124
|
-
echo "::warning::DevAudit not configured — skipping feature E2E
|
|
125
|
-
echo "
|
|
124
|
+
echo "::warning::DevAudit not configured — skipping feature E2E test execution lifecycle."
|
|
125
|
+
echo "execution_supported=false" >> "$GITHUB_OUTPUT"
|
|
126
126
|
exit 0
|
|
127
127
|
fi
|
|
128
128
|
export DEVAUDIT_BASE_URL="${BASE%/}"
|
|
129
|
-
chmod +x scripts/upload-evidence.sh scripts/report-test-
|
|
129
|
+
chmod +x scripts/upload-evidence.sh scripts/report-test-execution.sh 2>/dev/null || true
|
|
130
130
|
bash scripts/upload-evidence.sh \
|
|
131
131
|
{{PROJECT_SLUG}} _compliance-docs release_registration README.md \
|
|
132
132
|
--release "$REQ_ID" --create-release-if-missing \
|
|
133
133
|
--environment uat --category planning \
|
|
134
134
|
--git-sha "${{ github.event.pull_request.head.sha }}" \
|
|
135
135
|
--branch "${{ github.head_ref }}" || true
|
|
136
|
-
bash scripts/report-test-
|
|
136
|
+
bash scripts/report-test-execution.sh start \
|
|
137
137
|
--project-slug {{PROJECT_SLUG}} \
|
|
138
138
|
--release "$REQ_ID" \
|
|
139
139
|
--sdlc-stage 2 \
|
|
140
140
|
--environment uat \
|
|
141
|
-
--
|
|
141
|
+
--suite-kind e2e \
|
|
142
142
|
--provider github_actions \
|
|
143
143
|
--external-run-id "${{ github.run_id }}" \
|
|
144
144
|
--external-run-attempt "${{ github.run_attempt }}" \
|
|
@@ -168,9 +168,9 @@ jobs:
|
|
|
168
168
|
zip -qr playwright-report.zip playwright-report/ 2>/dev/null || true
|
|
169
169
|
fi
|
|
170
170
|
if [ -f playwright-report.zip ]; then
|
|
171
|
-
LINEAGE_FLAGS=(--test-
|
|
172
|
-
if [ "${{ steps.
|
|
173
|
-
LINEAGE_FLAGS+=(--evidence-scope
|
|
171
|
+
LINEAGE_FLAGS=(--test-execution "${{ github.run_id }}")
|
|
172
|
+
if [ "${{ steps.exec.outputs.execution_supported }}" = "true" ] && [ -n "${{ steps.exec.outputs.execution_record_id }}" ]; then
|
|
173
|
+
LINEAGE_FLAGS+=(--evidence-scope execution --test-execution-record-id "${{ steps.exec.outputs.execution_record_id }}")
|
|
174
174
|
fi
|
|
175
175
|
bash scripts/upload-evidence.sh \
|
|
176
176
|
{{PROJECT_SLUG}} "$REQ_ID" feature_e2e_report playwright-report.zip \
|
|
@@ -183,10 +183,10 @@ jobs:
|
|
|
183
183
|
|| echo "::warning::feature E2E report upload failed"
|
|
184
184
|
fi
|
|
185
185
|
|
|
186
|
-
- name: Complete feature E2E
|
|
186
|
+
- name: Complete feature E2E execution
|
|
187
187
|
if: always()
|
|
188
188
|
run: |
|
|
189
|
-
if [ "${{ steps.
|
|
189
|
+
if [ "${{ steps.exec.outputs.execution_supported }}" != "true" ] || [ -z "${{ steps.exec.outputs.execution_record_id }}" ]; then
|
|
190
190
|
exit 0
|
|
191
191
|
fi
|
|
192
192
|
CONFIG_URL=""
|
|
@@ -203,13 +203,13 @@ jobs:
|
|
|
203
203
|
*) OUTCOME=failed ;;
|
|
204
204
|
esac
|
|
205
205
|
export DEVAUDIT_BASE_URL="${BASE%/}"
|
|
206
|
-
chmod +x scripts/report-test-
|
|
207
|
-
bash scripts/report-test-
|
|
206
|
+
chmod +x scripts/report-test-execution.sh 2>/dev/null || true
|
|
207
|
+
bash scripts/report-test-execution.sh complete \
|
|
208
208
|
--project-slug {{PROJECT_SLUG}} \
|
|
209
209
|
--release "${{ needs.detect-req.outputs.req_id }}" \
|
|
210
210
|
--sdlc-stage 2 \
|
|
211
211
|
--environment uat \
|
|
212
|
-
--
|
|
212
|
+
--suite-kind e2e \
|
|
213
213
|
--provider github_actions \
|
|
214
214
|
--external-run-id "${{ github.run_id }}" \
|
|
215
215
|
--external-run-attempt "${{ github.run_attempt }}" \
|
|
@@ -218,5 +218,5 @@ jobs:
|
|
|
218
218
|
--branch "${{ github.head_ref }}" \
|
|
219
219
|
--workflow-name "Feature In-Scope E2E" \
|
|
220
220
|
--workflow-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
|
|
221
|
-
--started-at "${{ steps.
|
|
221
|
+
--started-at "${{ steps.exec.outputs.execution_started_at }}" \
|
|
222
222
|
--outcome "$OUTCOME"
|
|
@@ -145,6 +145,11 @@ jobs:
|
|
|
145
145
|
ISSUE_LABELS: ${{ toJSON(github.event.issue.labels.*.name) }}
|
|
146
146
|
run: |
|
|
147
147
|
mkdir -p compliance/governance
|
|
148
|
+
SOURCE_RELEASE=$(printf '%s\n' "$ISSUE_BODY" | grep -oE 'REQ-[0-9]+|v[0-9]{4}\.[0-9]{2}\.[0-9]{2}([.][0-9]+)?' | head -1 || true)
|
|
149
|
+
if [ -z "$SOURCE_RELEASE" ]; then
|
|
150
|
+
echo "::error::Baseline-only incident export requires the issue body to reference its owning REQ-XXX or housekeeping release. Reopen or route through Path B with human frontmatter review."
|
|
151
|
+
exit 1
|
|
152
|
+
fi
|
|
148
153
|
ISSUE_JSON=$(gh issue view "$ISSUE_NUMBER" \
|
|
149
154
|
--json title,body,labels,author,createdAt,closedAt,comments,url,assignees,number,state)
|
|
150
155
|
|
|
@@ -235,6 +240,11 @@ jobs:
|
|
|
235
240
|
"---",
|
|
236
241
|
("title: " + (.title | @json)),
|
|
237
242
|
("incident_id: \"INC-" + (.createdAt[:10] | gsub("-";"")) + "-" + (.number|tostring) + "\""),
|
|
243
|
+
"incident_kind: \"incident\"",
|
|
244
|
+
("source_release: \"'"${SOURCE_RELEASE}"'\""),
|
|
245
|
+
("source_issue: \"" + (.number|tostring) + "\""),
|
|
246
|
+
("source_issue_url: " + (.url | @json)),
|
|
247
|
+
("semantic_id: \"INC-" + (.createdAt[:10] | gsub("-";"")) + "-" + (.number|tostring) + "\""),
|
|
238
248
|
("severity: \"'"${SEVERITY}"'\""),
|
|
239
249
|
("detected_at: " + (.createdAt | @json)),
|
|
240
250
|
("resolved_at: " + (.closedAt | @json)),
|
|
@@ -242,8 +252,6 @@ jobs:
|
|
|
242
252
|
("reported_to_supervisory_authority: \"n/a\""),
|
|
243
253
|
("notification_window_72h: \"n/a\""),
|
|
244
254
|
("last_reviewed_at: " + (.closedAt[:10] | @json)),
|
|
245
|
-
("source_issue: " + (.url | @json)),
|
|
246
|
-
("source_issue_number: " + (.number|tostring)),
|
|
247
255
|
"---",
|
|
248
256
|
"",
|
|
249
257
|
"> ℹ️ Auto-exported by Incident Export workflow on issue close (Path A — baseline-only).",
|
|
@@ -349,8 +357,11 @@ jobs:
|
|
|
349
357
|
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
|
350
358
|
OUT: ${{ steps.paths.outputs.out }}
|
|
351
359
|
SEVERITY: ${{ steps.route.outputs.severity }}
|
|
360
|
+
ISSUE_BODY: ${{ github.event.issue.body }}
|
|
352
361
|
run: |
|
|
353
362
|
mkdir -p compliance/governance
|
|
363
|
+
SOURCE_RELEASE=$(printf '%s\n' "$ISSUE_BODY" | grep -oE 'REQ-[0-9]+|v[0-9]{4}\.[0-9]{2}\.[0-9]{2}([.][0-9]+)?' | head -1 || true)
|
|
364
|
+
[ -n "$SOURCE_RELEASE" ] || SOURCE_RELEASE="REPLACE — owning REQ-XXX or vYYYY.MM.DD.N"
|
|
354
365
|
ISSUE_JSON=$(gh issue view "$ISSUE_NUMBER" \
|
|
355
366
|
--json title,body,labels,author,createdAt,closedAt,comments,url,assignees,number,state)
|
|
356
367
|
{
|
|
@@ -358,6 +369,11 @@ jobs:
|
|
|
358
369
|
"---",
|
|
359
370
|
("title: " + (.title | @json)),
|
|
360
371
|
("incident_id: \"INC-" + (.createdAt[:10] | gsub("-";"")) + "-" + (.number|tostring) + "\""),
|
|
372
|
+
"incident_kind: \"incident\"",
|
|
373
|
+
("source_release: \"'"${SOURCE_RELEASE}"'\""),
|
|
374
|
+
("source_issue: \"" + (.number|tostring) + "\""),
|
|
375
|
+
("source_issue_url: " + (.url | @json)),
|
|
376
|
+
("semantic_id: \"INC-" + (.createdAt[:10] | gsub("-";"")) + "-" + (.number|tostring) + "\""),
|
|
361
377
|
("severity: \"REPLACE — low | medium | high | critical\""),
|
|
362
378
|
("detected_at: " + (.createdAt | @json)),
|
|
363
379
|
("resolved_at: " + (.closedAt | @json)),
|
|
@@ -365,8 +381,6 @@ jobs:
|
|
|
365
381
|
("reported_to_supervisory_authority: \"REPLACE — true | false | n/a\""),
|
|
366
382
|
("notification_window_72h: \"REPLACE — within | outside | n/a\""),
|
|
367
383
|
("last_reviewed_at: " + (.closedAt[:10] | @json)),
|
|
368
|
-
("source_issue: " + (.url | @json)),
|
|
369
|
-
("source_issue_number: " + (.number|tostring)),
|
|
370
384
|
"---",
|
|
371
385
|
"",
|
|
372
386
|
"> ℹ️ Auto-exported by Incident Export workflow on issue close.",
|