@metasession.co/devaudit-cli 0.3.11 → 0.3.13
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 +94 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/scripts/upload-evidence.sh +55 -1
- package/sdlc/ai-rules/SDLC_RULES.md +27 -7
- package/sdlc/files/_common/0-project-setup.md +13 -7
- package/sdlc/files/_common/Periodic_Security_Review_Schedule.md +5 -5
- package/sdlc/files/_common/README_TEMPLATE.md +8 -8
- package/sdlc/files/_common/Test_Architecture.md +15 -15
- package/sdlc/files/_common/Test_Plan_TEMPLATE.md +13 -13
- package/sdlc/files/_common/Test_Policy.md +2 -2
- package/sdlc/files/_common/Test_Strategy.md +10 -10
- package/sdlc/files/_common/governance/ai-disclosure.md.template +3 -3
- package/sdlc/files/_common/governance/dpia.md.template +3 -3
- package/sdlc/files/_common/governance/incident-report.md.template +3 -3
- package/sdlc/files/_common/governance/nil-incident-report.md.template +1 -1
- package/sdlc/files/_common/governance/periodic-review.md.template +3 -3
- package/sdlc/files/_common/governance/risk-register.md.template +1 -1
- package/sdlc/files/_common/governance/ropa.md.template +2 -2
- package/sdlc/files/_common/joining-an-existing-project.md +4 -4
- package/sdlc/files/_common/scripts/check-host-deployment.sh +107 -0
- package/sdlc/files/_common/scripts/check-host-deployment.test.sh +119 -0
- package/sdlc/files/_common/scripts/check-release-pr-scope.sh +95 -0
- package/sdlc/files/_common/scripts/check-release-pr-scope.test.sh +137 -0
- package/sdlc/files/_common/scripts/close-out-release.sh +175 -54
- package/sdlc/files/_common/scripts/close-out-release.test.sh +90 -0
- package/sdlc/files/_common/scripts/extract-release-metadata.sh +65 -0
- package/sdlc/files/_common/scripts/extract-release-metadata.test.sh +38 -0
- package/sdlc/files/_common/scripts/generate-bundled-changes.sh +307 -58
- package/sdlc/files/_common/scripts/generate-bundled-changes.test.sh +128 -78
- package/sdlc/files/_common/scripts/generate-housekeeping-release-ticket.sh +18 -9
- package/sdlc/files/_common/scripts/generate-housekeeping-release-ticket.test.sh +66 -0
- package/sdlc/files/_common/scripts/generate-security-summary.sh +14 -5
- package/sdlc/files/_common/scripts/generate-security-summary.test.sh +57 -0
- package/sdlc/files/_common/scripts/prepare-release-pr.sh +44 -4
- package/sdlc/files/_common/scripts/render-test-cycles.sh +227 -0
- package/sdlc/files/_common/scripts/render-test-cycles.test.sh +107 -0
- package/sdlc/files/_common/scripts/report-test-cycle.sh +344 -0
- package/sdlc/files/_common/scripts/submit-bundle-manifest.sh +100 -0
- package/sdlc/files/_common/scripts/submit-bundle-manifest.test.sh +138 -0
- package/sdlc/files/_common/scripts/upload-evidence.sh +55 -1
- package/sdlc/files/_common/scripts/upload-evidence.test.sh +119 -0
- package/sdlc/files/_common/scripts/validate-compliance-artifacts.sh +169 -2
- package/sdlc/files/_common/scripts/validate-compliance-artifacts.test.sh +169 -0
- package/sdlc/files/_common/scripts/validate-test-summary.test.sh +4 -4
- package/sdlc/files/_common/skills/adr-author/SKILL.md +2 -2
- package/sdlc/files/_common/skills/e2e-test-engineer/SKILL.md +2 -2
- package/sdlc/files/_common/skills/e2e-test-engineer/references/bootstrap.md +5 -5
- package/sdlc/files/_common/skills/governance-doc-author/references/incident-classification.md +1 -1
- package/sdlc/files/_common/skills/requirements-aligner/SKILL.md +4 -4
- package/sdlc/files/_common/skills/sdlc-implementer/SKILL.md +57 -3
- package/sdlc/files/_common/skills/sdlc-implementer/references/change-request-loop.md +1 -1
- package/sdlc/files/_common/skills/sdlc-implementer/references/compliance-constraints.md +7 -14
- package/sdlc/files/ci/check-release-approval.yml.template +18 -28
- package/sdlc/files/ci/ci.yml.template +95 -35
- package/sdlc/files/ci/close-out-release.yml.template +7 -4
- package/sdlc/files/ci/compliance-evidence.yml.template +234 -49
- package/sdlc/files/ci/feature-e2e.yml.template +84 -1
- package/sdlc/files/ci/post-deploy-prod.yml.template +59 -4
- package/sdlc/files/ci/python/ci.yml.template +60 -12
- package/sdlc/files/ci/quality-gates-provenance.yml.template +123 -28
- package/sdlc/package.json +1 -1
- package/sdlc/src/bin/devaudit-sdlc.js +44 -2
- package/sdlc/src/blueprints/1-plan-requirement.raw.md +6 -1
- package/sdlc/src/blueprints/2-implement-and-test.raw.md +1 -1
- package/sdlc/src/blueprints/3-compile-evidence.raw.md +102 -20
- package/sdlc/src/blueprints/4-submit-for-review.raw.md +3 -3
- package/sdlc/src/blueprints/5-deploy-main.raw.md +2 -2
|
@@ -43,7 +43,7 @@ End-to-end validation of complete user workflows from UI to database. Primary re
|
|
|
43
43
|
Full E2E regression on every PR is expensive — a 30+ minute wait per release-PR blocks velocity for diminishing marginal safety once smoke covers the headline flows. The framework's gating model maps the existing MoSCoW prioritisation onto three tiers, each gated at a different point in the workflow:
|
|
44
44
|
|
|
45
45
|
| Tier | Location | When it runs | Wall-clock target | Audit role |
|
|
46
|
-
|
|
46
|
+
| --- | --- | --- | --- | --- |
|
|
47
47
|
| **smoke** | `e2e/smoke/*.spec.ts` | every push to `$INTEGRATION_BRANCH` (via `ci.yml`) | ~3–5 min | fast feedback on every change |
|
|
48
48
|
| **critical** | `e2e/smoke/` + `e2e/critical/*.spec.ts` | PR-to-`$RELEASE_BRANCH` (via `e2e-regression.yml`) | ~10–15 min | release-readiness Must gate |
|
|
49
49
|
| **regression** | all `e2e/**/*.spec.ts` | nightly + push-to-`$RELEASE_BRANCH` + `workflow_dispatch` | ~35 min (or your project's full pack) | full audit trail + drift catch |
|
|
@@ -105,7 +105,7 @@ Specific tools implementing these gates are defined in the Test Architecture.
|
|
|
105
105
|
Defined in the Periodic Security Review Schedule:
|
|
106
106
|
|
|
107
107
|
| Activity | Frequency |
|
|
108
|
-
|
|
108
|
+
| --- | --- |
|
|
109
109
|
| Full codebase SAST review | Quarterly |
|
|
110
110
|
| Dependency deep audit | Quarterly |
|
|
111
111
|
| Access control review | Quarterly |
|
|
@@ -117,7 +117,7 @@ Defined in the Periodic Security Review Schedule:
|
|
|
117
117
|
### Remediation SLAs
|
|
118
118
|
|
|
119
119
|
| Severity | Per-Commit Gate | Periodic Finding |
|
|
120
|
-
|
|
120
|
+
| --- | --- | --- |
|
|
121
121
|
| Critical | Block merge, fix immediately | 7 days |
|
|
122
122
|
| High | Block merge, fix immediately | 30 days |
|
|
123
123
|
| Medium | Document and plan remediation | 90 days |
|
|
@@ -134,7 +134,7 @@ This section implements the AI governance commitments from the Test Policy with
|
|
|
134
134
|
AI involvement is a factor in risk classification:
|
|
135
135
|
|
|
136
136
|
| Code Category | Base Risk | AI Adjustment |
|
|
137
|
-
|
|
137
|
+
| --- | --- | --- |
|
|
138
138
|
| Internal tools, no regulated data | Low | Remains Low |
|
|
139
139
|
| User-facing features, API changes, PII handling | Medium | Remains Medium |
|
|
140
140
|
| Auth, payments, RBAC, crypto, data validation | High | Remains High |
|
|
@@ -145,7 +145,7 @@ AI involvement is a factor in risk classification:
|
|
|
145
145
|
**Review scope by risk level:**
|
|
146
146
|
|
|
147
147
|
| Risk Level | Reviewer | Focus |
|
|
148
|
-
|
|
148
|
+
| --- | --- | --- |
|
|
149
149
|
| Low | Any team member with domain knowledge | Functional correctness, obvious security issues |
|
|
150
150
|
| Medium | Developer experienced in affected area | Above + security implications, dependency validation |
|
|
151
151
|
| High | Senior developer + security-aware review | Above + independent verification, threat modeling |
|
|
@@ -174,7 +174,7 @@ Incremental AI-assisted edits follow standard testing gates.
|
|
|
174
174
|
### AI Documentation Requirements
|
|
175
175
|
|
|
176
176
|
| Risk Level | Commit Tag | Evidence | Prompts |
|
|
177
|
-
|
|
177
|
+
| --- | --- | --- | --- |
|
|
178
178
|
| Low | `Co-Authored-By` tag | Not required | Not required |
|
|
179
179
|
| Medium | `Co-Authored-By` tag | Summary of generation | Summary of prompts |
|
|
180
180
|
| High | `Co-Authored-By` tag | Detailed AI record | Detailed prompts and outputs |
|
|
@@ -196,7 +196,7 @@ Risk level is determined at planning time for each requirement:
|
|
|
196
196
|
### Testing Depth by Risk Level
|
|
197
197
|
|
|
198
198
|
| Activity | Low | Medium | High |
|
|
199
|
-
|
|
199
|
+
| --- | --- | --- | --- |
|
|
200
200
|
| Unit tests | Required | Required | Required |
|
|
201
201
|
| Integration tests | As applicable | Required | Required |
|
|
202
202
|
| E2E tests | Critical paths | Full coverage | Full coverage |
|
|
@@ -312,7 +312,7 @@ Tracked requirements include: `Ref: REQ-XXX`
|
|
|
312
312
|
### Required Artifact Types
|
|
313
313
|
|
|
314
314
|
| Category | Artifacts |
|
|
315
|
-
|
|
315
|
+
| --- | --- |
|
|
316
316
|
| Planning | Test Policy, Test Strategy, Project Test Plans |
|
|
317
317
|
| Specification | BDD feature files, test case specifications, security scenarios |
|
|
318
318
|
| Execution | Test logs, CI/CD logs, SAST/SCA reports, AI use records, defect reports |
|
|
@@ -323,7 +323,7 @@ Tracked requirements include: `Ref: REQ-XXX`
|
|
|
323
323
|
## Agile Artifact Mapping
|
|
324
324
|
|
|
325
325
|
| ISO Artifact | Metasession Implementation |
|
|
326
|
-
|
|
326
|
+
| --- | --- |
|
|
327
327
|
| Test Policy | This document hierarchy (Policy + Strategy + Architecture) |
|
|
328
328
|
| Requirements Specification | Product backlog with acceptance criteria |
|
|
329
329
|
| Test Plan | Project-specific Test Plan + sprint planning |
|
|
@@ -363,7 +363,7 @@ Sprint retrospectives, quarterly metric reviews, and incident lessons learned fe
|
|
|
363
363
|
## Document Control
|
|
364
364
|
|
|
365
365
|
| Version | Date | Author | Changes |
|
|
366
|
-
|
|
366
|
+
| --- | --- | --- | --- |
|
|
367
367
|
| 1.0 | January 2026 | QA Team | Initial creation |
|
|
368
368
|
| 2.0 | March 2026 | QA Team | Added AI methodology, security methodology, V&V |
|
|
369
369
|
| 3.0 | March 2026 | QA Team | Clean boundary split — moved specific tools, code patterns, CI config to Test Architecture. Strategy now owns methodology only. |
|
|
@@ -54,7 +54,7 @@ risk_class: "REPLACE — minimal | limited | high | unacceptable"
|
|
|
54
54
|
Add one row per AI tool / model / API. Delete this template row before your first audit.
|
|
55
55
|
|
|
56
56
|
| Tool / model | Vendor | Use case | Inputs (data classes) | Outputs | Risk class | Provider docs |
|
|
57
|
-
|
|
57
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
58
58
|
| REPLACE — e.g. GPT-4o | OpenAI | Code suggestion in IDE | Source code, no PII | Code suggestions | Limited | https://… |
|
|
59
59
|
| REPLACE | | | | | | |
|
|
60
60
|
|
|
@@ -91,7 +91,7 @@ Add one row per AI tool / model / API. Delete this template row before your firs
|
|
|
91
91
|
## 9. Sign-off
|
|
92
92
|
|
|
93
93
|
| Role | Name | Date |
|
|
94
|
-
|
|
94
|
+
| --- | --- | --- |
|
|
95
95
|
| Provider responsible | REPLACE | REPLACE |
|
|
96
96
|
| AI compliance reviewer | REPLACE | REPLACE |
|
|
97
97
|
|
|
@@ -104,5 +104,5 @@ Add one row per AI tool / model / API. Delete this template row before your firs
|
|
|
104
104
|
## Review log
|
|
105
105
|
|
|
106
106
|
| Date | Reviewer | Changes |
|
|
107
|
-
|
|
107
|
+
| --- | --- | --- |
|
|
108
108
|
| REPLACE — YYYY-MM-DD | REPLACE | Initial AI disclosure authored from starter template |
|
|
@@ -57,7 +57,7 @@ risk_level: "REPLACE — low | medium | high"
|
|
|
57
57
|
For each risk, populate one row. Add or remove rows to fit your assessment.
|
|
58
58
|
|
|
59
59
|
| # | Risk | Likelihood (1–3) | Severity (1–3) | Inherent risk | Existing controls | Residual risk | Acceptable? |
|
|
60
|
-
|
|
60
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
61
61
|
| 1 | REPLACE — e.g. unauthorised access to user accounts | REPLACE | REPLACE | REPLACE | REPLACE — MFA, RBAC, encryption-at-rest | REPLACE | REPLACE Y/N |
|
|
62
62
|
| 2 | REPLACE | | | | | | |
|
|
63
63
|
|
|
@@ -76,7 +76,7 @@ For each risk, populate one row. Add or remove rows to fit your assessment.
|
|
|
76
76
|
## 6. Sign-off
|
|
77
77
|
|
|
78
78
|
| Role | Name | Date | Decision |
|
|
79
|
-
|
|
79
|
+
| --- | --- | --- | --- |
|
|
80
80
|
| Controller | REPLACE | REPLACE | REPLACE — approved / rejected / conditional |
|
|
81
81
|
| DPO | REPLACE | REPLACE | REPLACE |
|
|
82
82
|
| Technical lead | REPLACE | REPLACE | REPLACE |
|
|
@@ -90,5 +90,5 @@ For each risk, populate one row. Add or remove rows to fit your assessment.
|
|
|
90
90
|
## Review log
|
|
91
91
|
|
|
92
92
|
| Date | Reviewer | Changes |
|
|
93
|
-
|
|
93
|
+
| --- | --- | --- |
|
|
94
94
|
| REPLACE — YYYY-MM-DD | REPLACE | Initial DPIA authored from starter template |
|
|
@@ -72,7 +72,7 @@ If none of the conditional boxes apply, this is still a valid incident_report
|
|
|
72
72
|
## 2. Personal data scope (GDPR triage)
|
|
73
73
|
|
|
74
74
|
| Question | Answer |
|
|
75
|
-
|
|
75
|
+
| --- | --- |
|
|
76
76
|
| Did the incident involve personal data? | REPLACE — Y / N |
|
|
77
77
|
| If Y: estimated number of data subjects affected | REPLACE |
|
|
78
78
|
| If Y: categories of personal data involved | REPLACE |
|
|
@@ -84,7 +84,7 @@ If none of the conditional boxes apply, this is still a valid incident_report
|
|
|
84
84
|
## 3. Timeline
|
|
85
85
|
|
|
86
86
|
| Time (UTC) | Event |
|
|
87
|
-
|
|
87
|
+
| --- | --- |
|
|
88
88
|
| REPLACE — ISO-8601 | REPLACE — first signal observed |
|
|
89
89
|
| REPLACE | REPLACE — detection escalated to on-call |
|
|
90
90
|
| REPLACE | REPLACE — incident channel opened, IC assigned |
|
|
@@ -131,7 +131,7 @@ If none of the conditional boxes apply, this is still a valid incident_report
|
|
|
131
131
|
## 9. Sign-off
|
|
132
132
|
|
|
133
133
|
| Role | Name | Date |
|
|
134
|
-
|
|
134
|
+
| --- | --- | --- |
|
|
135
135
|
| Incident Commander | REPLACE | REPLACE |
|
|
136
136
|
| Engineering lead | REPLACE | REPLACE |
|
|
137
137
|
| DPO (if personal data involved) | REPLACE | REPLACE |
|
|
@@ -29,7 +29,7 @@ No incidents or defects were discovered during the test cycle for release `<vers
|
|
|
29
29
|
## Sign-off
|
|
30
30
|
|
|
31
31
|
| Role | Name | Date |
|
|
32
|
-
|
|
32
|
+
| --- | --- | --- |
|
|
33
33
|
| Test lead | REPLACE — assign | REPLACE |
|
|
34
34
|
| Engineering lead | REPLACE — assign | REPLACE |
|
|
35
35
|
|
|
@@ -52,7 +52,7 @@ The 60% of this doc that the operator must fill (auto-generated sections at the
|
|
|
52
52
|
## 2. Activity summary (CI-derived, auto-fill when WS3 lands)
|
|
53
53
|
|
|
54
54
|
| Metric | Value | Notes |
|
|
55
|
-
|
|
55
|
+
| --- | --- | --- |
|
|
56
56
|
| Releases shipped this period | REPLACE | Tracked + housekeeping combined |
|
|
57
57
|
| Tracked releases (REQ-XXX) | REPLACE | |
|
|
58
58
|
| Quality-gate pass rate | REPLACE — % | % of CI runs where every gate passed |
|
|
@@ -107,14 +107,14 @@ For each control area, document evidence + reviewer judgement.
|
|
|
107
107
|
## 5. Findings and follow-up actions
|
|
108
108
|
|
|
109
109
|
| # | Finding | Severity | Owner | Due | Issue link |
|
|
110
|
-
|
|
110
|
+
| --- | --- | --- | --- | --- | --- |
|
|
111
111
|
| 1 | REPLACE | REPLACE | REPLACE | REPLACE | REPLACE |
|
|
112
112
|
| 2 | REPLACE | | | | |
|
|
113
113
|
|
|
114
114
|
## 6. Sign-off
|
|
115
115
|
|
|
116
116
|
| Role | Name | Date |
|
|
117
|
-
|
|
117
|
+
| --- | --- | --- |
|
|
118
118
|
| Reviewer | REPLACE | REPLACE |
|
|
119
119
|
| Approver (dual-actor) | REPLACE | REPLACE |
|
|
120
120
|
| Decision | REPLACE — controls effective / partially effective / not effective |
|
|
@@ -25,7 +25,7 @@ review_cadence_days: 90
|
|
|
25
25
|
## Scoring matrix
|
|
26
26
|
|
|
27
27
|
| Inherent + Residual rating | Likelihood (frequency / ease-of-exploit) | Impact (worst-case if realised) |
|
|
28
|
-
|
|
28
|
+
| --- | --- | --- |
|
|
29
29
|
| **Low** | Unlikely in a typical year; requires unusual access or coincidence | Recoverable inconvenience; no regulatory exposure |
|
|
30
30
|
| **Medium** | Plausible within a year; standard skill or access | Recoverable but disruptive; possible reportable incident |
|
|
31
31
|
| **High** | Probable within months; trivial to trigger | Material loss; regulatory reporting required |
|
|
@@ -50,7 +50,7 @@ For each distinct processing activity your project performs, add one section bel
|
|
|
50
50
|
### Activity 1 — REPLACE (e.g. "User authentication and session management")
|
|
51
51
|
|
|
52
52
|
| Field | Value |
|
|
53
|
-
|
|
53
|
+
| --- | --- |
|
|
54
54
|
| **Purpose(s) of processing** | REPLACE — why you process this data; lawful basis (Art. 6) |
|
|
55
55
|
| **Categories of data subjects** | REPLACE — e.g. customers, employees, prospects |
|
|
56
56
|
| **Categories of personal data** | REPLACE — e.g. name, email, IP address, hashed password |
|
|
@@ -73,5 +73,5 @@ For each distinct processing activity your project performs, add one section bel
|
|
|
73
73
|
## Review log
|
|
74
74
|
|
|
75
75
|
| Date | Reviewer | Changes |
|
|
76
|
-
|
|
76
|
+
| --- | --- | --- |
|
|
77
77
|
| REPLACE — YYYY-MM-DD | REPLACE | Initial ROPA authored from starter template |
|
|
@@ -27,7 +27,7 @@ If `devaudit doctor` and `devaudit status .` are both green, you can skip `devau
|
|
|
27
27
|
When you `git clone`, you've already got everything the framework synced into the project on first install:
|
|
28
28
|
|
|
29
29
|
| Path | What it is | Who owns it |
|
|
30
|
-
|
|
30
|
+
| --- | --- | --- |
|
|
31
31
|
| `sdlc-config.json` | Project-wide config (stack, host, slug, runtime, UAT, approval mode, e2e knobs, …) | Team — committed by the operator |
|
|
32
32
|
| `SDLC/*.md` | Stage walkthroughs (0-project-setup, 1-plan-requirement, …) — synced from DevAudit-Installer | Team — refreshed by `devaudit update` |
|
|
33
33
|
| `compliance/RTM.md`, `compliance/risk-register.md`, … | Compliance artefacts | Team — appended by tracked work |
|
|
@@ -149,7 +149,7 @@ You can skip this step entirely if the synced templates from `git clone` are alr
|
|
|
149
149
|
Two distinct credentials exist; conflating them is what causes the silent-CI-token rotation bug this guide exists to prevent.
|
|
150
150
|
|
|
151
151
|
| Credential | Format | Where it lives | Who it identifies | Used by |
|
|
152
|
-
|
|
152
|
+
| --- | --- | --- | --- | --- |
|
|
153
153
|
| **Personal PAT** | `mctok_…` | `~/.config/devaudit/auth.json` (per developer) | You (the user) | Your local CLI commands |
|
|
154
154
|
| **Project API key** | `dak_…` | Repo secret `DEVAUDIT_API_KEY` | The project | CI's `devaudit push` calls |
|
|
155
155
|
| **Operator's PAT** | `mctok_…` | Repo secret `DEVAUDIT_USER_TOKEN` | The operator (singular) | CI's DevAudit portal calls that need human attribution (release submit / approval flows) |
|
|
@@ -181,7 +181,7 @@ A second dev running it silently rotates the team's `DEVAUDIT_USER_TOKEN` repo s
|
|
|
181
181
|
The synced CI gates expect a specific environment. Here's what you need locally if you're running them against the same project:
|
|
182
182
|
|
|
183
183
|
| Surface | CI | Local (your machine) |
|
|
184
|
-
|
|
184
|
+
| --- | --- | --- |
|
|
185
185
|
| Personal identity | `secrets.DEVAUDIT_USER_TOKEN` (operator's, portal-only) | `~/.config/devaudit/auth.json` (yours) — `devaudit auth login` |
|
|
186
186
|
| Project API key | `secrets.DEVAUDIT_API_KEY` | Usually unset locally — only needed if you're testing `devaudit push` against the live portal; ask the operator if you need to debug it |
|
|
187
187
|
| Portal URL | `vars.DEVAUDIT_BASE_URL` | `~/.config/devaudit/auth.json` (set by `auth login`) or `$DEVAUDIT_BASE_URL` env |
|
|
@@ -196,7 +196,7 @@ For everything else (DB migrations, env files, `npm install`), the consumer's `R
|
|
|
196
196
|
## Troubleshooting
|
|
197
197
|
|
|
198
198
|
| Symptom | Cause | Fix |
|
|
199
|
-
|
|
199
|
+
| --- | --- | --- |
|
|
200
200
|
| `devaudit status` reports missing framework files | Local clone is behind `main`, or the operator hasn't run a recent `devaudit update` | `git pull origin develop` first; if still missing, ping the operator |
|
|
201
201
|
| `devaudit auth status` says "token rejected" | PAT expired, was revoked, or you typed it wrong | Issue a fresh PAT at `/settings/tokens` and re-run `devaudit auth login` |
|
|
202
202
|
| `devaudit doctor` flags missing tool | The matching binary isn't on PATH | Install via your package manager (`brew install gh`, `apt install jq`, etc.) |
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# check-host-deployment.sh — wait for a terminal GitHub deployment status for a
|
|
3
|
+
# merged production SHA before the release is promoted on the portal.
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
REPO=""
|
|
8
|
+
SHA=""
|
|
9
|
+
MAX_ATTEMPTS=30
|
|
10
|
+
POLL_SECONDS=10
|
|
11
|
+
|
|
12
|
+
for arg in "$@"; do
|
|
13
|
+
case "$arg" in
|
|
14
|
+
--repo=*) REPO="${arg#*=}" ;;
|
|
15
|
+
--sha=*) SHA="${arg#*=}" ;;
|
|
16
|
+
--max-attempts=*) MAX_ATTEMPTS="${arg#*=}" ;;
|
|
17
|
+
--poll-seconds=*) POLL_SECONDS="${arg#*=}" ;;
|
|
18
|
+
*)
|
|
19
|
+
echo "Unknown argument: $arg" >&2
|
|
20
|
+
exit 2
|
|
21
|
+
;;
|
|
22
|
+
esac
|
|
23
|
+
done
|
|
24
|
+
|
|
25
|
+
if [ -z "$REPO" ] || [ -z "$SHA" ]; then
|
|
26
|
+
echo "Usage: check-host-deployment.sh --repo=<owner/name> --sha=<sha> [--max-attempts=N] [--poll-seconds=N]" >&2
|
|
27
|
+
exit 2
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
select_deployment_id() {
|
|
31
|
+
python3 - <<'PY'
|
|
32
|
+
import json
|
|
33
|
+
import os
|
|
34
|
+
|
|
35
|
+
deployments = json.loads(os.environ.get("DEPLOYMENTS_JSON", "[]"))
|
|
36
|
+
preferred = None
|
|
37
|
+
fallback = None
|
|
38
|
+
for deployment in deployments:
|
|
39
|
+
env = str(deployment.get("environment") or "").lower()
|
|
40
|
+
dep_id = deployment.get("id")
|
|
41
|
+
if dep_id is None:
|
|
42
|
+
continue
|
|
43
|
+
if fallback is None:
|
|
44
|
+
fallback = dep_id
|
|
45
|
+
if env in {"production", "prod"}:
|
|
46
|
+
preferred = dep_id
|
|
47
|
+
break
|
|
48
|
+
if preferred is not None:
|
|
49
|
+
print(preferred)
|
|
50
|
+
elif fallback is not None:
|
|
51
|
+
print(fallback)
|
|
52
|
+
PY
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
extract_status_state() {
|
|
56
|
+
python3 - <<'PY'
|
|
57
|
+
import json
|
|
58
|
+
import os
|
|
59
|
+
|
|
60
|
+
statuses = json.loads(os.environ.get("STATUSES_JSON", "[]"))
|
|
61
|
+
if statuses:
|
|
62
|
+
latest = statuses[0]
|
|
63
|
+
print(str(latest.get("state") or "").lower())
|
|
64
|
+
PY
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for ATTEMPT in $(seq 1 "$MAX_ATTEMPTS"); do
|
|
68
|
+
DEPLOYMENTS_JSON=$(gh api "/repos/${REPO}/deployments?sha=${SHA}&per_page=20")
|
|
69
|
+
DEPLOYMENT_ID=$(DEPLOYMENTS_JSON="$DEPLOYMENTS_JSON" select_deployment_id)
|
|
70
|
+
|
|
71
|
+
if [ -z "$DEPLOYMENT_ID" ]; then
|
|
72
|
+
echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: no GitHub deployment found yet for ${SHA}."
|
|
73
|
+
if [ "$ATTEMPT" -lt "$MAX_ATTEMPTS" ]; then
|
|
74
|
+
sleep "$POLL_SECONDS"
|
|
75
|
+
continue
|
|
76
|
+
fi
|
|
77
|
+
echo "::error::No GitHub deployment was published for ${SHA} after ${MAX_ATTEMPTS} attempts."
|
|
78
|
+
exit 1
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
STATUSES_JSON=$(gh api "/repos/${REPO}/deployments/${DEPLOYMENT_ID}/statuses?per_page=20")
|
|
82
|
+
DEPLOY_STATE=$(STATUSES_JSON="$STATUSES_JSON" extract_status_state)
|
|
83
|
+
|
|
84
|
+
case "$DEPLOY_STATE" in
|
|
85
|
+
success)
|
|
86
|
+
echo "GitHub deployment ${DEPLOYMENT_ID} for ${SHA} reached terminal success."
|
|
87
|
+
exit 0
|
|
88
|
+
;;
|
|
89
|
+
failure|error)
|
|
90
|
+
echo "::error::GitHub deployment ${DEPLOYMENT_ID} for ${SHA} reached terminal failure (${DEPLOY_STATE})."
|
|
91
|
+
exit 1
|
|
92
|
+
;;
|
|
93
|
+
queued|in_progress|pending|"")
|
|
94
|
+
echo "Attempt ${ATTEMPT}/${MAX_ATTEMPTS}: deployment ${DEPLOYMENT_ID} for ${SHA} is ${DEPLOY_STATE:-pending}."
|
|
95
|
+
if [ "$ATTEMPT" -lt "$MAX_ATTEMPTS" ]; then
|
|
96
|
+
sleep "$POLL_SECONDS"
|
|
97
|
+
continue
|
|
98
|
+
fi
|
|
99
|
+
echo "::error::GitHub deployment ${DEPLOYMENT_ID} for ${SHA} did not reach a terminal success state after ${MAX_ATTEMPTS} attempts."
|
|
100
|
+
exit 1
|
|
101
|
+
;;
|
|
102
|
+
*)
|
|
103
|
+
echo "::error::Deployment ${DEPLOYMENT_ID} for ${SHA} returned unexpected state '${DEPLOY_STATE}'."
|
|
104
|
+
exit 1
|
|
105
|
+
;;
|
|
106
|
+
esac
|
|
107
|
+
done
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# check-host-deployment.test.sh — Tests for host deployment wait helper.
|
|
3
|
+
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
HELPER="$SCRIPT_DIR/check-host-deployment.sh"
|
|
8
|
+
|
|
9
|
+
PASS=0
|
|
10
|
+
FAIL=0
|
|
11
|
+
WORK=$(mktemp -d)
|
|
12
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
13
|
+
|
|
14
|
+
assert_exit() {
|
|
15
|
+
local name="$1" expected="$2" actual="$3"
|
|
16
|
+
if [ "$expected" = "$actual" ]; then
|
|
17
|
+
echo " ✓ $name"
|
|
18
|
+
PASS=$((PASS + 1))
|
|
19
|
+
else
|
|
20
|
+
echo " ✗ $name"
|
|
21
|
+
echo " Expected exit: $expected"
|
|
22
|
+
echo " Actual exit: $actual"
|
|
23
|
+
FAIL=$((FAIL + 1))
|
|
24
|
+
fi
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
assert_grep() {
|
|
28
|
+
local name="$1" pattern="$2" file="$3"
|
|
29
|
+
if grep -Eq "$pattern" "$file"; then
|
|
30
|
+
echo " ✓ $name"
|
|
31
|
+
PASS=$((PASS + 1))
|
|
32
|
+
else
|
|
33
|
+
echo " ✗ $name"
|
|
34
|
+
echo " Missing pattern: $pattern"
|
|
35
|
+
FAIL=$((FAIL + 1))
|
|
36
|
+
fi
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
write_mock_gh() {
|
|
40
|
+
local dir="$1"
|
|
41
|
+
mkdir -p "$dir/mock-bin"
|
|
42
|
+
cat > "$dir/mock-bin/gh" <<'EOF'
|
|
43
|
+
#!/usr/bin/env bash
|
|
44
|
+
set -euo pipefail
|
|
45
|
+
if [ "$1" != "api" ]; then
|
|
46
|
+
echo "unexpected gh invocation: $*" >&2
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
case "$2" in
|
|
50
|
+
*/deployments\?sha=*)
|
|
51
|
+
printf '%s' "${MOCK_DEPLOYMENTS_JSON}"
|
|
52
|
+
;;
|
|
53
|
+
*/statuses\?per_page=20)
|
|
54
|
+
printf '%s' "${MOCK_STATUSES_JSON}"
|
|
55
|
+
;;
|
|
56
|
+
*)
|
|
57
|
+
echo "unexpected gh api path: $2" >&2
|
|
58
|
+
exit 1
|
|
59
|
+
;;
|
|
60
|
+
esac
|
|
61
|
+
EOF
|
|
62
|
+
chmod +x "$dir/mock-bin/gh"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
run_check() {
|
|
66
|
+
local dir="$1" out="$2"
|
|
67
|
+
shift 2
|
|
68
|
+
(
|
|
69
|
+
cd "$dir"
|
|
70
|
+
PATH="$dir/mock-bin:$PATH" "$@" bash "$HELPER" --repo=metasession-dev/example --sha=abc123 --max-attempts=1 --poll-seconds=0
|
|
71
|
+
) >"$out" 2>&1
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
echo "=== check-host-deployment.sh tests ==="
|
|
75
|
+
echo ""
|
|
76
|
+
|
|
77
|
+
write_mock_gh "$WORK"
|
|
78
|
+
|
|
79
|
+
echo "--- Test 1: success status passes ---"
|
|
80
|
+
OUT="$WORK/test1.out"
|
|
81
|
+
if run_check "$WORK" "$OUT" env \
|
|
82
|
+
MOCK_DEPLOYMENTS_JSON='[{"id":5453707224,"environment":"production"}]' \
|
|
83
|
+
MOCK_STATUSES_JSON='[{"state":"success"}]'; then
|
|
84
|
+
CODE=0
|
|
85
|
+
else
|
|
86
|
+
CODE=$?
|
|
87
|
+
fi
|
|
88
|
+
assert_exit "success deployment passes" 0 "$CODE"
|
|
89
|
+
assert_grep "success message emitted" 'terminal success' "$OUT"
|
|
90
|
+
echo ""
|
|
91
|
+
|
|
92
|
+
echo "--- Test 2: failure status fails ---"
|
|
93
|
+
OUT="$WORK/test2.out"
|
|
94
|
+
if run_check "$WORK" "$OUT" env \
|
|
95
|
+
MOCK_DEPLOYMENTS_JSON='[{"id":5453707224,"environment":"production"}]' \
|
|
96
|
+
MOCK_STATUSES_JSON='[{"state":"failure"}]'; then
|
|
97
|
+
CODE=0
|
|
98
|
+
else
|
|
99
|
+
CODE=$?
|
|
100
|
+
fi
|
|
101
|
+
assert_exit "failed deployment exits 1" 1 "$CODE"
|
|
102
|
+
assert_grep "failure message emitted" 'terminal failure' "$OUT"
|
|
103
|
+
echo ""
|
|
104
|
+
|
|
105
|
+
echo "--- Test 3: missing deployment fails closed ---"
|
|
106
|
+
OUT="$WORK/test3.out"
|
|
107
|
+
if run_check "$WORK" "$OUT" env \
|
|
108
|
+
MOCK_DEPLOYMENTS_JSON='[]' \
|
|
109
|
+
MOCK_STATUSES_JSON='[]'; then
|
|
110
|
+
CODE=0
|
|
111
|
+
else
|
|
112
|
+
CODE=$?
|
|
113
|
+
fi
|
|
114
|
+
assert_exit "missing deployment exits 1" 1 "$CODE"
|
|
115
|
+
assert_grep "missing deployment message emitted" 'No GitHub deployment was published' "$OUT"
|
|
116
|
+
echo ""
|
|
117
|
+
|
|
118
|
+
echo "=== check-host-deployment.test.sh: $PASS passed, $FAIL failed ==="
|
|
119
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# check-release-pr-scope.sh — fail closed when a release PR's declared scope
|
|
3
|
+
# drifts from the release currently derived from the integration branch head.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# PR_TITLE="..." PR_BODY="..." HEAD_REF="develop" \
|
|
7
|
+
# bash scripts/check-release-pr-scope.sh
|
|
8
|
+
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
is_hotfix_branch() {
|
|
12
|
+
case "${1:-}" in
|
|
13
|
+
hotfix/*|fix/hotfix-*)
|
|
14
|
+
return 0
|
|
15
|
+
;;
|
|
16
|
+
*)
|
|
17
|
+
return 1
|
|
18
|
+
;;
|
|
19
|
+
esac
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
extract_declared_scope() {
|
|
23
|
+
python3 - <<'PY'
|
|
24
|
+
import os
|
|
25
|
+
import re
|
|
26
|
+
|
|
27
|
+
title = os.environ.get("PR_TITLE", "")
|
|
28
|
+
body = os.environ.get("PR_BODY", "")
|
|
29
|
+
text = f"{title}\n{body}"
|
|
30
|
+
patterns = [
|
|
31
|
+
r"^[ \t\-*]*Release:\s*((?:REQ-\d+)|(?:v\d+\.\d+\.\d+)|(?:v\d{4}\.\d{2}\.\d{2}(?:\.\d+)?))\b",
|
|
32
|
+
r"\b(REQ-\d+)\b",
|
|
33
|
+
r"\b(v\d+\.\d+\.\d+)\b",
|
|
34
|
+
r"\b(v\d{4}\.\d{2}\.\d{2}(?:\.\d+)?)\b",
|
|
35
|
+
]
|
|
36
|
+
flags = re.MULTILINE
|
|
37
|
+
for pattern in patterns:
|
|
38
|
+
match = re.search(pattern, text, flags)
|
|
39
|
+
if match:
|
|
40
|
+
print(match.group(1))
|
|
41
|
+
break
|
|
42
|
+
PY
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
HEAD_REF="${HEAD_REF:-}"
|
|
46
|
+
if is_hotfix_branch "$HEAD_REF"; then
|
|
47
|
+
echo "Hotfix PR detected (${HEAD_REF}) — release scope integrity check skipped."
|
|
48
|
+
exit 0
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
if [ ! -x scripts/derive-release-version.sh ]; then
|
|
52
|
+
chmod +x scripts/derive-release-version.sh 2>/dev/null || true
|
|
53
|
+
fi
|
|
54
|
+
CURRENT_RELEASE=$(bash scripts/derive-release-version.sh)
|
|
55
|
+
if [ -z "$CURRENT_RELEASE" ]; then
|
|
56
|
+
echo "::error::Could not derive the effective release scope from the current branch head."
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
DECLARED_SCOPE=$(extract_declared_scope)
|
|
61
|
+
if [ -z "$DECLARED_SCOPE" ]; then
|
|
62
|
+
echo "::error::Release Scope Integrity: the PR title/body do not declare a release scope."
|
|
63
|
+
echo "::error::Derived effective scope: ${CURRENT_RELEASE}"
|
|
64
|
+
echo "::error::Update the PR metadata or regenerate the release PR from the current integration head."
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
if [ "$DECLARED_SCOPE" != "$CURRENT_RELEASE" ]; then
|
|
69
|
+
echo "::error::Release Scope Integrity mismatch."
|
|
70
|
+
echo "::error::Declared PR scope: ${DECLARED_SCOPE}"
|
|
71
|
+
echo "::error::Derived effective scope: ${CURRENT_RELEASE}"
|
|
72
|
+
echo "::error::Update the PR metadata or regenerate the release PR from the current integration head."
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
if [[ "$CURRENT_RELEASE" =~ ^REQ-[0-9]+$ ]]; then
|
|
77
|
+
if [ ! -x scripts/extract-release-metadata.sh ]; then
|
|
78
|
+
chmod +x scripts/extract-release-metadata.sh 2>/dev/null || true
|
|
79
|
+
fi
|
|
80
|
+
# shellcheck disable=SC1091
|
|
81
|
+
source scripts/extract-release-metadata.sh
|
|
82
|
+
extract_release_metadata "$CURRENT_RELEASE"
|
|
83
|
+
if printf '%s' "${RELEASE_SUMMARY:-}" | grep -q 'Bundled release context:'; then
|
|
84
|
+
if ! printf '%s\n%s\n' "${PR_TITLE:-}" "${PR_BODY:-}" | grep -q "BUNDLED-CHANGES-${CURRENT_RELEASE}.md"; then
|
|
85
|
+
echo "::error::Release Scope Integrity mismatch."
|
|
86
|
+
echo "::error::Declared PR scope: ${DECLARED_SCOPE}"
|
|
87
|
+
echo "::error::Derived effective scope: ${CURRENT_RELEASE} with bundled release context."
|
|
88
|
+
echo "::error::PR metadata is missing the bundled release context marker for BUNDLED-CHANGES-${CURRENT_RELEASE}.md."
|
|
89
|
+
echo "::error::Update the PR body or regenerate the release PR from the current integration head."
|
|
90
|
+
exit 1
|
|
91
|
+
fi
|
|
92
|
+
fi
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
echo "Release Scope Integrity verified for ${CURRENT_RELEASE}."
|