@metasession.co/devaudit-cli 0.1.11 → 0.1.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metasession.co/devaudit-cli",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "DevAudit CLI — installs, syncs, and operates the Metasession SDLC across consumer projects.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@clack/prompts": "^0.8.2",
36
- "@metasession.co/devaudit-plugin-sdk": "^0.1.11",
36
+ "@metasession.co/devaudit-plugin-sdk": "^0.1.12",
37
37
  "commander": "^12.1.0",
38
38
  "consola": "^3.2.3",
39
39
  "env-paths": "^3.0.0",
@@ -6,16 +6,27 @@
6
6
  # Production verification is READ-ONLY.
7
7
  # No E2E tests, no database operations, no API mutations.
8
8
  #
9
+ # Promotes EVERY in-scope release (each requirement with a pending release
10
+ # ticket), not just the first REQ found — a develop→main PR that bundles
11
+ # several requirements must advance all of them to the terminal status, else
12
+ # the requirements not picked first stay stuck at uat_approved with no
13
+ # production evidence. `workflow_dispatch` allows re-running / catching up a
14
+ # single release via the `release` input.
15
+ #
9
16
  # In sdlc-v1.22.0+ the terminal release status is configurable via
10
17
  # sdlc-config.json `production_review.terminal_status`:
11
18
  # - "prod_review" (default, Option A) — stop at prod_review; human in the
12
- # portal clicks "Approve Production" then "Mark as Released" for an
13
- # explicit audit trail. Closes #138.
19
+ # portal clicks "Approve Production" then "Mark as Released".
14
20
  # - "released" (Option B) — preserves v1.21.x auto-release behaviour.
15
21
 
16
22
  name: Post-Deploy Production Evidence
17
23
 
18
24
  on:
25
+ workflow_dispatch:
26
+ inputs:
27
+ release:
28
+ description: 'Optional REQ-XXX / version to promote (blank = all in-scope from pending release tickets).'
29
+ required: false
19
30
  push:
20
31
  branches: [main]
21
32
 
@@ -30,11 +41,12 @@ jobs:
30
41
  PROJECT_SLUG: {{PROJECT_SLUG}}
31
42
  GIT_SHA: ${{ github.sha }}
32
43
  CI_RUN: ${{ github.run_id }}
44
+ RELEASE_INPUT: ${{ github.event.inputs.release }}
33
45
 
34
46
  steps:
35
47
  - uses: actions/checkout@v4
36
48
  with:
37
- fetch-depth: 0 # full history so the merged commits' REQ tags are readable
49
+ fetch-depth: 0 # full history so merged commits' REQ tags are readable
38
50
 
39
51
  - name: Resolve DevAudit base URL and post-deploy terminal status
40
52
  run: |
@@ -75,37 +87,31 @@ jobs:
75
87
  echo "DEVAUDIT_BASE_URL=${BASE%/}" >> "$GITHUB_ENV"
76
88
  echo "TERMINAL_STATUS=${TERMINAL_STATUS}" >> "$GITHUB_ENV"
77
89
 
78
- - name: Resolve current release
79
- id: release
90
+ - name: Resolve in-scope releases
80
91
  run: |
81
- # Resolve the release being PROMOTED the same REQ the dev/UAT
82
- # pipeline versioned (ci.yml / compliance-evidence.yml use
83
- # derive-release-version.sh → REQ-XXX). The merge commit itself
84
- # carries no REQ tag, so derive it from the commits merged into
85
- # this push ([REQ-XXX] subject tags / Ref: lines), and only fall
86
- # back to a bare date when the consumer uses date-versioned
87
- # releases. Without this, a REQ-versioned release never converges:
88
- # production evidence + the prod-review advance land on a phantom
89
- # date release while the real REQ release stays uat_approved.
90
- REQ=$(git log "${{ github.event.before }}..${{ github.sha }}" --format='%s%n%b' 2>/dev/null \
91
- | grep -oiE '\[REQ-[0-9]+\]|Ref:[[:space:]]*REQ-[0-9]+' \
92
- | grep -oiE 'REQ-[0-9]+' | head -1 | tr '[:lower:]' '[:upper:]' || true)
93
- if [ -n "$REQ" ]; then
94
- PREFIX="$REQ"
92
+ # The releases being PROMOTED are the requirements with a pending
93
+ # release ticket (the same set the dev/UAT pipeline versioned via
94
+ # derive-release-version.sh → REQ-XXX). A bundled develop→main PR
95
+ # carries several; promote ALL of them, not just the first. A manual
96
+ # dispatch can target one via the `release` input. Fall back to a
97
+ # bare date for date-versioned (ticketless) releases.
98
+ if [ -n "${RELEASE_INPUT}" ]; then
99
+ REQS="${RELEASE_INPUT}"
95
100
  else
