@metasession.co/devaudit-cli 0.3.17 → 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 +36 -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-host-deployment.sh +58 -8
- package/sdlc/files/_common/scripts/check-host-deployment.test.sh +35 -3
- package/sdlc/files/_common/scripts/check-release-pr-scope.sh +8 -1
- package/sdlc/files/_common/scripts/check-release-pr-scope.test.sh +61 -12
- 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/derive-release-version.sh +7 -9
- package/sdlc/files/_common/scripts/derive-release-version.test.sh +18 -18
- package/sdlc/files/_common/scripts/generate-bundled-changes.sh +24 -3
- package/sdlc/files/_common/scripts/generate-bundled-changes.test.sh +9 -5
- package/sdlc/files/_common/scripts/reconcile-railway-deployment.sh +37 -0
- package/sdlc/files/_common/scripts/reconcile-railway-deployment.test.sh +30 -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 +62 -10
- package/sdlc/files/_common/skills/sdlc-implementer/SKILL.md +19 -16
- package/sdlc/files/ci/ci.yml.template +86 -46
- package/sdlc/files/ci/compliance-evidence.yml.template +173 -49
- 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 +92 -48
- package/sdlc/files/ci/python/ci.yml.template +14 -14
- package/sdlc/files/ci/quality-gates-provenance.yml.template +3 -0
- package/sdlc/files/ci/reconcile-deployment.yml.template +59 -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
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# render-test-executions.sh - Render release test executions as markdown tables.
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# bash scripts/render-test-executions.sh path/to/release-journey.json
|
|
6
|
+
#
|
|
7
|
+
# Supported input:
|
|
8
|
+
# First-class test execution payloads with `.testExecutions[]`.
|
|
9
|
+
#
|
|
10
|
+
# Output:
|
|
11
|
+
# Markdown suitable for inclusion under `## Test Executions` in
|
|
12
|
+
# `compliance/evidence/REQ-XXX/test-execution-summary.md`.
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
INPUT_JSON="${1:-}"
|
|
17
|
+
|
|
18
|
+
if [ -z "$INPUT_JSON" ] || [ ! -f "$INPUT_JSON" ]; then
|
|
19
|
+
echo "Usage: $0 <release-journey.json>" >&2
|
|
20
|
+
exit 2
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
24
|
+
echo "Error: jq is required." >&2
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
stage_label() {
|
|
29
|
+
case "$1" in
|
|
30
|
+
1) printf '1 plan' ;;
|
|
31
|
+
2) printf '2 implement_test' ;;
|
|
32
|
+
3) printf '3 compile_evidence' ;;
|
|
33
|
+
4) printf '4 uat_review' ;;
|
|
34
|
+
5) printf '5 production' ;;
|
|
35
|
+
*) printf '%s unknown' "${1:-?}" ;;
|
|
36
|
+
esac
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
format_date() {
|
|
40
|
+
local value="$1"
|
|
41
|
+
if [ -z "$value" ] || [ "$value" = "null" ]; then
|
|
42
|
+
printf 'Unknown'
|
|
43
|
+
else
|
|
44
|
+
printf '%s' "$value" | sed -E 's/T/ /; s/Z$//; s/[.][0-9]+$//'
|
|
45
|
+
fi
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
render_test_executions() {
|
|
49
|
+
echo "| Source Release | SDLC Stage | Execution | Suite | Outcome | Workflow / Run | Related Evidence | Incident / Remediation | Date |"
|
|
50
|
+
echo "| --- | --- | --- | --- | --- | --- | --- | --- | --- |"
|
|
51
|
+
|
|
52
|
+
declare -A COUNTS=()
|
|
53
|
+
local rows
|
|
54
|
+
rows="$(
|
|
55
|
+
jq -r '
|
|
56
|
+
(.testExecutions // [])
|
|
57
|
+
| sort_by(.sourceRelease // "", .sdlcStage // 0, .startedAt // "", .completedAt // "", .suiteKind // .executionKind // "")
|
|
58
|
+
| .[]
|
|
59
|
+
| {
|
|
60
|
+
sourceRelease: (.sourceRelease // "Unknown"),
|
|
61
|
+
stageCode: ((.sdlcStage // 0) | tostring),
|
|
62
|
+
kind: (.suiteKind // .executionKind // "unknown"),
|
|
63
|
+
outcome: (.outcome // "unknown"),
|
|
64
|
+
workflow: (
|
|
65
|
+
if (.workflowUrl // "") != "" then
|
|
66
|
+
"[" + ((.workflowName // "Workflow")) + "](" + .workflowUrl + ")"
|
|
67
|
+
else
|
|
68
|
+
(.workflowName // "Manual")
|
|
69
|
+
end
|
|
70
|
+
),
|
|
71
|
+
runMeta: (
|
|
72
|
+
[
|
|
73
|
+
(if (.externalRunId // "") != "" then "run " + .externalRunId else empty end),
|
|
74
|
+
(if (.externalRunAttempt // null) != null then "attempt " + (.externalRunAttempt | tostring) else empty end)
|
|
75
|
+
]
|
|
76
|
+
| map(select(length > 0))
|
|
77
|
+
| join(", ")
|
|
78
|
+
),
|
|
79
|
+
evidence: (
|
|
80
|
+
(.relatedEvidence // [])
|
|
81
|
+
| map(.displayName // .fileName // .name // .evidenceType // empty)
|
|
82
|
+
| map(select(length > 0))
|
|
83
|
+
| join(", ")
|
|
84
|
+
),
|
|
85
|
+
incident: (
|
|
86
|
+
[
|
|
87
|
+
(.incidentReference // empty),
|
|
88
|
+
(.remediationReference // empty),
|
|
89
|
+
(.outcomeReason // empty)
|
|
90
|
+
]
|
|
91
|
+
| map(select(length > 0))
|
|
92
|
+
| join("; ")
|
|
93
|
+
),
|
|
94
|
+
date: (.completedAt // .startedAt // "")
|
|
95
|
+
}
|
|
96
|
+
| @base64
|
|
97
|
+
' "$INPUT_JSON"
|
|
98
|
+
)"
|
|
99
|
+
|
|
100
|
+
if [ -z "$rows" ]; then
|
|
101
|
+
echo "| Unknown | Unknown | None | unknown | unknown | No test execution records returned | None | None | Unknown |"
|
|
102
|
+
echo
|
|
103
|
+
echo "**Final assessment:** No first-class test execution records were returned by the portal."
|
|
104
|
+
return 0
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
while IFS= read -r row_b64; do
|
|
108
|
+
[ -n "$row_b64" ] || continue
|
|
109
|
+
local source_release stage_code kind outcome workflow run_meta evidence incident date
|
|
110
|
+
source_release="$(printf '%s' "$row_b64" | base64 -d | jq -r '.sourceRelease')"
|
|
111
|
+
stage_code="$(printf '%s' "$row_b64" | base64 -d | jq -r '.stageCode')"
|
|
112
|
+
kind="$(printf '%s' "$row_b64" | base64 -d | jq -r '.kind')"
|
|
113
|
+
outcome="$(printf '%s' "$row_b64" | base64 -d | jq -r '.outcome')"
|
|
114
|
+
workflow="$(printf '%s' "$row_b64" | base64 -d | jq -r '.workflow')"
|
|
115
|
+
run_meta="$(printf '%s' "$row_b64" | base64 -d | jq -r '.runMeta')"
|
|
116
|
+
evidence="$(printf '%s' "$row_b64" | base64 -d | jq -r '.evidence')"
|
|
117
|
+
incident="$(printf '%s' "$row_b64" | base64 -d | jq -r '.incident')"
|
|
118
|
+
date="$(printf '%s' "$row_b64" | base64 -d | jq -r '.date')"
|
|
119
|
+
[ -n "$source_release" ] || continue
|
|
120
|
+
local group_key execution_ordinal workflow_cell evidence_cell incident_cell
|
|
121
|
+
group_key="${source_release}|${stage_code}"
|
|
122
|
+
COUNTS["$group_key"]=$(( ${COUNTS["$group_key"]:-0} + 1 ))
|
|
123
|
+
execution_ordinal="#${COUNTS[$group_key]}"
|
|
124
|
+
workflow_cell="$workflow"
|
|
125
|
+
if [ -n "$run_meta" ]; then
|
|
126
|
+
workflow_cell="${workflow_cell} (${run_meta})"
|
|
127
|
+
fi
|
|
128
|
+
evidence_cell="${evidence:-None}"
|
|
129
|
+
incident_cell="${incident:-None}"
|
|
130
|
+
printf '| %s | %s | %s | %s | %s | %s | %s | %s | %s |\n' \
|
|
131
|
+
"$source_release" \
|
|
132
|
+
"$(stage_label "$stage_code")" \
|
|
133
|
+
"$execution_ordinal" \
|
|
134
|
+
"$kind" \
|
|
135
|
+
"$outcome" \
|
|
136
|
+
"$workflow_cell" \
|
|
137
|
+
"$evidence_cell" \
|
|
138
|
+
"$incident_cell" \
|
|
139
|
+
"$(format_date "$date")"
|
|
140
|
+
done <<<"$rows"
|
|
141
|
+
|
|
142
|
+
echo
|
|
143
|
+
echo "**Final assessment:** Stage-scoped execution numbering is authoritative from first-class portal test execution records."
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
render_test_executions
|
package/sdlc/files/_common/scripts/{render-test-cycles.test.sh → render-test-executions.test.sh}
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# render-test-
|
|
2
|
+
# render-test-executions.test.sh - Tests for render-test-executions.sh.
|
|
3
3
|
|
|
4
4
|
set -euo pipefail
|
|
5
5
|
|
|
6
6
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
-
HELPER="$SCRIPT_DIR/render-test-
|
|
7
|
+
HELPER="$SCRIPT_DIR/render-test-executions.sh"
|
|
8
8
|
[ -x "$HELPER" ] || chmod +x "$HELPER"
|
|
9
9
|
|
|
10
10
|
PASS=0
|
|
@@ -27,15 +27,15 @@ assert_contains() {
|
|
|
27
27
|
fi
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
echo "=== render-test-
|
|
30
|
+
echo "=== render-test-executions.sh tests ==="
|
|
31
31
|
|
|
32
32
|
cat > "$WORK/first-class.json" <<'EOF'
|
|
33
33
|
{
|
|
34
|
-
"
|
|
34
|
+
"testExecutions": [
|
|
35
35
|
{
|
|
36
36
|
"sourceRelease": "REQ-091",
|
|
37
37
|
"sdlcStage": 2,
|
|
38
|
-
"
|
|
38
|
+
"suiteKind": "quality_gate",
|
|
39
39
|
"outcome": "failed",
|
|
40
40
|
"workflowName": "Quality Gates",
|
|
41
41
|
"workflowUrl": "https://github.com/example/repo/actions/runs/100",
|
|
@@ -49,7 +49,7 @@ cat > "$WORK/first-class.json" <<'EOF'
|
|
|
49
49
|
{
|
|
50
50
|
"sourceRelease": "REQ-091",
|
|
51
51
|
"sdlcStage": 2,
|
|
52
|
-
"
|
|
52
|
+
"suiteKind": "quality_gate",
|
|
53
53
|
"outcome": "passed",
|
|
54
54
|
"workflowName": "Quality Gates",
|
|
55
55
|
"workflowUrl": "https://github.com/example/repo/actions/runs/101",
|
|
@@ -63,7 +63,7 @@ cat > "$WORK/first-class.json" <<'EOF'
|
|
|
63
63
|
{
|
|
64
64
|
"sourceRelease": "REQ-090",
|
|
65
65
|
"sdlcStage": 4,
|
|
66
|
-
"
|
|
66
|
+
"suiteKind": "uat",
|
|
67
67
|
"outcome": "passed",
|
|
68
68
|
"workflowName": "UAT Review",
|
|
69
69
|
"workflowUrl": "https://portal.example/releases/req-090",
|
|
@@ -76,32 +76,26 @@ cat > "$WORK/first-class.json" <<'EOF'
|
|
|
76
76
|
EOF
|
|
77
77
|
|
|
78
78
|
OUTPUT="$(bash "$HELPER" "$WORK/first-class.json")"
|
|
79
|
-
assert_contains "first-class heading rendered" "| Source Release | SDLC Stage |
|
|
79
|
+
assert_contains "first-class heading rendered" "| Source Release | SDLC Stage | Execution | Suite | Outcome | Workflow / Run | Related Evidence | Incident / Remediation | Date |" "$OUTPUT"
|
|
80
80
|
assert_contains "stage label rendered" "| REQ-091 | 2 implement_test | #1 | quality_gate | failed | [Quality Gates](https://github.com/example/repo/actions/runs/100) (run 100, attempt 1) | quality-gates.json | #501 | 2026-07-15 08:05:00 |" "$OUTPUT"
|
|
81
81
|
assert_contains "ordinal increments within source release + stage" "| REQ-091 | 2 implement_test | #2 | quality_gate | passed | [Quality Gates](https://github.com/example/repo/actions/runs/101) (run 101, attempt 2) | quality-gates-rerun.json | PR #507 | 2026-07-15 09:07:00 |" "$OUTPUT"
|
|
82
82
|
assert_contains "different release/stage restarts numbering" "| REQ-090 | 4 uat_review | #1 | uat | passed | [UAT Review](https://portal.example/releases/req-090) | uat-checklist.md | None | 2026-07-15 10:12:00 |" "$OUTPUT"
|
|
83
|
-
assert_contains "first-class final assessment rendered" "Stage-scoped
|
|
83
|
+
assert_contains "first-class final assessment rendered" "Stage-scoped execution numbering is authoritative from first-class portal test execution records." "$OUTPUT"
|
|
84
84
|
|
|
85
85
|
cat > "$WORK/legacy.json" <<'EOF'
|
|
86
86
|
{
|
|
87
87
|
"legacyCycles": [
|
|
88
88
|
{
|
|
89
89
|
"testCycleId": "287654321",
|
|
90
|
-
"workflowName": "Legacy E2E"
|
|
91
|
-
"workflowUrl": "https://github.com/example/repo/actions/runs/287654321",
|
|
92
|
-
"relatedEvidence": [{ "fileName": "e2e-results.json" }, { "fileName": "screenshot.png" }],
|
|
93
|
-
"notes": "grouped from uploaded evidence only",
|
|
94
|
-
"date": "2026-07-14T18:30:00Z"
|
|
90
|
+
"workflowName": "Legacy E2E"
|
|
95
91
|
}
|
|
96
92
|
]
|
|
97
93
|
}
|
|
98
94
|
EOF
|
|
99
95
|
|
|
100
96
|
OUTPUT="$(bash "$HELPER" "$WORK/legacy.json")"
|
|
101
|
-
assert_contains "legacy
|
|
102
|
-
assert_contains "legacy row rendered" "| Legacy grouped evidence | legacy grouping | 287654321 | legacy | unknown | [Legacy E2E](https://github.com/example/repo/actions/runs/287654321) | e2e-results.json, screenshot.png | grouped from uploaded evidence only | 2026-07-14 18:30:00 |" "$OUTPUT"
|
|
103
|
-
assert_contains "legacy final assessment rendered" "Legacy grouping was used because the portal did not expose first-class cycle records." "$OUTPUT"
|
|
97
|
+
assert_contains "legacy grouping is ignored" "No first-class test execution records were returned by the portal." "$OUTPUT"
|
|
104
98
|
|
|
105
99
|
echo
|
|
106
|
-
echo "=== render-test-
|
|
100
|
+
echo "=== render-test-executions.test.sh: $PASS passed, $FAIL failed ==="
|
|
107
101
|
[ "$FAIL" -eq 0 ]
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# report-test-
|
|
2
|
+
# report-test-execution.sh - Producer helper for first-class test execution lifecycle events.
|
|
3
3
|
#
|
|
4
4
|
# Usage:
|
|
5
|
-
# ./scripts/report-test-
|
|
5
|
+
# ./scripts/report-test-execution.sh start|complete [flags...]
|
|
6
6
|
#
|
|
7
7
|
# Required flags:
|
|
8
8
|
# --project-slug <slug>
|
|
9
9
|
# --release <version>
|
|
10
10
|
# --sdlc-stage <1-5>
|
|
11
11
|
# --environment <ci|uat|production>
|
|
12
|
-
# --
|
|
12
|
+
# --suite-kind <kind>
|
|
13
13
|
# --idempotency-key <key>
|
|
14
14
|
#
|
|
15
15
|
# Optional flags:
|
|
16
|
+
# --iteration-key <key>
|
|
17
|
+
# --iteration-ordinal <n>
|
|
16
18
|
# --provider <name> Defaults to github_actions
|
|
17
19
|
# --external-run-id <id>
|
|
18
20
|
# --external-run-attempt <n>
|
|
@@ -26,17 +28,18 @@
|
|
|
26
28
|
# --outcome <value> Terminal outcome for `complete`
|
|
27
29
|
# --outcome-reason <text>
|
|
28
30
|
# --incident-reference <text>
|
|
31
|
+
# --remediation-reference <text>
|
|
29
32
|
# --output-file <path> Writes key=value outputs for callers
|
|
30
33
|
#
|
|
31
34
|
# Output keys:
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
35
|
+
# execution_supported=true
|
|
36
|
+
# execution_release_id=<uuid>
|
|
37
|
+
# execution_release_version=<exact-version>
|
|
38
|
+
# execution_record_id=<uuid-if-created-or-updated>
|
|
39
|
+
# execution_idempotency_key=<input-key>
|
|
40
|
+
# execution_started_at=<iso8601-if-known>
|
|
41
|
+
# execution_completed_at=<iso8601-if-known>
|
|
42
|
+
# execution_endpoint=<start|complete|reconcile-if-used>
|
|
40
43
|
|
|
41
44
|
set -euo pipefail
|
|
42
45
|
|
|
@@ -58,7 +61,9 @@ PROJECT_SLUG=""
|
|
|
58
61
|
RELEASE_VERSION=""
|
|
59
62
|
SDLC_STAGE=""
|
|
60
63
|
ENVIRONMENT=""
|
|
61
|
-
|
|
64
|
+
SUITE_KIND=""
|
|
65
|
+
ITERATION_KEY=""
|
|
66
|
+
ITERATION_ORDINAL=""
|
|
62
67
|
PROVIDER="github_actions"
|
|
63
68
|
EXTERNAL_RUN_ID=""
|
|
64
69
|
EXTERNAL_RUN_ATTEMPT=""
|
|
@@ -73,6 +78,7 @@ COMPLETED_AT=""
|
|
|
73
78
|
OUTCOME=""
|
|
74
79
|
OUTCOME_REASON=""
|
|
75
80
|
INCIDENT_REFERENCE=""
|
|
81
|
+
REMEDIATION_REFERENCE=""
|
|
76
82
|
OUTPUT_FILE=""
|
|
77
83
|
|
|
78
84
|
while [ "$#" -gt 0 ]; do
|
|
@@ -81,7 +87,9 @@ while [ "$#" -gt 0 ]; do
|
|
|
81
87
|
--release) RELEASE_VERSION="$2"; shift 2 ;;
|
|
82
88
|
--sdlc-stage) SDLC_STAGE="$2"; shift 2 ;;
|
|
83
89
|
--environment) ENVIRONMENT="$2"; shift 2 ;;
|
|
84
|
-
--
|
|
90
|
+
--suite-kind) SUITE_KIND="$2"; shift 2 ;;
|
|
91
|
+
--iteration-key) ITERATION_KEY="$2"; shift 2 ;;
|
|
92
|
+
--iteration-ordinal) ITERATION_ORDINAL="$2"; shift 2 ;;
|
|
85
93
|
--provider) PROVIDER="$2"; shift 2 ;;
|
|
86
94
|
--external-run-id) EXTERNAL_RUN_ID="$2"; shift 2 ;;
|
|
87
95
|
--external-run-attempt) EXTERNAL_RUN_ATTEMPT="$2"; shift 2 ;;
|
|
@@ -96,6 +104,7 @@ while [ "$#" -gt 0 ]; do
|
|
|
96
104
|
--outcome) OUTCOME="$2"; shift 2 ;;
|
|
97
105
|
--outcome-reason) OUTCOME_REASON="$2"; shift 2 ;;
|
|
98
106
|
--incident-reference) INCIDENT_REFERENCE="$2"; shift 2 ;;
|
|
107
|
+
--remediation-reference) REMEDIATION_REFERENCE="$2"; shift 2 ;;
|
|
99
108
|
--output-file) OUTPUT_FILE="$2"; shift 2 ;;
|
|
100
109
|
*) echo "Unknown option: $1"; exit 1 ;;
|
|
101
110
|
esac
|
|
@@ -113,7 +122,7 @@ require_arg "$PROJECT_SLUG" "--project-slug"
|
|
|
113
122
|
require_arg "$RELEASE_VERSION" "--release"
|
|
114
123
|
require_arg "$SDLC_STAGE" "--sdlc-stage"
|
|
115
124
|
require_arg "$ENVIRONMENT" "--environment"
|
|
116
|
-
require_arg "$
|
|
125
|
+
require_arg "$SUITE_KIND" "--suite-kind"
|
|
117
126
|
require_arg "$IDEMPOTENCY_KEY" "--idempotency-key"
|
|
118
127
|
|
|
119
128
|
if [ -z "${DEVAUDIT_BASE_URL:-}" ]; then
|
|
@@ -146,11 +155,16 @@ fi
|
|
|
146
155
|
case "$OUTCOME" in
|
|
147
156
|
""|passed|failed|cancelled|skipped|timed_out|action_required|unknown) ;;
|
|
148
157
|
*)
|
|
149
|
-
echo "Error: --outcome must be a terminal
|
|
158
|
+
echo "Error: --outcome must be a terminal test execution outcome" >&2
|
|
150
159
|
exit 1
|
|
151
160
|
;;
|
|
152
161
|
esac
|
|
153
162
|
|
|
163
|
+
if [ -n "$ITERATION_ORDINAL" ] && ! [[ "$ITERATION_ORDINAL" =~ ^[0-9]+$ ]]; then
|
|
164
|
+
echo "Error: --iteration-ordinal must be a positive integer" >&2
|
|
165
|
+
exit 1
|
|
166
|
+
fi
|
|
167
|
+
|
|
154
168
|
iso_now() {
|
|
155
169
|
date -u +"%Y-%m-%dT%H:%M:%SZ"
|
|
156
170
|
}
|
|
@@ -171,18 +185,6 @@ write_output() {
|
|
|
171
185
|
fi
|
|
172
186
|
}
|
|
173
187
|
|
|
174
|
-
emit_fallback() {
|
|
175
|
-
local release_id="${1:-}" resolved_version="${2:-}"
|
|
176
|
-
write_output cycle_supported false
|
|
177
|
-
write_output cycle_release_id "$release_id"
|
|
178
|
-
write_output cycle_release_version "$resolved_version"
|
|
179
|
-
write_output cycle_record_id ""
|
|
180
|
-
write_output cycle_idempotency_key "$IDEMPOTENCY_KEY"
|
|
181
|
-
write_output cycle_started_at "$STARTED_AT"
|
|
182
|
-
write_output cycle_completed_at "$COMPLETED_AT"
|
|
183
|
-
write_output cycle_endpoint "$MODE"
|
|
184
|
-
}
|
|
185
|
-
|
|
186
188
|
uri_escape() {
|
|
187
189
|
jq -rn --arg v "$1" '$v|@uri'
|
|
188
190
|
}
|
|
@@ -199,23 +201,21 @@ resolve_release_exact() {
|
|
|
199
201
|
--max-time "$MAX_TIME_SECONDS" \
|
|
200
202
|
"$url")
|
|
201
203
|
if [ "$code" -lt 200 ] || [ "$code" -ge 300 ]; then
|
|
202
|
-
echo "
|
|
204
|
+
echo "Error: could not resolve release ${RELEASE_VERSION} for test execution lifecycle (HTTP ${code})." >&2
|
|
203
205
|
rm -f "$body"
|
|
204
|
-
emit_fallback "" ""
|
|
205
206
|
return 1
|
|
206
207
|
fi
|
|
207
208
|
latest_id=$(jq -r '.latest.id // empty' "$body" 2>/dev/null || true)
|
|
208
209
|
latest_version=$(jq -r '.latest.version // empty' "$body" 2>/dev/null || true)
|
|
209
210
|
rm -f "$body"
|
|
210
211
|
if [ -z "$latest_id" ] || [ "$latest_version" != "$RELEASE_VERSION" ]; then
|
|
211
|
-
echo "
|
|
212
|
-
emit_fallback "$latest_id" "$latest_version"
|
|
212
|
+
echo "Error: release resolve for ${RELEASE_VERSION} was empty or ambiguous (latest=${latest_version:-none})." >&2
|
|
213
213
|
return 1
|
|
214
214
|
fi
|
|
215
215
|
RELEASE_ID="$latest_id"
|
|
216
216
|
RESOLVED_RELEASE_VERSION="$latest_version"
|
|
217
|
-
write_output
|
|
218
|
-
write_output
|
|
217
|
+
write_output execution_release_id "$RELEASE_ID"
|
|
218
|
+
write_output execution_release_version "$RESOLVED_RELEASE_VERSION"
|
|
219
219
|
return 0
|
|
220
220
|
}
|
|
221
221
|
|
|
@@ -231,7 +231,9 @@ build_payload() {
|
|
|
231
231
|
--arg mode "$mode" \
|
|
232
232
|
--arg sdlcStage "$SDLC_STAGE" \
|
|
233
233
|
--arg environment "$ENVIRONMENT" \
|
|
234
|
-
--arg
|
|
234
|
+
--arg suiteKind "$SUITE_KIND" \
|
|
235
|
+
--arg iterationKey "$ITERATION_KEY" \
|
|
236
|
+
--arg iterationOrdinal "$ITERATION_ORDINAL" \
|
|
235
237
|
--arg provider "$PROVIDER" \
|
|
236
238
|
--arg externalRunId "$EXTERNAL_RUN_ID" \
|
|
237
239
|
--argjson externalRunAttempt "$EXTERNAL_RUN_ATTEMPT_JSON" \
|
|
@@ -246,12 +248,16 @@ build_payload() {
|
|
|
246
248
|
--arg workflowName "$WORKFLOW_NAME" \
|
|
247
249
|
--arg workflowUrl "$WORKFLOW_URL" \
|
|
248
250
|
--arg incidentReference "$INCIDENT_REFERENCE" \
|
|
251
|
+
--arg remediationReference "$REMEDIATION_REFERENCE" \
|
|
249
252
|
'
|
|
250
253
|
{
|
|
251
254
|
schemaVersion: $schemaVersion,
|
|
252
255
|
sdlcStage: ($sdlcStage | tonumber),
|
|
253
256
|
environment: $environment,
|
|
254
|
-
cycleKind: $
|
|
257
|
+
cycleKind: $suiteKind,
|
|
258
|
+
suiteKind: $suiteKind,
|
|
259
|
+
iterationKey: ($iterationKey | if . == "" then null else . end),
|
|
260
|
+
iterationOrdinal: ($iterationOrdinal | if . == "" then null else tonumber end),
|
|
255
261
|
provider: $provider,
|
|
256
262
|
externalRunId: ($externalRunId | if . == "" then null else . end),
|
|
257
263
|
externalRunAttempt: $externalRunAttempt,
|
|
@@ -262,7 +268,8 @@ build_payload() {
|
|
|
262
268
|
branch: ($branch | if . == "" then null else . end),
|
|
263
269
|
workflowName: ($workflowName | if . == "" then null else . end),
|
|
264
270
|
workflowUrl: ($workflowUrl | if . == "" then null else . end),
|
|
265
|
-
incidentReference: ($incidentReference | if . == "" then null else . end)
|
|
271
|
+
incidentReference: ($incidentReference | if . == "" then null else . end),
|
|
272
|
+
remediationReference: ($remediationReference | if . == "" then null else . end)
|
|
266
273
|
}
|
|
267
274
|
+ (if $mode == "start"
|
|
268
275
|
then { outcome: "running" }
|
|
@@ -275,7 +282,7 @@ build_payload() {
|
|
|
275
282
|
'
|
|
276
283
|
}
|
|
277
284
|
|
|
278
|
-
|
|
285
|
+
post_execution_event() {
|
|
279
286
|
local endpoint="$1" payload="$2" body code
|
|
280
287
|
body=$(mktemp)
|
|
281
288
|
code=$(curl -sS -o "$body" -w "%{http_code}" \
|
|
@@ -286,59 +293,44 @@ post_cycle_event() {
|
|
|
286
293
|
--max-time "$MAX_TIME_SECONDS" \
|
|
287
294
|
"${DEVAUDIT_BASE_URL}/api/ci/releases/${RELEASE_ID}/cycles/${endpoint}" \
|
|
288
295
|
-d "$payload")
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
handle_unavailable_endpoint() {
|
|
294
|
-
case "$CYCLE_HTTP_CODE" in
|
|
295
|
-
404|405|501)
|
|
296
|
-
echo "::warning::Cycle lifecycle endpoint ${MODE} unavailable on this portal (HTTP ${CYCLE_HTTP_CODE}); continuing in legacy testCycleId mode." >&2
|
|
297
|
-
emit_fallback "$RELEASE_ID" "$RESOLVED_RELEASE_VERSION"
|
|
298
|
-
rm -f "$CYCLE_RESPONSE_BODY_FILE"
|
|
299
|
-
return 0
|
|
300
|
-
;;
|
|
301
|
-
esac
|
|
302
|
-
return 1
|
|
296
|
+
EXECUTION_HTTP_CODE="$code"
|
|
297
|
+
EXECUTION_RESPONSE_BODY_FILE="$body"
|
|
303
298
|
}
|
|
304
299
|
|
|
305
300
|
if ! resolve_release_exact; then
|
|
306
|
-
exit
|
|
301
|
+
exit 1
|
|
307
302
|
fi
|
|
308
303
|
|
|
309
304
|
PAYLOAD="$(build_payload "$MODE")"
|
|
310
305
|
ENDPOINT="$MODE"
|
|
311
|
-
|
|
306
|
+
post_execution_event "$ENDPOINT" "$PAYLOAD"
|
|
312
307
|
|
|
313
|
-
if [ "$MODE" = "complete" ] && [ "$
|
|
314
|
-
if grep -q 'different terminal outcome' "$
|
|
308
|
+
if [ "$MODE" = "complete" ] && [ "$EXECUTION_HTTP_CODE" -eq 400 ]; then
|
|
309
|
+
if grep -q 'different terminal outcome' "$EXECUTION_RESPONSE_BODY_FILE" 2>/dev/null; then
|
|
315
310
|
ENDPOINT="reconcile"
|
|
316
|
-
|
|
311
|
+
post_execution_event "$ENDPOINT" "$PAYLOAD"
|
|
317
312
|
fi
|
|
318
313
|
fi
|
|
319
314
|
|
|
320
|
-
if [ "$
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
BODY_EXCERPT="$(head -c 500 "$CYCLE_RESPONSE_BODY_FILE" 2>/dev/null || true)"
|
|
325
|
-
rm -f "$CYCLE_RESPONSE_BODY_FILE"
|
|
326
|
-
echo "Error: cycle ${ENDPOINT} failed for ${RELEASE_VERSION} (HTTP ${CYCLE_HTTP_CODE})${BODY_EXCERPT:+ — ${BODY_EXCERPT}}" >&2
|
|
315
|
+
if [ "$EXECUTION_HTTP_CODE" -lt 200 ] || [ "$EXECUTION_HTTP_CODE" -ge 300 ]; then
|
|
316
|
+
BODY_EXCERPT="$(head -c 500 "$EXECUTION_RESPONSE_BODY_FILE" 2>/dev/null || true)"
|
|
317
|
+
rm -f "$EXECUTION_RESPONSE_BODY_FILE"
|
|
318
|
+
echo "Error: test execution ${ENDPOINT} failed for ${RELEASE_VERSION} (HTTP ${EXECUTION_HTTP_CODE})${BODY_EXCERPT:+ - ${BODY_EXCERPT}}" >&2
|
|
327
319
|
exit 1
|
|
328
320
|
fi
|
|
329
321
|
|
|
330
|
-
|
|
331
|
-
rm -f "$
|
|
332
|
-
if [ -z "$
|
|
333
|
-
echo "Error:
|
|
322
|
+
EXECUTION_RECORD_ID=$(jq -r '.id // empty' "$EXECUTION_RESPONSE_BODY_FILE" 2>/dev/null || true)
|
|
323
|
+
rm -f "$EXECUTION_RESPONSE_BODY_FILE"
|
|
324
|
+
if [ -z "$EXECUTION_RECORD_ID" ]; then
|
|
325
|
+
echo "Error: test execution ${ENDPOINT} succeeded but response did not include an id" >&2
|
|
334
326
|
exit 1
|
|
335
327
|
fi
|
|
336
328
|
|
|
337
|
-
write_output
|
|
338
|
-
write_output
|
|
339
|
-
write_output
|
|
340
|
-
write_output
|
|
341
|
-
write_output
|
|
342
|
-
write_output
|
|
329
|
+
write_output execution_supported true
|
|
330
|
+
write_output execution_record_id "$EXECUTION_RECORD_ID"
|
|
331
|
+
write_output execution_idempotency_key "$IDEMPOTENCY_KEY"
|
|
332
|
+
write_output execution_started_at "$STARTED_AT"
|
|
333
|
+
write_output execution_completed_at "$COMPLETED_AT"
|
|
334
|
+
write_output execution_endpoint "$ENDPOINT"
|
|
343
335
|
|
|
344
|
-
echo "
|
|
336
|
+
echo "Test execution ${ENDPOINT} recorded for ${RELEASE_VERSION}: ${EXECUTION_RECORD_ID}"
|