@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.
Files changed (68) hide show
  1. package/dist/index.js +94 -6
  2. package/dist/index.js.map +1 -1
  3. package/package.json +2 -2
  4. package/scripts/upload-evidence.sh +55 -1
  5. package/sdlc/ai-rules/SDLC_RULES.md +27 -7
  6. package/sdlc/files/_common/0-project-setup.md +13 -7
  7. package/sdlc/files/_common/Periodic_Security_Review_Schedule.md +5 -5
  8. package/sdlc/files/_common/README_TEMPLATE.md +8 -8
  9. package/sdlc/files/_common/Test_Architecture.md +15 -15
  10. package/sdlc/files/_common/Test_Plan_TEMPLATE.md +13 -13
  11. package/sdlc/files/_common/Test_Policy.md +2 -2
  12. package/sdlc/files/_common/Test_Strategy.md +10 -10
  13. package/sdlc/files/_common/governance/ai-disclosure.md.template +3 -3
  14. package/sdlc/files/_common/governance/dpia.md.template +3 -3
  15. package/sdlc/files/_common/governance/incident-report.md.template +3 -3
  16. package/sdlc/files/_common/governance/nil-incident-report.md.template +1 -1
  17. package/sdlc/files/_common/governance/periodic-review.md.template +3 -3
  18. package/sdlc/files/_common/governance/risk-register.md.template +1 -1
  19. package/sdlc/files/_common/governance/ropa.md.template +2 -2
  20. package/sdlc/files/_common/joining-an-existing-project.md +4 -4
  21. package/sdlc/files/_common/scripts/check-host-deployment.sh +107 -0
  22. package/sdlc/files/_common/scripts/check-host-deployment.test.sh +119 -0
  23. package/sdlc/files/_common/scripts/check-release-pr-scope.sh +95 -0
  24. package/sdlc/files/_common/scripts/check-release-pr-scope.test.sh +137 -0
  25. package/sdlc/files/_common/scripts/close-out-release.sh +175 -54
  26. package/sdlc/files/_common/scripts/close-out-release.test.sh +90 -0
  27. package/sdlc/files/_common/scripts/extract-release-metadata.sh +65 -0
  28. package/sdlc/files/_common/scripts/extract-release-metadata.test.sh +38 -0
  29. package/sdlc/files/_common/scripts/generate-bundled-changes.sh +307 -58
  30. package/sdlc/files/_common/scripts/generate-bundled-changes.test.sh +128 -78
  31. package/sdlc/files/_common/scripts/generate-housekeeping-release-ticket.sh +18 -9
  32. package/sdlc/files/_common/scripts/generate-housekeeping-release-ticket.test.sh +66 -0
  33. package/sdlc/files/_common/scripts/generate-security-summary.sh +14 -5
  34. package/sdlc/files/_common/scripts/generate-security-summary.test.sh +57 -0
  35. package/sdlc/files/_common/scripts/prepare-release-pr.sh +44 -4
  36. package/sdlc/files/_common/scripts/render-test-cycles.sh +227 -0
  37. package/sdlc/files/_common/scripts/render-test-cycles.test.sh +107 -0
  38. package/sdlc/files/_common/scripts/report-test-cycle.sh +344 -0
  39. package/sdlc/files/_common/scripts/submit-bundle-manifest.sh +100 -0
  40. package/sdlc/files/_common/scripts/submit-bundle-manifest.test.sh +138 -0
  41. package/sdlc/files/_common/scripts/upload-evidence.sh +55 -1
  42. package/sdlc/files/_common/scripts/upload-evidence.test.sh +119 -0
  43. package/sdlc/files/_common/scripts/validate-compliance-artifacts.sh +169 -2
  44. package/sdlc/files/_common/scripts/validate-compliance-artifacts.test.sh +169 -0
  45. package/sdlc/files/_common/scripts/validate-test-summary.test.sh +4 -4
  46. package/sdlc/files/_common/skills/adr-author/SKILL.md +2 -2
  47. package/sdlc/files/_common/skills/e2e-test-engineer/SKILL.md +2 -2
  48. package/sdlc/files/_common/skills/e2e-test-engineer/references/bootstrap.md +5 -5
  49. package/sdlc/files/_common/skills/governance-doc-author/references/incident-classification.md +1 -1
  50. package/sdlc/files/_common/skills/requirements-aligner/SKILL.md +4 -4
  51. package/sdlc/files/_common/skills/sdlc-implementer/SKILL.md +57 -3
  52. package/sdlc/files/_common/skills/sdlc-implementer/references/change-request-loop.md +1 -1
  53. package/sdlc/files/_common/skills/sdlc-implementer/references/compliance-constraints.md +7 -14
  54. package/sdlc/files/ci/check-release-approval.yml.template +18 -28
  55. package/sdlc/files/ci/ci.yml.template +95 -35
  56. package/sdlc/files/ci/close-out-release.yml.template +7 -4
  57. package/sdlc/files/ci/compliance-evidence.yml.template +234 -49
  58. package/sdlc/files/ci/feature-e2e.yml.template +84 -1
  59. package/sdlc/files/ci/post-deploy-prod.yml.template +59 -4
  60. package/sdlc/files/ci/python/ci.yml.template +60 -12
  61. package/sdlc/files/ci/quality-gates-provenance.yml.template +123 -28
  62. package/sdlc/package.json +1 -1
  63. package/sdlc/src/bin/devaudit-sdlc.js +44 -2
  64. package/sdlc/src/blueprints/1-plan-requirement.raw.md +6 -1
  65. package/sdlc/src/blueprints/2-implement-and-test.raw.md +1 -1
  66. package/sdlc/src/blueprints/3-compile-evidence.raw.md +102 -20
  67. package/sdlc/src/blueprints/4-submit-for-review.raw.md +3 -3
  68. 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}."