96
- PREFIX="v$(date +%Y.%m.%d)"
97
- fi
98
- echo "Resolving release for version prefix: ${PREFIX}"
99
- RESP=$(curl -s -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
100
- "${BASE}/api/ci/releases/resolve?projectSlug=${PROJECT_SLUG}&versionPrefix=${PREFIX}")
101
- VERSION=$(echo "$RESP" | jq -r '.latest.version // empty')
102
- if [ -z "$VERSION" ]; then
103
- VERSION="${PREFIX}"
101
+ REQS=""
102
+ if [ -d compliance/pending-releases ]; then
103
+ for T in compliance/pending-releases/RELEASE-TICKET-REQ-*.md; do
104
+ [ -f "$T" ] || continue
105
+ REQS="${REQS} $(basename "$T" .md | sed 's/^RELEASE-TICKET-//')"
106
+ done
107
+ fi
108
+ REQS=$(echo ${REQS} | tr ' ' '\n' | sort -u | tr '\n' ' ' | sed 's/[[:space:]]*$//')
109
+ if [ -z "${REQS}" ]; then
110
+ REQS="v$(date +%Y.%m.%d)"
111
+ fi
104
112
  fi
105
- RELEASE_ID=$(echo "$RESP" | jq -r '.latest.id // empty')
106
- echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
107
- echo "release_id=${RELEASE_ID}" >> "$GITHUB_OUTPUT"
108
- echo "Release version: ${VERSION}"
113
+ echo "In-scope releases to promote: ${REQS}"
114
+ echo "REQS=${REQS}" >> "$GITHUB_ENV"
109
115
 
110
116
  - name: Wait for production deployment
111
117
  run: |
@@ -142,63 +148,57 @@ jobs:
142
148
  }
143
149
  RESULTS_EOF
144
150
 
145
- - name: Upload production evidence
146
- run: |
147
- chmod +x scripts/upload-evidence.sh 2>/dev/null || true
148
- VERSION="${{ steps.release.outputs.version }}"
149
- FLAGS="--release ${VERSION} --create-release-if-missing --environment production --category test_report"
150
- FLAGS="${FLAGS} --git-sha ${GIT_SHA} --ci-run-id ${CI_RUN} --branch main"
151
- if [ -f prod-smoke-results.json ]; then
152
- bash scripts/upload-evidence.sh \
153
- "${PROJECT_SLUG}" "_compliance-docs" "test_report" prod-smoke-results.json \
154
- ${FLAGS} || echo "Warning: Failed to upload smoke results"
155
- fi
156
-
157
- - name: Upload release ticket to production
151
+ - name: Promote in-scope releases (evidence + status)
158
152
  run: |
159
- # Submit-for-production-review requires a release ticket (release_artifact)
160
- # in the PRODUCTION environment — the dev/UAT pipeline only uploads it to
161
- # uat. Carry the promoted release's ticket forward to production so the
162
- # production release is self-contained and the prod-review gate passes.
163
153
  chmod +x scripts/upload-evidence.sh 2>/dev/null || true
