@metasession.co/devaudit-cli 0.1.27 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/scripts/upload-evidence.sh +12 -0
- package/sdlc/files/_common/scripts/derive-release-version.sh +72 -6
- package/sdlc/files/_common/scripts/derive-release-version.test.sh +126 -0
- package/sdlc/files/_common/scripts/validate-commits.sh +4 -1
- package/sdlc/files/_common/skills/e2e-test-engineer/SKILL.md +14 -3
- package/sdlc/files/_common/skills/e2e-test-engineer/references/common-patterns.md +98 -0
- package/sdlc/files/_common/skills/sdlc-implementer/SKILL.md +57 -5
- package/sdlc/files/ci/ci.yml.template +61 -3
- package/sdlc/files/ci/python/ci.yml.template +61 -4
|
@@ -76,11 +76,13 @@ jobs:
|
|
|
76
76
|
# ── Gate 1: TypeScript ──
|
|
77
77
|
|
|
78
78
|
- name: TypeScript Check
|
|
79
|
+
id: typescript
|
|
79
80
|
run: npx tsc --noEmit
|
|
80
81
|
|
|
81
82
|
# ── Gate 2: SAST (Semgrep) ──
|
|
82
83
|
|
|
83
84
|
- name: SAST Scan
|
|
85
|
+
id: sast
|
|
84
86
|
run: |
|
|
85
87
|
# --output writes the JSON report to the file directly; stderr
|
|
86
88
|
# (progress/metrics/version notices) goes to /dev/null. Using
|
|
@@ -106,6 +108,7 @@ jobs:
|
|
|
106
108
|
# ── Gate 3: Dependency Audit ──
|
|
107
109
|
|
|
108
110
|
- name: Dependency Audit
|
|
111
|
+
id: dep-audit
|
|
109
112
|
run: |
|
|
110
113
|
# stderr → /dev/null so warnings can't corrupt the JSON (DevAudit #48)
|
|
111
114
|
npm audit --json > dependency-audit.json 2>/dev/null || true
|
|
@@ -144,10 +147,34 @@ jobs:
|
|
|
144
147
|
# ── Gate 5: Build ──
|
|
145
148
|
|
|
146
149
|
- name: Build Check
|
|
150
|
+
id: build
|
|
147
151
|
run: npm run build
|
|
148
152
|
env:
|
|
149
153
|
{{BUILD_ENV}}
|
|
150
154
|
|
|
155
|
+
# ── Summarise per-gate outcomes ──
|
|
156
|
+
#
|
|
157
|
+
# Runs unconditionally so a failed earlier gate doesn't strand the
|
|
158
|
+
# outcome of the gates that did run. Step outcomes are one of
|
|
159
|
+
# `success` / `failure` / `skipped` / `cancelled`. The upload-evidence
|
|
160
|
+
# job maps these to `passed` / `failed` / `skipped` and sends the
|
|
161
|
+
# result as `gateStatus=` on each per-gate upload, so the portal can
|
|
162
|
+
# render a failed gate as failed (with the run artefact) rather than
|
|
163
|
+
# showing it as missing. DevAudit-Installer#96.
|
|
164
|
+
|
|
165
|
+
- name: Summarise gate outcomes
|
|
166
|
+
if: always()
|
|
167
|
+
run: |
|
|
168
|
+
cat > gate-outcomes.json <<EOF
|
|
169
|
+
{
|
|
170
|
+
"typescript": "${{ steps.typescript.outcome }}",
|
|
171
|
+
"sast": "${{ steps.sast.outcome }}",
|
|
172
|
+
"dependency_audit": "${{ steps.dep-audit.outcome }}",
|
|
173
|
+
"build": "${{ steps.build.outcome }}"
|
|
174
|
+
}
|
|
175
|
+
EOF
|
|
176
|
+
cat gate-outcomes.json
|
|
177
|
+
|
|
151
178
|
# ── Upload artifacts ──
|
|
152
179
|
|
|
153
180
|
- uses: actions/upload-artifact@v4
|
|
@@ -162,6 +189,7 @@ jobs:
|
|
|
162
189
|
e2e-auth-results.json
|
|
163
190
|
playwright-report/
|
|
164
191
|
coverage/coverage-summary.json
|
|
192
|
+
gate-outcomes.json
|
|
165
193
|
compliance/evidence/*/screenshots/*.png
|
|
166
194
|
retention-days: 90
|
|
167
195
|
|
|
@@ -278,7 +306,11 @@ jobs:
|
|
|
278
306
|
name: Upload Evidence
|
|
279
307
|
runs-on: {{RUNNER}}
|
|
280
308
|
needs: [quality-gates, register-release]
|
|
281
|
-
|
|
309
|
+
# `always()` instead of `!failure()` so failed gates still upload their
|
|
310
|
+
# evidence — `status=failed` is itself the audit trail. `!cancelled()`
|
|
311
|
+
# still guards against partial state on operator-cancel.
|
|
312
|
+
# DevAudit-Installer#96.
|
|
313
|
+
if: ${{ always() && !cancelled() && vars.DEVAUDIT_BASE_URL != '' && needs.register-release.result == 'success' }}
|
|
282
314
|
env:
|
|
283
315
|
DEVAUDIT_BASE_URL: ${{ vars.DEVAUDIT_BASE_URL }}
|
|
284
316
|
DEVAUDIT_API_KEY: ${{ secrets.DEVAUDIT_API_KEY }}
|
|
@@ -315,6 +347,32 @@ jobs:
|
|
|
315
347
|
fi
|
|
316
348
|
}
|
|
317
349
|
|
|
350
|
+
# Map step outcomes from gate-outcomes.json (written by the
|
|
351
|
+
# `Summarise gate outcomes` step) to gateStatus values the
|
|
352
|
+
# uploader forwards to the portal. Failed gates upload as
|
|
353
|
+
# gateStatus=failed so the portal can distinguish a gate that
|
|
354
|
+
# ran-and-failed from one that never ran. DevAudit-Installer#96.
|
|
355
|
+
gate_status() {
|
|
356
|
+
case "$1" in
|
|
357
|
+
success) echo passed ;;
|
|
358
|
+
failure) echo failed ;;
|
|
359
|
+
*) echo skipped ;;
|
|
360
|
+
esac
|
|
361
|
+
}
|
|
362
|
+
STATUS_TYPESCRIPT=skipped
|
|
363
|
+
STATUS_SAST=skipped
|
|
364
|
+
STATUS_DEPAUDIT=skipped
|
|
365
|
+
STATUS_BUILD=skipped
|
|
366
|
+
if [ -f ci-evidence/gate-outcomes.json ]; then
|
|
367
|
+
STATUS_TYPESCRIPT=$(gate_status "$(jq -r '.typescript // "skipped"' ci-evidence/gate-outcomes.json)")
|
|
368
|
+
STATUS_SAST=$(gate_status "$(jq -r '.sast // "skipped"' ci-evidence/gate-outcomes.json)")
|
|
369
|
+
STATUS_DEPAUDIT=$(gate_status "$(jq -r '.dependency_audit // "skipped"' ci-evidence/gate-outcomes.json)")
|
|
370
|
+
STATUS_BUILD=$(gate_status "$(jq -r '.build // "skipped"' ci-evidence/gate-outcomes.json)")
|
|
371
|
+
upload gate-outcomes.json \
|
|
372
|
+
{{PROJECT_SLUG}} _compliance-docs compliance_document ci-evidence/gate-outcomes.json \
|
|
373
|
+
--category ci_pipeline ${FLAGS}
|
|
374
|
+
fi
|
|
375
|
+
|
|
318
376
|
# Re-generate gate evidence as fallback if artifact download failed
|
|
319
377
|
mkdir -p ci-evidence
|
|
320
378
|
if [ ! -f ci-evidence/sast-results.json ]; then
|
|
@@ -337,7 +395,7 @@ jobs:
|
|
|
337
395
|
if [ -f ci-evidence/sast-results.json ]; then
|
|
338
396
|
upload sast-results.json \
|
|
339
397
|
{{PROJECT_SLUG}} _compliance-docs sast_report ci-evidence/sast-results.json \
|
|
340
|
-
--category security_scan ${FLAGS}
|
|
398
|
+
--category security_scan --gate-status "$STATUS_SAST" ${FLAGS}
|
|
341
399
|
fi
|
|
342
400
|
|
|
343
401
|
# Upload dependency audit — precise evidence_type=dependency_audit
|
|
@@ -345,7 +403,7 @@ jobs:
|
|
|
345
403
|
if [ -f ci-evidence/dependency-audit.json ]; then
|
|
346
404
|
upload dependency-audit.json \
|
|
347
405
|
{{PROJECT_SLUG}} _compliance-docs dependency_audit ci-evidence/dependency-audit.json \
|
|
348
|
-
--category security_scan ${FLAGS}
|
|
406
|
+
--category security_scan --gate-status "$STATUS_DEPAUDIT" ${FLAGS}
|
|
349
407
|
fi
|
|
350
408
|
|
|
351
409
|
# Upload E2E test results (ci_pipeline category)
|
|
@@ -61,19 +61,23 @@ jobs:
|
|
|
61
61
|
# ── Gate 1: Lint + format (ruff) ──
|
|
62
62
|
|
|
63
63
|
- name: Ruff lint
|
|
64
|
+
id: ruff-lint
|
|
64
65
|
run: ruff check {{SOURCE_DIRS}}
|
|
65
66
|
|
|
66
67
|
- name: Ruff format check
|
|
68
|
+
id: ruff-format
|
|
67
69
|
run: ruff format --check {{SOURCE_DIRS}}
|
|
68
70
|
|
|
69
71
|
# ── Gate 2: Type check (mypy --strict) ──
|
|
70
72
|
|
|
71
73
|
- name: Type Check (mypy)
|
|
74
|
+
id: mypy
|
|
72
75
|
run: mypy {{SOURCE_DIRS}}
|
|
73
76
|
|
|
74
77
|
# ── Gate 3: SAST (Semgrep) ──
|
|
75
78
|
|
|
76
79
|
- name: SAST Scan
|
|
80
|
+
id: sast
|
|
77
81
|
run: |
|
|
78
82
|
mkdir -p ci-evidence
|
|
79
83
|
semgrep scan --config auto {{SOURCE_DIRS}} \
|
|
@@ -95,6 +99,7 @@ jobs:
|
|
|
95
99
|
# ── Gate 4: Dependency Audit (pip-audit) ──
|
|
96
100
|
|
|
97
101
|
- name: Dependency Audit
|
|
102
|
+
id: dep-audit
|
|
98
103
|
run: |
|
|
99
104
|
mkdir -p ci-evidence
|
|
100
105
|
pip-audit --format=json > ci-evidence/dependency-audit.json 2>&1 || true
|
|
@@ -124,6 +129,7 @@ jobs:
|
|
|
124
129
|
{{DATABASE_URI_STEP}}
|
|
125
130
|
|
|
126
131
|
- name: Tests
|
|
132
|
+
id: tests
|
|
127
133
|
run: |
|
|
128
134
|
mkdir -p ci-evidence
|
|
129
135
|
pytest --junit-xml=ci-evidence/junit.xml --tb=short
|
|
@@ -131,10 +137,31 @@ jobs:
|
|
|
131
137
|
# ── Gate 6: Build ──
|
|
132
138
|
|
|
133
139
|
- name: Build Check
|
|
140
|
+
id: build
|
|
134
141
|
run: python -m build --sdist --wheel
|
|
135
142
|
env:
|
|
136
143
|
{{BUILD_ENV}}
|
|
137
144
|
|
|
145
|
+
# ── Summarise per-gate outcomes ──
|
|
146
|
+
# Runs unconditionally so a failed earlier gate doesn't strand the
|
|
147
|
+
# outcome of the gates that did run. DevAudit-Installer#96.
|
|
148
|
+
- name: Summarise gate outcomes
|
|
149
|
+
if: always()
|
|
150
|
+
run: |
|
|
151
|
+
mkdir -p ci-evidence
|
|
152
|
+
cat > ci-evidence/gate-outcomes.json <<EOF
|
|
153
|
+
{
|
|
154
|
+
"ruff_lint": "${{ steps.ruff-lint.outcome }}",
|
|
155
|
+
"ruff_format": "${{ steps.ruff-format.outcome }}",
|
|
156
|
+
"mypy": "${{ steps.mypy.outcome }}",
|
|
157
|
+
"sast": "${{ steps.sast.outcome }}",
|
|
158
|
+
"dependency_audit": "${{ steps.dep-audit.outcome }}",
|
|
159
|
+
"tests": "${{ steps.tests.outcome }}",
|
|
160
|
+
"build": "${{ steps.build.outcome }}"
|
|
161
|
+
}
|
|
162
|
+
EOF
|
|
163
|
+
cat ci-evidence/gate-outcomes.json
|
|
164
|
+
|
|
138
165
|
# ── Upload artifacts ──
|
|
139
166
|
|
|
140
167
|
# actions/upload-artifact@v4 doesn't honour the job's `working-directory`;
|
|
@@ -150,6 +177,7 @@ jobs:
|
|
|
150
177
|
{{WORKING_DIR_PREFIX}}ci-evidence/sast-results.json
|
|
151
178
|
{{WORKING_DIR_PREFIX}}ci-evidence/dependency-audit.json
|
|
152
179
|
{{WORKING_DIR_PREFIX}}ci-evidence/junit.xml
|
|
180
|
+
{{WORKING_DIR_PREFIX}}ci-evidence/gate-outcomes.json
|
|
153
181
|
{{WORKING_DIR_PREFIX}}dist/
|
|
154
182
|
retention-days: 90
|
|
155
183
|
|
|
@@ -256,7 +284,11 @@ jobs:
|
|
|
256
284
|
name: Upload Evidence
|
|
257
285
|
runs-on: {{RUNNER}}
|
|
258
286
|
needs: [quality-gates, register-release]
|
|
259
|
-
|
|
287
|
+
# `always()` instead of `!failure()` so failed gates still upload their
|
|
288
|
+
# evidence — `status=failed` is itself the audit trail. `!cancelled()`
|
|
289
|
+
# still guards against partial state on operator-cancel.
|
|
290
|
+
# DevAudit-Installer#96.
|
|
291
|
+
if: ${{ always() && !cancelled() && vars.DEVAUDIT_BASE_URL != '' && needs.register-release.result == 'success' }}
|
|
260
292
|
env:
|
|
261
293
|
DEVAUDIT_BASE_URL: ${{ vars.DEVAUDIT_BASE_URL }}
|
|
262
294
|
DEVAUDIT_API_KEY: ${{ secrets.DEVAUDIT_API_KEY }}
|
|
@@ -293,25 +325,50 @@ jobs:
|
|
|
293
325
|
fi
|
|
294
326
|
}
|
|
295
327
|
|
|
328
|
+
# Map step outcomes from gate-outcomes.json (written by the
|
|
329
|
+
# `Summarise gate outcomes` step) to gateStatus values the
|
|
330
|
+
# uploader forwards to the portal. Failed gates upload as
|
|
331
|
+
# gateStatus=failed so the portal can distinguish a gate that
|
|
332
|
+
# ran-and-failed from one that never ran. DevAudit-Installer#96.
|
|
333
|
+
gate_status() {
|
|
334
|
+
case "$1" in
|
|
335
|
+
success) echo passed ;;
|
|
336
|
+
failure) echo failed ;;
|
|
337
|
+
*) echo skipped ;;
|
|
338
|
+
esac
|
|
339
|
+
}
|
|
340
|
+
STATUS_SAST=skipped
|
|
341
|
+
STATUS_DEPAUDIT=skipped
|
|
342
|
+
STATUS_TESTS=skipped
|
|
343
|
+
OUTCOMES_FILE="{{WORKING_DIR_PREFIX}}ci-evidence/gate-outcomes.json"
|
|
344
|
+
if [ -f "$OUTCOMES_FILE" ]; then
|
|
345
|
+
STATUS_SAST=$(gate_status "$(jq -r '.sast // "skipped"' "$OUTCOMES_FILE")")
|
|
346
|
+
STATUS_DEPAUDIT=$(gate_status "$(jq -r '.dependency_audit // "skipped"' "$OUTCOMES_FILE")")
|
|
347
|
+
STATUS_TESTS=$(gate_status "$(jq -r '.tests // "skipped"' "$OUTCOMES_FILE")")
|
|
348
|
+
upload gate-outcomes.json \
|
|
349
|
+
{{PROJECT_SLUG}} _compliance-docs compliance_document "$OUTCOMES_FILE" \
|
|
350
|
+
--category ci_pipeline ${FLAGS}
|
|
351
|
+
fi
|
|
352
|
+
|
|
296
353
|
mkdir -p {{WORKING_DIR_PREFIX}}ci-evidence
|
|
297
354
|
|
|
298
355
|
if [ -f {{WORKING_DIR_PREFIX}}ci-evidence/sast-results.json ]; then
|
|
299
356
|
upload sast-results.json \
|
|
300
357
|
{{PROJECT_SLUG}} _compliance-docs audit_log {{WORKING_DIR_PREFIX}}ci-evidence/sast-results.json \
|
|
301
|
-
--category security_scan ${FLAGS}
|
|
358
|
+
--category security_scan --gate-status "$STATUS_SAST" ${FLAGS}
|
|
302
359
|
fi
|
|
303
360
|
|
|
304
361
|
if [ -f {{WORKING_DIR_PREFIX}}ci-evidence/dependency-audit.json ]; then
|
|
305
362
|
upload dependency-audit.json \
|
|
306
363
|
{{PROJECT_SLUG}} _compliance-docs audit_log {{WORKING_DIR_PREFIX}}ci-evidence/dependency-audit.json \
|
|
307
|
-
--category security_scan ${FLAGS}
|
|
364
|
+
--category security_scan --gate-status "$STATUS_DEPAUDIT" ${FLAGS}
|
|
308
365
|
fi
|
|
309
366
|
|
|
310
367
|
# pytest junit.xml is the Python equivalent of e2e-results.json — same `e2e_result` evidence type
|
|
311
368
|
if [ -f {{WORKING_DIR_PREFIX}}ci-evidence/junit.xml ]; then
|
|
312
369
|
upload junit.xml \
|
|
313
370
|
{{PROJECT_SLUG}} _compliance-docs e2e_result {{WORKING_DIR_PREFIX}}ci-evidence/junit.xml \
|
|
314
|
-
--category ci_pipeline ${FLAGS}
|
|
371
|
+
--category ci_pipeline --gate-status "$STATUS_TESTS" ${FLAGS}
|
|
315
372
|
fi
|
|
316
373
|
|
|
317
374
|
if [ -f "compliance/test-summary-report.md" ]; then
|