@metasession.co/devaudit-cli 0.3.12 → 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 +53 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/scripts/upload-evidence.sh +55 -1
- package/sdlc/files/_common/0-project-setup.md +10 -4
- 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 +27 -0
- package/sdlc/files/_common/scripts/extract-release-metadata.test.sh +13 -1
- package/sdlc/files/_common/scripts/generate-bundled-changes.sh +302 -64
- package/sdlc/files/_common/scripts/generate-bundled-changes.test.sh +128 -82
- 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 +24 -0
- package/sdlc/files/_common/scripts/validate-compliance-artifacts.sh +16 -0
- package/sdlc/files/_common/scripts/validate-compliance-artifacts.test.sh +14 -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 +21 -2
- 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/ci.yml.template +82 -28
- package/sdlc/files/ci/close-out-release.yml.template +7 -4
- package/sdlc/files/ci/compliance-evidence.yml.template +204 -20
- 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 +34 -20
- package/sdlc/src/blueprints/4-submit-for-review.raw.md +2 -2
- package/sdlc/src/blueprints/5-deploy-main.raw.md +2 -2
|
@@ -1,99 +1,337 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# generate-bundled-changes.sh
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# a markdown summary suitable for upload as `bundled_changes` evidence
|
|
8
|
-
# against a REQ-tagged release.
|
|
4
|
+
# Emits:
|
|
5
|
+
# 1. human-readable bundled-changes markdown on stdout
|
|
6
|
+
# 2. optional machine-readable bundle manifest JSON via --json-out
|
|
9
7
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
8
|
+
# Source of truth:
|
|
9
|
+
# - explicit predecessor release membership comes from release tickets
|
|
10
|
+
# - commit scanning contributes only non-release housekeeping work items
|
|
13
11
|
#
|
|
14
12
|
# Usage:
|
|
15
|
-
# bash scripts/generate-bundled-changes.sh <since-ref> [<version>]
|
|
16
|
-
#
|
|
17
|
-
# Where:
|
|
18
|
-
# <since-ref> — git ref to scan from (tag, SHA, or branch name).
|
|
19
|
-
# Typically the previous release tag or the merge-base
|
|
20
|
-
# of the current branch and the previous release.
|
|
21
|
-
# <version> — (optional) the release version for the header.
|
|
22
|
-
# Defaults to "current release".
|
|
23
|
-
#
|
|
24
|
-
# Output (stdout): markdown document listing housekeeping commits
|
|
25
|
-
# absorbed into this release.
|
|
26
|
-
#
|
|
27
|
-
# DevAudit-Installer#220.
|
|
13
|
+
# bash scripts/generate-bundled-changes.sh <since-ref> [<version>] [--json-out <path>]
|
|
28
14
|
|
|
29
15
|
set -euo pipefail
|
|
30
16
|
|
|
31
17
|
SINCE_REF="${1:-}"
|
|
32
|
-
VERSION="
|
|
18
|
+
VERSION="current release"
|
|
19
|
+
if [ "${2:-}" != "" ] && [[ "${2:-}" != --* ]]; then
|
|
20
|
+
VERSION="$2"
|
|
21
|
+
shift 2
|
|
22
|
+
else
|
|
23
|
+
shift $(( $# > 0 ? 1 : 0 ))
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
JSON_OUT=""
|
|
27
|
+
while [ "$#" -gt 0 ]; do
|
|
28
|
+
case "$1" in
|
|
29
|
+
--json-out) JSON_OUT="$2"; shift 2 ;;
|
|
30
|
+
*) echo "Unknown argument: $1" >&2; exit 2 ;;
|
|
31
|
+
esac
|
|
32
|
+
done
|
|
33
33
|
|
|
34
34
|
if [ -z "$SINCE_REF" ]; then
|
|
35
|
-
echo "Usage: bash scripts/generate-bundled-changes.sh <since-ref> [<version>]" >&2
|
|
35
|
+
echo "Usage: bash scripts/generate-bundled-changes.sh <since-ref> [<version>] [--json-out <path>]" >&2
|
|
36
36
|
exit 1
|
|
37
37
|
fi
|
|
38
38
|
|
|
39
|
-
# Verify the ref exists before proceeding.
|
|
40
39
|
if ! git rev-parse --verify "$SINCE_REF" >/dev/null 2>&1; then
|
|
41
40
|
echo "Error: ref '$SINCE_REF' not found in git history." >&2
|
|
42
41
|
exit 1
|
|
43
42
|
fi
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
# the commit type after the tab character.
|
|
49
|
-
HOUSEKEEPING_TYPES='^[0-9a-f]+ (chore|docs|ci|build|test|revert|style|perf|refactor)(\(.+\))?!?:'
|
|
50
|
-
|
|
51
|
-
# Collect commits since the ref, filtering for housekeeping types.
|
|
52
|
-
# Format: <short-sha>\t<subject>
|
|
53
|
-
COMMITS=$(git log "$SINCE_REF"..HEAD --format='%h %s' 2>/dev/null || true)
|
|
54
|
-
|
|
55
|
-
if [ -z "$COMMITS" ]; then
|
|
56
|
-
# No commits since ref — output an empty summary.
|
|
57
|
-
echo "## Bundled Changes"
|
|
58
|
-
echo ""
|
|
59
|
-
echo "No housekeeping commits found since \`${SINCE_REF}\`."
|
|
60
|
-
echo ""
|
|
61
|
-
exit 0
|
|
44
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
45
|
+
echo "Error: jq is required." >&2
|
|
46
|
+
exit 1
|
|
62
47
|
fi
|
|
63
48
|
|
|
64
|
-
|
|
65
|
-
|
|
49
|
+
HOUSEKEEPING_TYPES='^[0-9a-f]+[[:space:]]+(chore|docs|ci|build|test|revert|style|perf|refactor)(\([^)]+\))?!?:'
|
|
50
|
+
DATE_VERSION_RE='^v[0-9]{4}\.[0-9]{2}\.[0-9]{2}([.][0-9]+)?$'
|
|
51
|
+
|
|
52
|
+
find_release_ticket() {
|
|
53
|
+
local version="$1"
|
|
54
|
+
local candidate=""
|
|
55
|
+
for candidate in \
|
|
56
|
+
"compliance/pending-releases/RELEASE-TICKET-${version}.md" \
|
|
57
|
+
"compliance/approved-releases/RELEASE-TICKET-${version}.md" \
|
|
58
|
+
"compliance/superseded-releases/RELEASE-TICKET-${version}.md"; do
|
|
59
|
+
if [ -f "$candidate" ]; then
|
|
60
|
+
printf '%s' "$candidate"
|
|
61
|
+
return 0
|
|
62
|
+
fi
|
|
63
|
+
done
|
|
64
|
+
return 1
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
trim() {
|
|
68
|
+
printf '%s' "$1" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
extract_ticket_title() {
|
|
72
|
+
local file="$1"
|
|
73
|
+
local line=""
|
|
74
|
+
line=$(grep -m1 '^\*\*Requirement:\*\*' "$file" 2>/dev/null || true)
|
|
75
|
+
if [ -n "$line" ]; then
|
|
76
|
+
printf '%s' "$line" \
|
|
77
|
+
| sed -E 's/^\*\*Requirement:\*\*[[:space:]]*//' \
|
|
78
|
+
| sed -E 's/^REQ-[0-9]+[[:space:]]*[—–:-][[:space:]]*//' \
|
|
79
|
+
| sed -E 's/[[:space:]]*$//'
|
|
80
|
+
return 0
|
|
81
|
+
fi
|
|
82
|
+
line=$(grep -m1 '^# ' "$file" 2>/dev/null || true)
|
|
83
|
+
if [ -n "$line" ]; then
|
|
84
|
+
printf '%s' "$line" \
|
|
85
|
+
| sed -E 's/^# *//' \
|
|
86
|
+
| sed -E 's/^Release Ticket[[:space:]]*[—:-][[:space:]]*//' \
|
|
87
|
+
| sed -E 's/^REQ-[0-9]+[[:space:]]*[—:-][[:space:]]*//' \
|
|
88
|
+
| sed -E 's/[[:space:]]*$//'
|
|
89
|
+
fi
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
extract_ticket_summary() {
|
|
93
|
+
local file="$1"
|
|
94
|
+
awk '
|
|
95
|
+
/^## Summary/ { found=1; next }
|
|
96
|
+
/^## / { if (found) exit }
|
|
97
|
+
found { print }
|
|
98
|
+
' "$file" 2>/dev/null \
|
|
99
|
+
| sed -E 's/[[:space:]]+$//' \
|
|
100
|
+
| awk 'NF { p=1 } p { print }' \
|
|
101
|
+
| awk '{ lines[NR]=$0 } END { for (i=NR; i>=1; i--) { if (lines[i] ~ /[^[:space:]]/) { last=i; break } } for (i=1; i<=last; i++) print lines[i] }'
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
extract_ticket_pr() {
|
|
105
|
+
local file="$1" line pr_num pr_url
|
|
106
|
+
line=$(grep -m1 '^\*\*PR:\*\*' "$file" 2>/dev/null || true)
|
|
107
|
+
pr_num=$(printf '%s' "$line" | grep -oE '#[0-9]+' | head -1 | tr -d '#' || true)
|
|
108
|
+
pr_url=$(printf '%s' "$line" | grep -oE 'https://[^ )]+' | head -1 || true)
|
|
109
|
+
printf '%s\t%s' "$pr_num" "$pr_url"
|
|
110
|
+
}
|
|
66
111
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
112
|
+
extract_explicit_predecessors() {
|
|
113
|
+
local file="$1" line=""
|
|
114
|
+
line=$(grep -m1 '^\- \*\*Absorbed predecessor releases:\*\*' "$file" 2>/dev/null || true)
|
|
115
|
+
if [ -z "$line" ]; then
|
|
116
|
+
return 0
|
|
117
|
+
fi
|
|
118
|
+
if printf '%s' "$line" | grep -qi '\bNone\b'; then
|
|
119
|
+
return 0
|
|
120
|
+
fi
|
|
121
|
+
printf '%s' "$line" | grep -oE 'REQ-[0-9]+|v[0-9]{4}\.[0-9]{2}\.[0-9]{2}([.][0-9]+)?' || true
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
derive_commit_range() {
|
|
125
|
+
local version="$1" file="$2"
|
|
126
|
+
local from="" to="" sha=""
|
|
127
|
+
if [[ "$version" =~ ^REQ-[0-9]+$ ]]; then
|
|
128
|
+
while IFS= read -r sha; do
|
|
129
|
+
[ -n "$sha" ] || continue
|
|
130
|
+
[ -n "$from" ] || from="${sha:0:7}"
|
|
131
|
+
to="${sha:0:7}"
|
|
132
|
+
done < <(git log --reverse --format='%H' --grep "\\[${version}\\]\\|Ref: ${version}" 2>/dev/null || true)
|
|
133
|
+
fi
|
|
134
|
+
printf '%s\t%s' "$from" "$to"
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
CURRENT_TICKET="$(find_release_ticket "$VERSION" 2>/dev/null || true)"
|
|
138
|
+
if [ -z "$CURRENT_TICKET" ] && [ -d "compliance/pending-releases" ]; then
|
|
139
|
+
echo "Error: release ticket for ${VERSION} not found; cannot build explicit bundle manifest." >&2
|
|
140
|
+
exit 1
|
|
73
141
|
fi
|
|
74
142
|
|
|
75
|
-
|
|
76
|
-
|
|
143
|
+
mapfile -t CANDIDATE_RELEASES < <(
|
|
144
|
+
if [ -d "compliance/pending-releases" ]; then
|
|
145
|
+
for ticket in compliance/pending-releases/RELEASE-TICKET-*.md; do
|
|
146
|
+
[ -f "$ticket" ] || continue
|
|
147
|
+
version="${ticket##*/RELEASE-TICKET-}"
|
|
148
|
+
version="${version%.md}"
|
|
149
|
+
[ "$version" = "$VERSION" ] && continue
|
|
150
|
+
printf '%s\n' "$version"
|
|
151
|
+
done
|
|
152
|
+
fi
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
mapfile -t EXPLICIT_PREDECESSORS < <(
|
|
156
|
+
if [ -n "$CURRENT_TICKET" ]; then
|
|
157
|
+
extract_explicit_predecessors "$CURRENT_TICKET"
|
|
158
|
+
fi
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
declare -A EXPLICIT_SET=()
|
|
162
|
+
for version in "${EXPLICIT_PREDECESSORS[@]}"; do
|
|
163
|
+
if [ "$version" = "$VERSION" ]; then
|
|
164
|
+
echo "Error: bundle manifest cannot self-supersede ${VERSION}." >&2
|
|
165
|
+
exit 1
|
|
166
|
+
fi
|
|
167
|
+
if [ -n "${EXPLICIT_SET[$version]:-}" ]; then
|
|
168
|
+
echo "Error: duplicate explicit predecessor '${version}'." >&2
|
|
169
|
+
exit 1
|
|
170
|
+
fi
|
|
171
|
+
EXPLICIT_SET["$version"]=1
|
|
172
|
+
if [ -z "$(find_release_ticket "$version" 2>/dev/null || true)" ]; then
|
|
173
|
+
echo "Error: explicit predecessor '${version}' has no release ticket on disk." >&2
|
|
174
|
+
exit 1
|
|
175
|
+
fi
|
|
176
|
+
done
|
|
177
|
+
|
|
178
|
+
if [ "${#CANDIDATE_RELEASES[@]}" -gt 0 ] && [ "${#EXPLICIT_PREDECESSORS[@]}" -eq 0 ]; then
|
|
179
|
+
echo "Error: ambiguous predecessor ownership for ${VERSION}. Pending release tickets exist but the release ticket does not explicitly list absorbed predecessor releases." >&2
|
|
180
|
+
printf 'Candidates: %s\n' "${CANDIDATE_RELEASES[*]}" >&2
|
|
181
|
+
exit 1
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
UNACCOUNTED=()
|
|
185
|
+
for version in "${CANDIDATE_RELEASES[@]}"; do
|
|
186
|
+
if [ -z "${EXPLICIT_SET[$version]:-}" ]; then
|
|
187
|
+
UNACCOUNTED+=("$version")
|
|
188
|
+
fi
|
|
189
|
+
done
|
|
190
|
+
if [ "${#UNACCOUNTED[@]}" -gt 0 ]; then
|
|
191
|
+
echo "Error: ambiguous predecessor ownership for ${VERSION}. The following pending releases are not explicitly listed in the release ticket bundle section:" >&2
|
|
192
|
+
printf ' - %s\n' "${UNACCOUNTED[@]}" >&2
|
|
193
|
+
exit 1
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
MEMBERS='[]'
|
|
197
|
+
PREDECESSOR_LINES=()
|
|
198
|
+
for version in "${EXPLICIT_PREDECESSORS[@]}"; do
|
|
199
|
+
ticket="$(find_release_ticket "$version")"
|
|
200
|
+
title="$(extract_ticket_title "$ticket")"
|
|
201
|
+
summary="$(extract_ticket_summary "$ticket")"
|
|
202
|
+
pr_meta="$(extract_ticket_pr "$ticket")"
|
|
203
|
+
pr_number="${pr_meta%%$'\t'*}"
|
|
204
|
+
pr_url="${pr_meta#*$'\t'}"
|
|
205
|
+
range_meta="$(derive_commit_range "$version" "$ticket")"
|
|
206
|
+
commit_from="${range_meta%%$'\t'*}"
|
|
207
|
+
commit_to="${range_meta#*$'\t'}"
|
|
208
|
+
|
|
209
|
+
if [[ "$version" =~ $DATE_VERSION_RE ]]; then
|
|
210
|
+
role="housekeeping"
|
|
211
|
+
relationship="absorbed"
|
|
212
|
+
reason="Explicit housekeeping release ticket carried forward into approval envelope ${VERSION}."
|
|
213
|
+
else
|
|
214
|
+
role="predecessor"
|
|
215
|
+
relationship="superseded"
|
|
216
|
+
reason="Explicit predecessor release absorbed into approval envelope ${VERSION}."
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
MEMBERS="$(
|
|
220
|
+
jq -c \
|
|
221
|
+
--arg version "$version" \
|
|
222
|
+
--arg role "$role" \
|
|
223
|
+
--arg relationship "$relationship" \
|
|
224
|
+
--arg reason "$reason" \
|
|
225
|
+
--arg scopeSummary "$summary" \
|
|
226
|
+
--arg commitFrom "$commit_from" \
|
|
227
|
+
--arg commitTo "$commit_to" \
|
|
228
|
+
--arg prNumber "$pr_number" \
|
|
229
|
+
--arg prUrl "$pr_url" \
|
|
230
|
+
'. + [{
|
|
231
|
+
version: $version,
|
|
232
|
+
role: $role,
|
|
233
|
+
relationship: $relationship,
|
|
234
|
+
reason: $reason,
|
|
235
|
+
scopeSummary: (if $scopeSummary == "" then null else $scopeSummary end),
|
|
236
|
+
commitFrom: (if $commitFrom == "" then null else $commitFrom end),
|
|
237
|
+
commitTo: (if $commitTo == "" then null else $commitTo end),
|
|
238
|
+
prNumber: (if $prNumber == "" then null else ($prNumber | tonumber) end),
|
|
239
|
+
prUrl: (if $prUrl == "" then null else $prUrl end)
|
|
240
|
+
}]' <<<"$MEMBERS"
|
|
241
|
+
)"
|
|
242
|
+
PREDECESSOR_LINES+=("- \`${version}\` (${role}/${relationship}) — ${title:-Untitled release ticket}")
|
|
243
|
+
done
|
|
244
|
+
|
|
245
|
+
COMMITS="$(git log "$SINCE_REF"..HEAD --format='%h%x09%s' 2>/dev/null || true)"
|
|
246
|
+
BUNDLED="$(printf '%s\n' "$COMMITS" | grep -E "$HOUSEKEEPING_TYPES" || true)"
|
|
247
|
+
NON_RELEASE_ITEMS='[]'
|
|
248
|
+
NON_RELEASE_LINES=()
|
|
249
|
+
if [ -n "$BUNDLED" ]; then
|
|
250
|
+
while IFS=$'\t' read -r sha subject; do
|
|
251
|
+
[ -n "$sha" ] || continue
|
|
252
|
+
NON_RELEASE_ITEMS="$(
|
|
253
|
+
jq -c \
|
|
254
|
+
--arg kind "housekeeping_commit" \
|
|
255
|
+
--arg title "$subject" \
|
|
256
|
+
--arg reference "$sha" \
|
|
257
|
+
'. + [{
|
|
258
|
+
kind: $kind,
|
|
259
|
+
title: $title,
|
|
260
|
+
reference: $reference
|
|
261
|
+
}]' <<<"$NON_RELEASE_ITEMS"
|
|
262
|
+
)"
|
|
263
|
+
NON_RELEASE_LINES+=("- \`${sha}\` ${subject}")
|
|
264
|
+
done <<<"$BUNDLED"
|
|
265
|
+
fi
|
|
266
|
+
|
|
267
|
+
GENERATOR_VERSION="$(jq -r '.version' cli/package.json 2>/dev/null || echo '0.0.0')"
|
|
268
|
+
REPOSITORY_SLUG="$(git config --get remote.origin.url 2>/dev/null | sed -E 's#(git@github.com:|https://github.com/)##; s#\.git$##' || true)"
|
|
269
|
+
GENERATED_AT="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
270
|
+
|
|
271
|
+
MANIFEST_BASE="$(
|
|
272
|
+
jq -c -n \
|
|
273
|
+
--arg version "$VERSION" \
|
|
274
|
+
--arg members "$MEMBERS" \
|
|
275
|
+
--arg nonReleaseWorkItems "$NON_RELEASE_ITEMS" \
|
|
276
|
+
--arg generatorVersion "$GENERATOR_VERSION" \
|
|
277
|
+
--arg repository "$REPOSITORY_SLUG" \
|
|
278
|
+
--arg generatedAt "$GENERATED_AT" \
|
|
279
|
+
'{
|
|
280
|
+
schemaVersion: 1,
|
|
281
|
+
approvalRelease: { version: $version },
|
|
282
|
+
coreRelease: { version: $version },
|
|
283
|
+
members: ($members | fromjson),
|
|
284
|
+
nonReleaseWorkItems: ($nonReleaseWorkItems | fromjson),
|
|
285
|
+
generator: {
|
|
286
|
+
name: "devaudit-installer",
|
|
287
|
+
version: $generatorVersion,
|
|
288
|
+
repository: (if $repository == "" then null else $repository end),
|
|
289
|
+
generatedAt: $generatedAt
|
|
290
|
+
}
|
|
291
|
+
}'
|
|
292
|
+
)"
|
|
293
|
+
|
|
294
|
+
HASH_INPUT="$(jq -S 'del(.generator.generatedAt)' <<<"$MANIFEST_BASE")"
|
|
295
|
+
MANIFEST_HASH="sha256:$(printf '%s' "$HASH_INPUT" | sha256sum | awk '{print $1}')"
|
|
296
|
+
MANIFEST_JSON="$(jq --arg manifestHash "$MANIFEST_HASH" '. + { manifestHash: $manifestHash }' <<<"$MANIFEST_BASE")"
|
|
297
|
+
|
|
298
|
+
if [ -n "$JSON_OUT" ]; then
|
|
299
|
+
mkdir -p "$(dirname "$JSON_OUT")"
|
|
300
|
+
jq -S . <<<"$MANIFEST_JSON" > "$JSON_OUT"
|
|
301
|
+
fi
|
|
77
302
|
|
|
78
|
-
# Generate the markdown summary.
|
|
79
303
|
echo "## Bundled Changes"
|
|
80
304
|
echo ""
|
|
81
305
|
echo "- **Core tracked release:** \`${VERSION}\`"
|
|
82
|
-
echo "- **
|
|
83
|
-
echo "- **
|
|
84
|
-
echo "- **
|
|
85
|
-
|
|
86
|
-
echo "- **
|
|
306
|
+
echo "- **Bundle manifest:** \`BUNDLED-CHANGES-${VERSION}.json\`"
|
|
307
|
+
echo "- **Manifest hash:** \`${MANIFEST_HASH}\`"
|
|
308
|
+
echo "- **Absorbed predecessor releases:** $(if [ "${#PREDECESSOR_LINES[@]}" -gt 0 ]; then printf '%s' "${EXPLICIT_PREDECESSORS[*]}"; else printf 'None'; fi)"
|
|
309
|
+
if [ "${#NON_RELEASE_LINES[@]}" -gt 0 ]; then
|
|
310
|
+
echo "- **Absorbed non-release work:** housekeeping commits since \`${SINCE_REF}\`"
|
|
311
|
+
else
|
|
312
|
+
echo "- **Absorbed non-release work:** None"
|
|
313
|
+
fi
|
|
314
|
+
echo "- **Why bundled here:** Explicit predecessor release tickets and non-release work are consolidated under approval envelope \`${VERSION}\`."
|
|
315
|
+
echo "- **Evidence impact:** Evidence ownership remains on the source releases; the bundle manifest provides lineage and inherited visibility only."
|
|
316
|
+
echo "- **Reviewer impact:** Approval scope includes the core tracked release plus the explicit predecessor releases and non-release work listed below."
|
|
87
317
|
echo "- **Security / risk impact:** No additional security/risk impact identified automatically; reviewer must confirm in the canonical release artifacts."
|
|
88
318
|
echo "- **Reference:** commit range \`${SINCE_REF}..HEAD\`"
|
|
89
319
|
echo ""
|
|
90
|
-
|
|
320
|
+
|
|
321
|
+
echo "### Explicit Constituent Releases"
|
|
91
322
|
echo ""
|
|
92
|
-
|
|
323
|
+
if [ "${#PREDECESSOR_LINES[@]}" -gt 0 ]; then
|
|
324
|
+
printf '%s\n' "${PREDECESSOR_LINES[@]}"
|
|
325
|
+
else
|
|
326
|
+
echo "- None"
|
|
327
|
+
fi
|
|
93
328
|
echo ""
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
329
|
+
|
|
330
|
+
echo "### Absorbed Non-Release Work"
|
|
331
|
+
echo ""
|
|
332
|
+
if [ "${#NON_RELEASE_LINES[@]}" -gt 0 ]; then
|
|
333
|
+
printf '%s\n' "${NON_RELEASE_LINES[@]}"
|
|
334
|
+
else
|
|
335
|
+
echo "- None"
|
|
336
|
+
fi
|
|
97
337
|
echo ""
|
|
98
|
-
echo "These commits were not individually released (tooling syncs / housekeeping)."
|
|
99
|
-
echo "Gate evidence above covers the full state of develop at CI time, including these changes."
|