@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
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
# Generated by `devaudit install` / `devaudit update` from sdlc-config.json.
|
|
4
4
|
# Do not edit manually — re-run the CLI (`devaudit update`) to regenerate.
|
|
5
5
|
#
|
|
6
|
-
# Production verification is READ-ONLY
|
|
6
|
+
# Production verification is READ-ONLY and starts only after a successful
|
|
7
|
+
# production deployment status. It must not join the main-push check suite a
|
|
8
|
+
# host evaluates before deciding whether to deploy.
|
|
7
9
|
# No E2E tests, no database operations, no API mutations.
|
|
8
10
|
#
|
|
9
11
|
# Promotes EVERY in-scope release (each requirement with a pending release
|
|
@@ -27,12 +29,19 @@ on:
|
|
|
27
29
|
release:
|
|
28
30
|
description: 'Optional REQ-XXX / version to promote (blank = all in-scope from pending release tickets).'
|
|
29
31
|
required: false
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
deployment_status:
|
|
33
|
+
types: [created]
|
|
32
34
|
|
|
33
35
|
jobs:
|
|
34
36
|
production-evidence:
|
|
35
37
|
name: Production Evidence
|
|
38
|
+
if: >-
|
|
39
|
+
github.event_name == 'workflow_dispatch' ||
|
|
40
|
+
(github.event.deployment_status.state == 'success' &&
|
|
41
|
+
(github.event.deployment.environment == 'production' ||
|
|
42
|
+
github.event.deployment.environment == 'prod' ||
|
|
43
|
+
endsWith(github.event.deployment.environment, '/ production') ||
|
|
44
|
+
endsWith(github.event.deployment.environment, '/production')))
|
|
36
45
|
runs-on: {{RUNNER}}
|
|
37
46
|
permissions:
|
|
38
47
|
contents: read
|
|
@@ -43,7 +52,7 @@ jobs:
|
|
|
43
52
|
DEVAUDIT_API_KEY: ${{ secrets.DEVAUDIT_API_KEY }}
|
|
44
53
|
PROD_URL: ${{ secrets.{{PRODUCTION_URL_SECRET}} }}
|
|
45
54
|
PROJECT_SLUG: {{PROJECT_SLUG}}
|
|
46
|
-
GIT_SHA: ${{ github.sha }}
|
|
55
|
+
GIT_SHA: ${{ github.event.deployment.sha || github.sha }}
|
|
47
56
|
CI_RUN: ${{ github.run_id }}
|
|
48
57
|
RELEASE_INPUT: ${{ github.event.inputs.release }}
|
|
49
58
|
|
|
@@ -138,17 +147,17 @@ jobs:
|
|
|
138
147
|
echo "In-scope releases to promote: ${REQS}"
|
|
139
148
|
echo "REQS=${REQS}" >> "$GITHUB_ENV"
|
|
140
149
|
|
|
141
|
-
- name: Start production deployment
|
|
150
|
+
- name: Start production deployment executions
|
|
142
151
|
run: |
|
|
143
|
-
chmod +x scripts/report-test-
|
|
152
|
+
chmod +x scripts/report-test-execution.sh 2>/dev/null || true
|
|
144
153
|
for PREFIX in ${REQS}; do
|
|
145
154
|
RESP=$(curl -fsS -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
|
|
146
155
|
"${BASE}/api/ci/releases/resolve?projectSlug=${PROJECT_SLUG}&versionPrefix=${PREFIX}")
|
|
147
156
|
VERSION=$(echo "$RESP" | jq -r '.latest.version // empty')
|
|
148
157
|
[ -n "$VERSION" ] || VERSION="$PREFIX"
|
|
149
|
-
bash scripts/report-test-
|
|
158
|
+
bash scripts/report-test-execution.sh start \
|
|
150
159
|
--project-slug "$PROJECT_SLUG" --release "$VERSION" \
|
|
151
|
-
--sdlc-stage 5 --environment production --
|
|
160
|
+
--sdlc-stage 5 --environment production --suite-kind deployment \
|
|
152
161
|
--provider github_actions --external-run-id "$CI_RUN" \
|
|
153
162
|
--external-run-attempt "${{ github.run_attempt }}" --external-job-id "host-deployment" \
|
|
154
163
|
--idempotency-key "github:${{ github.repository }}:post-deploy-prod.deployment:${CI_RUN}:${{ github.run_attempt }}:5:${VERSION}" \
|
|
@@ -157,67 +166,90 @@ jobs:
|
|
|
157
166
|
--workflow-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
|
158
167
|
done
|
|
159
168
|
|
|
160
|
-
- name:
|
|
161
|
-
id:
|
|
169
|
+
- name: Probe production health independently
|
|
170
|
+
id: production_probe
|
|
162
171
|
run: |
|
|
172
|
+
set -euo pipefail
|
|
163
173
|
DEPLOY_READY=false
|
|
174
|
+
LAST_HTTP_CODE=000
|
|
164
175
|
for i in $(seq 1 30); do
|
|
165
176
|
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "${PROD_URL}/" || echo "000")
|
|
177
|
+
LAST_HTTP_CODE="$HTTP_CODE"
|
|
166
178
|
if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 400 ]; then
|
|
167
|
-
echo "
|
|
179
|
+
echo "::notice::production health probe attempt=${i}/30 url=${PROD_URL}/ http_status=${HTTP_CODE}"
|
|
168
180
|
DEPLOY_READY=true
|
|
169
181
|
break
|
|
170
182
|
fi
|
|
171
|
-
echo "
|
|
183
|
+
echo "::notice::production health probe attempt=${i}/30 url=${PROD_URL}/ http_status=${HTTP_CODE}"
|
|
172
184
|
sleep 10
|
|
173
185
|
done
|
|
174
|
-
if [ "$DEPLOY_READY"
|
|
175
|
-
echo "
|
|
176
|
-
|
|
186
|
+
if [ "$DEPLOY_READY" = "true" ]; then
|
|
187
|
+
echo "outcome=success" >> "$GITHUB_OUTPUT"
|
|
188
|
+
echo "verification=production_health_success" >> "$GITHUB_OUTPUT"
|
|
189
|
+
else
|
|
190
|
+
echo "outcome=failure" >> "$GITHUB_OUTPUT"
|
|
191
|
+
echo "verification=production_health_timeout" >> "$GITHUB_OUTPUT"
|
|
192
|
+
echo "::error::production_health_timeout: ${PROD_URL}/ did not return a successful response after 30 attempts (last HTTP ${LAST_HTTP_CODE})."
|
|
177
193
|
fi
|
|
194
|
+
echo "http_code=${LAST_HTTP_CODE}" >> "$GITHUB_OUTPUT"
|
|
178
195
|
|
|
179
196
|
- name: Confirm terminal host deployment success
|
|
180
197
|
id: host_deployment
|
|
181
|
-
if: steps.wait_deployment.outcome == 'success'
|
|
182
198
|
env:
|
|
183
199
|
GH_TOKEN: ${{ github.token }}
|
|
184
200
|
run: |
|
|
201
|
+
set +e
|
|
185
202
|
chmod +x scripts/check-host-deployment.sh 2>/dev/null || true
|
|
186
203
|
bash scripts/check-host-deployment.sh \
|
|
187
204
|
--repo="${{ github.repository }}" \
|
|
188
205
|
--sha="${GIT_SHA}" \
|
|
189
206
|
--max-attempts=30 \
|
|
190
|
-
--poll-seconds=10
|
|
207
|
+
--poll-seconds=10 \
|
|
208
|
+
--output-file=host-deployment-result.env
|
|
209
|
+
SCRIPT_EXIT=$?
|
|
210
|
+
set -e
|
|
211
|
+
if [ -f host-deployment-result.env ]; then
|
|
212
|
+
for KEY in verification deployment_id deployment_state target_url environment elapsed_seconds; do
|
|
213
|
+
VALUE=$(sed -n "s/^${KEY}=//p" host-deployment-result.env | head -n 1)
|
|
214
|
+
echo "${KEY}=${VALUE}" >> "$GITHUB_OUTPUT"
|
|
215
|
+
done
|
|
216
|
+
fi
|
|
217
|
+
exit "$SCRIPT_EXIT"
|
|
191
218
|
|
|
192
|
-
- name: Complete production deployment
|
|
219
|
+
- name: Complete production deployment executions
|
|
193
220
|
if: always() && env.BASE != ''
|
|
194
221
|
run: |
|
|
195
|
-
|
|
222
|
+
HOST_VERIFICATION="${{ steps.host_deployment.outputs.verification || 'not_run' }}"
|
|
223
|
+
PROBE_VERIFICATION="${{ steps.production_probe.outputs.verification || 'not_run' }}"
|
|
224
|
+
if [ "$HOST_VERIFICATION" = "success" ] && [ "$PROBE_VERIFICATION" = "production_health_success" ]; then
|
|
196
225
|
OUTCOME=passed
|
|
197
226
|
CHECK_STATUS=successful
|
|
198
|
-
elif [ "$
|
|
227
|
+
elif [ "$HOST_VERIFICATION" = "deployment_status_timeout" ]; then
|
|
228
|
+
OUTCOME=timed_out
|
|
229
|
+
CHECK_STATUS=failed
|
|
230
|
+
elif [ "${{ steps.host_deployment.outcome }}" = "cancelled" ]; then
|
|
199
231
|
OUTCOME=cancelled
|
|
200
232
|
CHECK_STATUS=cancelled
|
|
201
233
|
else
|
|
202
234
|
OUTCOME=failed
|
|
203
235
|
CHECK_STATUS=failed
|
|
204
236
|
fi
|
|
205
|
-
chmod +x scripts/report-test-
|
|
237
|
+
chmod +x scripts/report-test-execution.sh scripts/report-release-check.sh 2>/dev/null || true
|
|
206
238
|
for PREFIX in ${REQS}; do
|
|
207
239
|
RESP=$(curl -fsS -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
|
|
208
240
|
"${BASE}/api/ci/releases/resolve?projectSlug=${PROJECT_SLUG}&versionPrefix=${PREFIX}")
|
|
209
241
|
VERSION=$(echo "$RESP" | jq -r '.latest.version // empty')
|
|
210
242
|
[ -n "$VERSION" ] || VERSION="$PREFIX"
|
|
211
|
-
bash scripts/report-test-
|
|
243
|
+
bash scripts/report-test-execution.sh complete \
|
|
212
244
|
--project-slug "$PROJECT_SLUG" --release "$VERSION" \
|
|
213
|
-
--sdlc-stage 5 --environment production --
|
|
245
|
+
--sdlc-stage 5 --environment production --suite-kind deployment \
|
|
214
246
|
--provider github_actions --external-run-id "$CI_RUN" \
|
|
215
247
|
--external-run-attempt "${{ github.run_attempt }}" --external-job-id "host-deployment" \
|
|
216
248
|
--idempotency-key "github:${{ github.repository }}:post-deploy-prod.deployment:${CI_RUN}:${{ github.run_attempt }}:5:${VERSION}" \
|
|
217
249
|
--commit-sha "$GIT_SHA" --branch main \
|
|
218
250
|
--workflow-name "Post-Deploy Production" \
|
|
219
251
|
--workflow-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
|
|
220
|
-
--outcome "$OUTCOME" --outcome-reason "host
|
|
252
|
+
--outcome "$OUTCOME" --outcome-reason "host verification=${HOST_VERIFICATION}; production probe=${PROBE_VERIFICATION}; last HTTP=${{ steps.production_probe.outputs.http_code || 'unknown' }}"
|
|
221
253
|
bash scripts/report-release-check.sh \
|
|
222
254
|
--project-slug "$PROJECT_SLUG" --release "$VERSION" \
|
|
223
255
|
--check-key "production-deployment:${CI_RUN}:${{ github.run_attempt }}" \
|
|
@@ -225,21 +257,33 @@ jobs:
|
|
|
225
257
|
--external-run-id "$CI_RUN" \
|
|
226
258
|
--external-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
|
|
227
259
|
--commit-sha "$GIT_SHA" --branch main \
|
|
228
|
-
--details-json
|
|
260
|
+
--details-json "$(jq -cn \
|
|
261
|
+
--arg hostVerification "$HOST_VERIFICATION" \
|
|
262
|
+
--arg hostOutcome "${{ steps.host_deployment.outcome }}" \
|
|
263
|
+
--arg deploymentId "${{ steps.host_deployment.outputs.deployment_id || '' }}" \
|
|
264
|
+
--arg deploymentState "${{ steps.host_deployment.outputs.deployment_state || '' }}" \
|
|
265
|
+
--arg targetUrl "${{ steps.host_deployment.outputs.target_url || '' }}" \
|
|
266
|
+
--arg probeVerification "$PROBE_VERIFICATION" \
|
|
267
|
+
--arg probeHttpCode "${{ steps.production_probe.outputs.http_code || '' }}" \
|
|
268
|
+
'{hostVerification:$hostVerification,hostOutcome:$hostOutcome,deploymentId:$deploymentId,deploymentState:$deploymentState,targetUrl:$targetUrl,probeVerification:$probeVerification,probeHttpCode:$probeHttpCode}')"
|
|
229
269
|
done
|
|
270
|
+
if [ "$CHECK_STATUS" != "successful" ]; then
|
|
271
|
+
echo "::error::Production deployment verification is not successful: host=${HOST_VERIFICATION}, probe=${PROBE_VERIFICATION}. Investigate the hosting provider before retrying."
|
|
272
|
+
exit 1
|
|
273
|
+
fi
|
|
230
274
|
|
|
231
|
-
- name: Start production smoke
|
|
232
|
-
if: steps.
|
|
275
|
+
- name: Start production smoke executions
|
|
276
|
+
if: steps.production_probe.outputs.verification == 'production_health_success' && steps.host_deployment.outputs.verification == 'success'
|
|
233
277
|
run: |
|
|
234
|
-
chmod +x scripts/report-test-
|
|
278
|
+
chmod +x scripts/report-test-execution.sh 2>/dev/null || true
|
|
235
279
|
for PREFIX in ${REQS}; do
|
|
236
280
|
RESP=$(curl -fsS -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
|
|
237
281
|
"${BASE}/api/ci/releases/resolve?projectSlug=${PROJECT_SLUG}&versionPrefix=${PREFIX}")
|
|
238
282
|
VERSION=$(echo "$RESP" | jq -r '.latest.version // empty')
|
|
239
283
|
[ -n "$VERSION" ] || VERSION="$PREFIX"
|
|
240
|
-
bash scripts/report-test-
|
|
284
|
+
bash scripts/report-test-execution.sh start \
|
|
241
285
|
--project-slug "$PROJECT_SLUG" --release "$VERSION" \
|
|
242
|
-
--sdlc-stage 5 --environment production --
|
|
286
|
+
--sdlc-stage 5 --environment production --suite-kind smoke \
|
|
243
287
|
--provider github_actions --external-run-id "$CI_RUN" \
|
|
244
288
|
--external-run-attempt "${{ github.run_attempt }}" --external-job-id "production-smoke" \
|
|
245
289
|
--idempotency-key "github:${{ github.repository }}:post-deploy-prod.smoke:${CI_RUN}:${{ github.run_attempt }}:5:${VERSION}" \
|
|
@@ -250,7 +294,7 @@ jobs:
|
|
|
250
294
|
|
|
251
295
|
- name: Production smoke tests (read-only)
|
|
252
296
|
id: production_smoke
|
|
253
|
-
if: steps.
|
|
297
|
+
if: steps.production_probe.outputs.verification == 'production_health_success' && steps.host_deployment.outputs.verification == 'success'
|
|
254
298
|
run: |
|
|
255
299
|
echo "=== Production Smoke Tests ==="
|
|
256
300
|
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "${PROD_URL}/")
|
|
@@ -273,7 +317,7 @@ jobs:
|
|
|
273
317
|
}
|
|
274
318
|
RESULTS_EOF
|
|
275
319
|
|
|
276
|
-
- name: Complete production smoke
|
|
320
|
+
- name: Complete production smoke executions
|
|
277
321
|
if: always() && env.BASE != '' && steps.host_deployment.outcome == 'success'
|
|
278
322
|
run: |
|
|
279
323
|
case "${{ steps.production_smoke.outcome }}" in
|
|
@@ -282,15 +326,15 @@ jobs:
|
|
|
282
326
|
skipped) OUTCOME=skipped; CHECK_STATUS=skipped ;;
|
|
283
327
|
*) OUTCOME=failed; CHECK_STATUS=failed ;;
|
|
284
328
|
esac
|
|
285
|
-
chmod +x scripts/report-test-
|
|
329
|
+
chmod +x scripts/report-test-execution.sh scripts/report-release-check.sh 2>/dev/null || true
|
|
286
330
|
for PREFIX in ${REQS}; do
|
|
287
331
|
RESP=$(curl -fsS -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
|
|
288
332
|
"${BASE}/api/ci/releases/resolve?projectSlug=${PROJECT_SLUG}&versionPrefix=${PREFIX}")
|
|
289
333
|
VERSION=$(echo "$RESP" | jq -r '.latest.version // empty')
|
|
290
334
|
[ -n "$VERSION" ] || VERSION="$PREFIX"
|
|
291
|
-
bash scripts/report-test-
|
|
335
|
+
bash scripts/report-test-execution.sh complete \
|
|
292
336
|
--project-slug "$PROJECT_SLUG" --release "$VERSION" \
|
|
293
|
-
--sdlc-stage 5 --environment production --
|
|
337
|
+
--sdlc-stage 5 --environment production --suite-kind smoke \
|
|
294
338
|
--provider github_actions --external-run-id "$CI_RUN" \
|
|
295
339
|
--external-run-attempt "${{ github.run_attempt }}" --external-job-id "production-smoke" \
|
|
296
340
|
--idempotency-key "github:${{ github.repository }}:post-deploy-prod.smoke:${CI_RUN}:${{ github.run_attempt }}:5:${VERSION}" \
|
|
@@ -324,8 +368,8 @@ jobs:
|
|
|
324
368
|
# incident-export.yml fires on close → incident_report evidence
|
|
325
369
|
# lands on the portal → ISO29119.3.5.4 + SOC2.CC7.2 flip to COVERED.
|
|
326
370
|
#
|
|
327
|
-
#
|
|
328
|
-
# report traces back to the specific test
|
|
371
|
+
# testExecutionId (CI run ID) cross-references #209 so the incident
|
|
372
|
+
# report traces back to the specific test execution that detected
|
|
329
373
|
# the production failure.
|
|
330
374
|
#
|
|
331
375
|
# Ensure the incident label exists (idempotent).
|
|
@@ -344,7 +388,7 @@ jobs:
|
|
|
344
388
|
|
|
345
389
|
**Production URL:** ${PROD_URL}
|
|
346
390
|
**Git SHA:** ${GIT_SHA}
|
|
347
|
-
**
|
|
391
|
+
**testExecutionId:** ${CI_RUN}
|
|
348
392
|
**Workflow run:** ${WORKFLOW_URL}
|
|
349
393
|
**Date:** ${DATE}
|
|
350
394
|
|
|
@@ -388,7 +432,7 @@ jobs:
|
|
|
388
432
|
- name: Promote in-scope releases (evidence + status)
|
|
389
433
|
if: steps.production_smoke.outcome == 'success'
|
|
390
434
|
run: |
|
|
391
|
-
chmod +x scripts/upload-evidence.sh scripts/report-test-
|
|
435
|
+
chmod +x scripts/upload-evidence.sh scripts/report-test-execution.sh scripts/report-release-check.sh 2>/dev/null || true
|
|
392
436
|
PROMOTED=0
|
|
393
437
|
EVIDENCE_FAILURES=0
|
|
394
438
|
for PREFIX in ${REQS}; do
|
|
@@ -399,14 +443,14 @@ jobs:
|
|
|
399
443
|
[ -z "$VERSION" ] && VERSION="${PREFIX}"
|
|
400
444
|
RELEASE_ID=$(echo "$RESP" | jq -r '.latest.id // empty')
|
|
401
445
|
REQ_FAILURES=0
|
|
402
|
-
LINEAGE_FLAGS=(--test-
|
|
403
|
-
|
|
404
|
-
bash scripts/report-test-
|
|
446
|
+
LINEAGE_FLAGS=(--test-execution "${CI_RUN}")
|
|
447
|
+
EXECUTION_OUT="$(mktemp)"
|
|
448
|
+
bash scripts/report-test-execution.sh start \
|
|
405
449
|
--project-slug "${PROJECT_SLUG}" \
|
|
406
450
|
--release "${VERSION}" \
|
|
407
451
|
--sdlc-stage 5 \
|
|
408
452
|
--environment production \
|
|
409
|
-
--
|
|
453
|
+
--suite-kind smoke \
|
|
410
454
|
--provider github_actions \
|
|
411
455
|
--external-run-id "${CI_RUN}" \
|
|
412
456
|
--external-run-attempt "${{ github.run_attempt }}" \
|
|
@@ -416,11 +460,11 @@ jobs:
|
|
|
416
460
|
--branch main \
|
|
417
461
|
--workflow-name "Post-Deploy Production" \
|
|
418
462
|
--workflow-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
|
|
419
|
-
--output-file "$
|
|
420
|
-
. "$
|
|
421
|
-
rm -f "$
|
|
422
|
-
if [ "${
|
|
423
|
-
LINEAGE_FLAGS+=(--evidence-scope
|
|
463
|
+
--output-file "$EXECUTION_OUT"
|
|
464
|
+
. "$EXECUTION_OUT"
|
|
465
|
+
rm -f "$EXECUTION_OUT"
|
|
466
|
+
if [ "${execution_supported:-false}" = "true" ] && [ -n "${execution_record_id:-}" ]; then
|
|
467
|
+
LINEAGE_FLAGS+=(--evidence-scope execution --test-execution-record-id "${execution_record_id}")
|
|
424
468
|
fi
|
|
425
469
|
# Production smoke evidence (whole-app health) attached to this release.
|
|
426
470
|
if [ -f prod-smoke-results.json ]; then
|
|
@@ -357,19 +357,19 @@ jobs:
|
|
|
357
357
|
- name: Generate and upload gate evidence
|
|
358
358
|
if: env.DEVAUDIT_BASE_URL != ''
|
|
359
359
|
run: |
|
|
360
|
-
chmod +x scripts/upload-evidence.sh scripts/report-test-
|
|
360
|
+
chmod +x scripts/upload-evidence.sh scripts/report-test-execution.sh 2>/dev/null || true
|
|
361
361
|
FLAGS="--git-sha ${{ github.sha }} --ci-run-id ${{ github.run_id }} --branch ${{ github.ref_name }}"
|
|
362
362
|
FLAGS="${FLAGS} --release ${{ needs.register-release.outputs.version }} --create-release-if-missing"
|
|
363
363
|
FLAGS="${FLAGS} --environment uat"
|
|
364
364
|
FLAGS="${FLAGS} --sdlc-stage 2"
|
|
365
|
-
PRIMARY_LINEAGE_FLAGS=(--test-
|
|
366
|
-
|
|
367
|
-
bash scripts/report-test-
|
|
365
|
+
PRIMARY_LINEAGE_FLAGS=(--test-execution "${{ github.run_id }}")
|
|
366
|
+
EXECUTION_OUT="$(mktemp)"
|
|
367
|
+
bash scripts/report-test-execution.sh start \
|
|
368
368
|
--project-slug {{PROJECT_SLUG}} \
|
|
369
369
|
--release "${{ needs.register-release.outputs.version }}" \
|
|
370
370
|
--sdlc-stage 2 \
|
|
371
371
|
--environment uat \
|
|
372
|
-
--
|
|
372
|
+
--suite-kind quality_gate \
|
|
373
373
|
--provider github_actions \
|
|
374
374
|
--external-run-id "${{ github.run_id }}" \
|
|
375
375
|
--external-run-attempt "${{ github.run_attempt }}" \
|
|
@@ -378,11 +378,11 @@ jobs:
|
|
|
378
378
|
--branch "${{ github.ref_name }}" \
|
|
379
379
|
--workflow-name "Quality Gates" \
|
|
380
380
|
--workflow-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
|
|
381
|
-
--output-file "$
|
|
382
|
-
. "$
|
|
383
|
-
rm -f "$
|
|
384
|
-
if [ "${
|
|
385
|
-
PRIMARY_LINEAGE_FLAGS+=(--evidence-scope
|
|
381
|
+
--output-file "$EXECUTION_OUT"
|
|
382
|
+
. "$EXECUTION_OUT"
|
|
383
|
+
rm -f "$EXECUTION_OUT"
|
|
384
|
+
if [ "${execution_supported:-false}" = "true" ] && [ -n "${execution_record_id:-}" ]; then
|
|
385
|
+
PRIMARY_LINEAGE_FLAGS+=(--evidence-scope execution --test-execution-record-id "${execution_record_id}")
|
|
386
386
|
fi
|
|
387
387
|
|
|
388
388
|
UPLOAD_FAILURES=0
|
|
@@ -546,7 +546,7 @@ jobs:
|
|
|
546
546
|
exit 1
|
|
547
547
|
fi
|
|
548
548
|
|
|
549
|
-
- name: Complete primary quality-gate
|
|
549
|
+
- name: Complete primary quality-gate execution
|
|
550
550
|
if: always() && env.DEVAUDIT_BASE_URL != ''
|
|
551
551
|
run: |
|
|
552
552
|
case "${{ job.status }}" in
|
|
@@ -554,13 +554,13 @@ jobs:
|
|
|
554
554
|
cancelled) OUTCOME=cancelled ;;
|
|
555
555
|
*) OUTCOME=failed ;;
|
|
556
556
|
esac
|
|
557
|
-
chmod +x scripts/report-test-
|
|
558
|
-
bash scripts/report-test-
|
|
557
|
+
chmod +x scripts/report-test-execution.sh 2>/dev/null || true
|
|
558
|
+
bash scripts/report-test-execution.sh complete \
|
|
559
559
|
--project-slug {{PROJECT_SLUG}} \
|
|
560
560
|
--release "${{ needs.register-release.outputs.version }}" \
|
|
561
561
|
--sdlc-stage 2 \
|
|
562
562
|
--environment uat \
|
|
563
|
-
--
|
|
563
|
+
--suite-kind quality_gate \
|
|
564
564
|
--provider github_actions \
|
|
565
565
|
--external-run-id "${{ github.run_id }}" \
|
|
566
566
|
--external-run-attempt "${{ github.run_attempt }}" \
|
|
@@ -42,6 +42,9 @@ jobs:
|
|
|
42
42
|
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
43
43
|
PR_BODY: ${{ github.event.pull_request.body }}
|
|
44
44
|
HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
|
45
|
+
BASE_REF: ${{ github.event.pull_request.base.ref }}
|
|
46
|
+
INTEGRATION_BRANCH: {{INTEGRATION_BRANCH}}
|
|
47
|
+
RELEASE_BRANCH: {{RELEASE_BRANCH}}
|
|
45
48
|
run: |
|
|
46
49
|
chmod +x scripts/check-release-pr-scope.sh 2>/dev/null || true
|
|
47
50
|
bash scripts/check-release-pr-scope.sh
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: Reconcile Verified Host Deployment
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
release:
|
|
7
|
+
description: 'REQ-XXX release receiving recovery evidence'
|
|
8
|
+
required: true
|
|
9
|
+
expected_sha:
|
|
10
|
+
description: 'Exact main SHA verified at the provider'
|
|
11
|
+
required: true
|
|
12
|
+
github_deployment_id:
|
|
13
|
+
description: 'Existing GitHub deployment ID to reconcile'
|
|
14
|
+
required: true
|
|
15
|
+
railway_deployment_id:
|
|
16
|
+
description: 'Terminal-successful Railway deployment ID'
|
|
17
|
+
required: true
|
|
18
|
+
railway_project:
|
|
19
|
+
description: 'Railway project ID'
|
|
20
|
+
required: true
|
|
21
|
+
railway_service:
|
|
22
|
+
description: 'Railway service ID or name'
|
|
23
|
+
required: true
|
|
24
|
+
health_url:
|
|
25
|
+
description: 'Configured production health URL'
|
|
26
|
+
required: true
|
|
27
|
+
|
|
28
|
+
jobs:
|
|
29
|
+
reconcile:
|
|
30
|
+
name: Verify and reconcile deployment
|
|
31
|
+
runs-on: {{RUNNER}}
|
|
32
|
+
permissions:
|
|
33
|
+
contents: read
|
|
34
|
+
deployments: write
|
|
35
|
+
env:
|
|
36
|
+
DEVAUDIT_API_KEY: ${{ secrets.DEVAUDIT_API_KEY }}
|
|
37
|
+
DEVAUDIT_BASE_URL: ${{ vars.DEVAUDIT_BASE_URL }}
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v6
|
|
40
|
+
with:
|
|
41
|
+
ref: ${{ inputs.expected_sha }}
|
|
42
|
+
- name: Verify provider deployment before reconciliation
|
|
43
|
+
env:
|
|
44
|
+
GH_TOKEN: ${{ github.token }}
|
|
45
|
+
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
|
|
46
|
+
run: |
|
|
47
|
+
chmod +x scripts/reconcile-railway-deployment.sh
|
|
48
|
+
bash scripts/reconcile-railway-deployment.sh \
|
|
49
|
+
--repo="${{ github.repository }}" --sha="${{ inputs.expected_sha }}" \
|
|
50
|
+
--github-deployment-id="${{ inputs.github_deployment_id }}" \
|
|
51
|
+
--railway-deployment-id="${{ inputs.railway_deployment_id }}" \
|
|
52
|
+
--railway-project="${{ inputs.railway_project }}" --railway-service="${{ inputs.railway_service }}" \
|
|
53
|
+
--health-url="${{ inputs.health_url }}"
|
|
54
|
+
- name: Upload manual reconciliation evidence
|
|
55
|
+
run: |
|
|
56
|
+
bash scripts/upload-evidence.sh {{PROJECT_SLUG}} "${{ inputs.release }}" test_report deployment-reconciliation.json \
|
|
57
|
+
--category release_artifact --release "${{ inputs.release }}" --environment production \
|
|
58
|
+
--git-sha "${{ inputs.expected_sha }}" --branch main --sdlc-stage 5 \
|
|
59
|
+
--meta-key 'provenance=manual_reconciliation' --meta-key 'provider=railway'
|
package/sdlc/package.json
CHANGED
|
@@ -82,7 +82,7 @@ A release is classified by version shape and release mode. **Most of this doc wa
|
|
|
82
82
|
|
|
83
83
|
### Housekeeping outcomes
|
|
84
84
|
|
|
85
|
-
Normal housekeeping skips the per-requirement evidence and approval path. Run the relevant gates, obtain terminal-green PR review, and merge to `develop`; the bare-date portal row is integration history. When a tracked release later absorbs the work, its bundled-change manifest preserves the source title, evidence ownership, stage, and
|
|
85
|
+
Normal housekeeping skips the per-requirement evidence and approval path. Run the relevant gates, obtain terminal-green PR review, and merge to `develop`; the bare-date portal row is integration history. When a tracked release later absorbs the work, its bundled-change manifest preserves the source title, evidence ownership, stage, and executions.
|
|
86
86
|
|
|
87
87
|
Use standalone housekeeping only when the change cannot reasonably wait for that tracked release. Follow `docs/release-playbooks/housekeeping-release.md`: add the validated `STANDALONE-HOUSEKEEPING-vYYYY.MM.DD.json` declaration, document why promotion is necessary, pass the required checks, and verify production. There is no automatic release-ticket or security-summary stub PR for either housekeeping outcome.
|
|
88
88
|
|
|
@@ -173,27 +173,27 @@ Rules:
|
|
|
173
173
|
- `environment_gap` and `precondition_gap` skips must be resolved before listing — if they appear here, the gate is not truly `SKIPPED` with operator approval.
|
|
174
174
|
- A skipped test never proves an acceptance criterion.
|
|
175
175
|
|
|
176
|
-
## Test
|
|
176
|
+
## Test Executions
|
|
177
177
|
|
|
178
|
-
Prefer the first-class release-journey /
|
|
178
|
+
Prefer the first-class release-journey / execution API when the portal exposes it. Generate the table from the API response rather than inferring executions from uploaded files:
|
|
179
179
|
|
|
180
180
|
```bash
|
|
181
181
|
# Example:
|
|
182
182
|
curl -s -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
|
|
183
183
|
"${DEVAUDIT_BASE_URL%/}/api/projects/<slug>/releases/REQ-XXX/journey" \
|
|
184
184
|
> /tmp/release-journey.json
|
|
185
|
-
bash scripts/render-test-
|
|
185
|
+
bash scripts/render-test-executions.sh /tmp/release-journey.json
|
|
186
186
|
```
|
|
187
187
|
|
|
188
|
-
The helper renders a first-class table when `.
|
|
188
|
+
The helper renders a first-class table when `.testExecutions[]` is present. It must not infer executions from legacy artifact grouping.
|
|
189
189
|
|
|
190
|
-
| Source Release | SDLC Stage |
|
|
190
|
+
| Source Release | SDLC Stage | Execution | Kind | Outcome | Workflow / Run | Related Evidence | Incident / Remediation | Date |
|
|
191
191
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
192
192
|
| REQ-XXX | 2 implement_test | #1 | quality_gate | passed | [Quality Gates](https://github.com/example/repo/actions/runs/123) (run 123, attempt 1) | quality-gates.json | None | 2026-07-16 11:03:00 |
|
|
193
193
|
| REQ-XXX | 2 implement_test | #2 | e2e | passed | [E2E Regression](https://github.com/example/repo/actions/runs/124) (run 124, attempt 2) | e2e-results.json, screenshots.zip | Incident #501 fixed in PR #507 | 2026-07-16 11:19:00 |
|
|
194
194
|
| REQ-089 | 4 uat_review | #1 | uat | passed | [UAT Review](https://devaudit.example/releases/REQ-089) | uat-checklist.md | None | 2026-07-16 12:02:00 |
|
|
195
195
|
|
|
196
|
-
**Final assessment:** [First-class
|
|
196
|
+
**Final assessment:** [First-class test execution records show all stage executions passed / N executions failed — see incidents]
|
|
197
197
|
|
|
198
198
|
## Bundled Release Context
|
|
199
199
|
|
|
@@ -338,12 +338,12 @@ status: "nil"
|
|
|
338
338
|
|
|
339
339
|
## Attestation
|
|
340
340
|
|
|
341
|
-
No incidents or defects were discovered during the test
|
|
341
|
+
No incidents or defects were discovered during the test execution for release `<version>`.
|
|
342
342
|
|
|
343
343
|
## Scope
|
|
344
344
|
|
|
345
345
|
- **Release:** <version>
|
|
346
|
-
- **Test
|
|
346
|
+
- **Test execution:** <description>
|
|
347
347
|
- **Test cases executed:** <count>
|
|
348
348
|
- **Test cases passed:** <count>
|
|
349
349
|
- **Test cases failed:** 0
|
|
@@ -352,7 +352,7 @@ No incidents or defects were discovered during the test cycle for release `<vers
|
|
|
352
352
|
|
|
353
353
|
## Framework attribution
|
|
354
354
|
|
|
355
|
-
- [x] `ISO29119.3.5.4` (Test incident report — nil report for this release
|
|
355
|
+
- [x] `ISO29119.3.5.4` (Test incident report — nil report for this release execution)
|
|
356
356
|
|
|
357
357
|
## Sign-off
|
|
358
358
|
|
|
@@ -179,7 +179,7 @@ CI runs automatically on this PR. The following gates must pass before merge:
|
|
|
179
179
|
- [ ] Implementation plan present and matches implementation (MEDIUM/HIGH risk)
|
|
180
180
|
- [ ] Release ticket created
|
|
181
181
|
- [ ] Test evidence saved
|
|
182
|
-
- [ ] Test execution summary includes Test
|
|
182
|
+
- [ ] Test execution summary includes Test Executions section (all CI runs for this release listed)
|
|
183
183
|
- [ ] Security evidence saved
|
|
184
184
|
- [ ] AI use documented
|
|
185
185
|
|
|
@@ -151,7 +151,7 @@ The backend stores both with reviewer identity, SHA, and timestamp. This satisfi
|
|
|
151
151
|
|
|
152
152
|
1. Wait for `post-deploy-prod.yml` to complete successfully **and** for its host-deployment check to confirm terminal success for the merged SHA.
|
|
153
153
|
2. Open the release in DevAudit: `https://[DEVAUDIT_BASE_URL]/projects/[PROJECT_SLUG]/releases/[releaseId]`.
|
|
154
|
-
3. Review the production deployment and smoke
|
|
154
|
+
3. Review the production deployment and smoke test execution records, their evidence, and any post-deploy actions logged in the release ticket. Do not approve while either execution or the host deployment remains queued/in progress.
|
|
155
155
|
4. Click **Approve Production** — status transitions to `prod_approved`.
|
|
156
156
|
5. Click **Mark as Released** — status transitions to `released`. This dispatches the automated close-out flow.
|
|
157
157
|
|
|
@@ -67,7 +67,7 @@ The [`sdlc-implementer`](#skills-inventory) skill is the **default way to implem
|
|
|
67
67
|
> Implement issue #N under the SDLC.
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
It **triages first** (Phase 0): it reads the issue + labels, classifies the change-type against the [change-workflows](https://github.com/metasession-dev/DevAudit-Installer/blob/main/docs/change-workflows.md) taxonomy, announces a **Workflow Decision** (which path, which gates, which approvals, what's skipped), and routes — only a tracked change continues into the full
|
|
70
|
+
It **triages first** (Phase 0): it reads the issue + labels, classifies the change-type against the [change-workflows](https://github.com/metasession-dev/DevAudit-Installer/blob/main/docs/change-workflows.md) taxonomy, announces a **Workflow Decision** (which path, which gates, which approvals, what's skipped), and routes — only a tracked change continues into the full execution; housekeeping / trivial / doc-only is announced and handed off to its lighter path. So pointing the skill at an issue no longer defaults to maximum ceremony — it decides the path at pickup.
|
|
71
71
|
|
|
72
72
|
For a tracked change it then runs Phases 1–4 unattended (with a plan-approval pause for HIGH/CRITICAL risk, or always-on via `--require-plan-approval`): classify risk, assign the next `REQ-XXX`, write the implementation plan, update `RTM.md`, implement, delegate all end-to-end / visual test work to [`e2e-test-engineer`](#skills-inventory), run the gates, compile evidence, open the PR, and submit for UAT review on the portal. It then **halts** at the UAT gate. After a reviewer approves on the portal:
|
|
73
73
|
|
|
@@ -77,9 +77,9 @@ For a tracked change it then runs Phases 1–4 unattended (with a plan-approval
|
|
|
77
77
|
|
|
78
78
|
It runs Phase 5: merge, monitor post-deploy, confirm production smoke evidence, advance the release. If changes are requested at UAT instead of approval, it addresses them and re-submits for UAT re-review. It **refuses** issues that decompose into multiple requirements (split them first).
|
|
79
79
|
|
|
80
|
-
**Where it routes (the Phase-0 decision):** the skill now makes this call at pickup rather than you discovering mid-PR that you over- (or under-) ceremonied. It still **drives each path to completion** — these are the paths it takes *instead of* the full tracked
|
|
80
|
+
**Where it routes (the Phase-0 decision):** the skill now makes this call at pickup rather than you discovering mid-PR that you over- (or under-) ceremonied. It still **drives each path to completion** — these are the paths it takes *instead of* the full tracked execution, not points where it abandons you:
|
|
81
81
|
|
|
82
|
-
- **Trivial / housekeeping changes** → the skill drives the lightweight escape hatch (above) to merge: branch → all gates locally → `chore:`/`docs:`/`ci:` PR → review → merge. No requirement, no tracked
|
|
82
|
+
- **Trivial / housekeeping changes** → the skill drives the lightweight escape hatch (above) to merge: branch → all gates locally → `chore:`/`docs:`/`ci:` PR → review → merge. No requirement, no tracked execution, but it guides every step. Docs, formatting, dependency bumps, CI tweaks (`docs:` / `chore:` / `ci:` …) don't need a requirement. (Note: `feat` / `fix` / `refactor` / `perf` commits **do** require a `[REQ-XXX]` / `Ref: REQ-XXX` and are rejected by commitlint + `validate-commits.sh` without one.)
|
|
83
83
|
- **Compliance-doc-only** → the skill drives a docs push against an **existing** `REQ-XXX` through to merge; no new requirement, no quality gates.
|
|
84
84
|
- **Stage-1 planning in isolation, or e2e test work alone** → run the manual walkthrough / invoke `e2e-test-engineer` directly.
|
|
85
85
|
- **Cross-issue refactors** spanning multiple `REQ-XXX` scopes → out of the one-issue contract; the skill refuses and asks you to split.
|