164
- VERSION="${{ steps.release.outputs.version }}"
165
- FLAGS="--release ${VERSION} --create-release-if-missing --environment production --category release_artifact"
166
- FLAGS="${FLAGS} --git-sha ${GIT_SHA} --ci-run-id ${CI_RUN} --branch main"
167
- TICKET=""
168
- for DIR in compliance/pending-releases compliance/approved-releases; do
169
- if [ -f "${DIR}/RELEASE-TICKET-${VERSION}.md" ]; then
170
- TICKET="${DIR}/RELEASE-TICKET-${VERSION}.md"
171
- break
154
+ PROMOTED=0
155
+ for PREFIX in ${REQS}; do
156
+ echo "=== Promoting ${PREFIX} ==="
157
+ RESP=$(curl -s -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
158
+ "${BASE}/api/ci/releases/resolve?projectSlug=${PROJECT_SLUG}&versionPrefix=${PREFIX}")
159
+ VERSION=$(echo "$RESP" | jq -r '.latest.version // empty')
160
+ [ -z "$VERSION" ] && VERSION="${PREFIX}"
161
+ RELEASE_ID=$(echo "$RESP" | jq -r '.latest.id // empty')
162
+ # Production smoke evidence (whole-app health) attached to this release.
163
+ if [ -f prod-smoke-results.json ]; then
164
+ bash scripts/upload-evidence.sh \
165
+ "${PROJECT_SLUG}" "${VERSION}" test_report prod-smoke-results.json \
166
+ --release "${VERSION}" --create-release-if-missing --environment production \
167
+ --category test_report --git-sha "${GIT_SHA}" --ci-run-id "${CI_RUN}" --branch main \
168
+ || echo "Warning: smoke upload failed for ${VERSION}"
169
+ fi
170
+ # Carry the release ticket into the production environment so the
171
+ # prod-review gate is self-contained.
172
+ TICKET=""
173
+ for DIR in compliance/pending-releases compliance/approved-releases; do
174
+ if [ -f "${DIR}/RELEASE-TICKET-${VERSION}.md" ]; then
175
+ TICKET="${DIR}/RELEASE-TICKET-${VERSION}.md"; break
176
+ fi
177
+ done
178
+ if [ -n "$TICKET" ]; then
179
+ bash scripts/upload-evidence.sh \
180
+ "${PROJECT_SLUG}" "${VERSION}" compliance_document "$TICKET" \
181
+ --release "${VERSION}" --create-release-if-missing --environment production \
182
+ --category release_artifact --git-sha "${GIT_SHA}" --ci-run-id "${CI_RUN}" --branch main \
183
+ || echo "Warning: ticket upload failed for ${VERSION}"
184
+ else
185
+ echo "No RELEASE-TICKET-${VERSION}.md found — skipping ticket (date-versioned or archived)."
186
+ fi
187
+ # Advance status (idempotent — re-PATCHing an already-promoted release is a no-op).
188
+ if [ -n "$RELEASE_ID" ]; then
189
+ curl -s -o /dev/null -w " ${VERSION} status patch: HTTP %{http_code}\n" \
190
+ -X PATCH "${BASE}/api/ci/releases/${RELEASE_ID}" \
191
+ -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
192
+ -H "Content-Type: application/json" \
193
+ -d "{\"status\":\"${TERMINAL_STATUS}\"}"
194
+ echo " ${VERSION} → ${TERMINAL_STATUS}"
195
+ PROMOTED=$((PROMOTED + 1))
196
+ else
197
+ echo "::warning::No release_id resolved for ${PREFIX} — skipping status patch"
172
198
  fi
173
199
  done
174
- if [ -n "$TICKET" ]; then
175
- echo "Uploading release ticket to production: $TICKET"
176
- bash scripts/upload-evidence.sh \
177
- "${PROJECT_SLUG}" "_compliance-docs" compliance_document "$TICKET" \
178
- ${FLAGS} || echo "Warning: Failed to upload release ticket to production"
179
- else
180
- echo "No RELEASE-TICKET-${VERSION}.md in pending-/approved-releases — skipping (date-versioned release or ticket archived)."
181
- fi
182
-
183
- - name: Advance release status (post-deploy)
184
- run: |
185
- RELEASE_ID="${{ steps.release.outputs.release_id }}"
186
- if [ -z "$RELEASE_ID" ]; then
187
- echo "::warning::No release_id resolved — skipping status patch"
188
- exit 0
200
+ echo "Promoted ${PROMOTED} release(s) to ${TERMINAL_STATUS}."
201
+ if [ "${TERMINAL_STATUS}" = "prod_review" ]; then
202
+ echo "Next: a human in the portal clicks 'Approve Production' then 'Mark as Released' for each."
203
+ echo "Audit trail captures both events with reviewer identity per compliance/risk-register.md."
189
204
  fi
190
- curl -s -o /dev/null -w "Release status patch: HTTP %{http_code}\n" \
191
- -X PATCH "${BASE}/api/ci/releases/${RELEASE_ID}" \
192
- -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
193
- -H "Content-Type: application/json" \
194
- -d "{\"status\":\"${TERMINAL_STATUS}\"}"
195
- case "$TERMINAL_STATUS" in
196
- prod_review)
197
- echo "Release ${{ steps.release.outputs.version }} → prod_review."
198
- echo "Next: a human in the portal clicks 'Approve Production' (→ prod_approved), then 'Mark as Released' (→ released)."
199
- echo "Audit trail captures both events with reviewer identity per compliance/risk-register.md."
200
- ;;
201
- released)
202
- echo "Release ${{ steps.release.outputs.version }} → released (Option B: auto-release with no post-deploy human gate)."
203
- ;;
204
- esac