its-magic 0.1.2 → 0.1.3-0
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/bin/postinstall.js +72 -0
- package/installer.py +55 -0
- package/package.json +1 -1
- package/scripts/check_intake_template_parity.py +28 -0
- package/template/.cursor/commands/release.md +18 -0
- package/template/CHANGELOG.md +11 -0
- package/template/docs/engineering/runbook.md +77 -6
- package/template/handoffs/releases/vX.Y.Z-release-notes.md.example +21 -0
- package/template/scripts/check_intake_template_parity.py +28 -0
- package/template/scripts/dev_environment_lib.py +138 -0
- package/template/scripts/release-all.sh +249 -0
- package/template/scripts/release_changelog_backfill.py +153 -0
- package/template/scripts/release_changelog_lib.py +544 -0
- package/template/scripts/release_changelog_validate.py +134 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ─────────────────────────────────────────────────────────────────
|
|
3
|
+
# Unified release: npm + Chocolatey + Homebrew (all three at once)
|
|
4
|
+
# ─────────────────────────────────────────────────────────────────
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
BUMP="patch"
|
|
8
|
+
NPM_TAG="latest"
|
|
9
|
+
SKIP_NPM=false
|
|
10
|
+
SKIP_CHOCO=false
|
|
11
|
+
SKIP_BREW=false
|
|
12
|
+
DRY_RUN=false
|
|
13
|
+
|
|
14
|
+
usage() {
|
|
15
|
+
cat <<EOF
|
|
16
|
+
Usage: $0 [options]
|
|
17
|
+
--bump <patch|minor|major|x.y.z> Version bump (default: patch)
|
|
18
|
+
--npm-tag <tag> npm dist-tag (default: latest)
|
|
19
|
+
--skip-npm Skip npm publish
|
|
20
|
+
--skip-choco Skip Chocolatey push
|
|
21
|
+
--skip-brew Skip Homebrew formula update
|
|
22
|
+
--dry-run Print actions without executing
|
|
23
|
+
-h|--help Show this help
|
|
24
|
+
EOF
|
|
25
|
+
exit 0
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
while [[ $# -gt 0 ]]; do
|
|
29
|
+
case "$1" in
|
|
30
|
+
--bump) BUMP="$2"; shift 2;;
|
|
31
|
+
--npm-tag) NPM_TAG="$2"; shift 2;;
|
|
32
|
+
--skip-npm) SKIP_NPM=true; shift;;
|
|
33
|
+
--skip-choco)SKIP_CHOCO=true; shift;;
|
|
34
|
+
--skip-brew) SKIP_BREW=true; shift;;
|
|
35
|
+
--dry-run) DRY_RUN=true; shift;;
|
|
36
|
+
-h|--help) usage;;
|
|
37
|
+
*) echo "Unknown option: $1"; usage;;
|
|
38
|
+
esac
|
|
39
|
+
done
|
|
40
|
+
|
|
41
|
+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
42
|
+
cd "$REPO_ROOT"
|
|
43
|
+
|
|
44
|
+
# Node 22+: trust OS CA store when TLS inspection adds system CAs (Windows/antivirus/corp proxy).
|
|
45
|
+
export NODE_USE_SYSTEM_CA="${NODE_USE_SYSTEM_CA:-1}"
|
|
46
|
+
|
|
47
|
+
log() { echo -e "\033[36m[release]\033[0m $1"; }
|
|
48
|
+
warn() { echo -e "\033[33m[release]\033[0m $1"; }
|
|
49
|
+
err() { echo -e "\033[31m[release]\033[0m $1"; exit 1; }
|
|
50
|
+
|
|
51
|
+
# ── 1. Version bump ─────────────────────────────────────────────────
|
|
52
|
+
log "Bumping version ($BUMP) ..."
|
|
53
|
+
if $DRY_RUN; then
|
|
54
|
+
log "(dry-run) would run: npm version $BUMP --no-git-tag-version"
|
|
55
|
+
NEW_VERSION="0.0.0-dryrun"
|
|
56
|
+
else
|
|
57
|
+
npm version "$BUMP" --no-git-tag-version >/dev/null
|
|
58
|
+
NEW_VERSION=$(node -p "require('./package.json').version")
|
|
59
|
+
fi
|
|
60
|
+
TAG_NAME="v$NEW_VERSION"
|
|
61
|
+
log "New version: $NEW_VERSION (tag: $TAG_NAME)"
|
|
62
|
+
CHOCO_VERSION="$NEW_VERSION"
|
|
63
|
+
if [[ "$NEW_VERSION" =~ ^([0-9]+\.[0-9]+\.[0-9]+)-([0-9]+)$ ]]; then
|
|
64
|
+
# Old Chocolatey/NuGet rejects numeric-only prerelease labels (e.g. 0.1.1-1).
|
|
65
|
+
# Convert to a compatible label for nuspec only.
|
|
66
|
+
CHOCO_VERSION="${BASH_REMATCH[1]}-beta${BASH_REMATCH[2]}"
|
|
67
|
+
fi
|
|
68
|
+
if [[ "$CHOCO_VERSION" != "$NEW_VERSION" ]]; then
|
|
69
|
+
log "Chocolatey version normalized: $NEW_VERSION -> $CHOCO_VERSION"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# ── 2. npm publish ──────────────────────────────────────────────────
|
|
73
|
+
if ! $SKIP_NPM; then
|
|
74
|
+
log "Publishing to npm (tag=$NPM_TAG) ..."
|
|
75
|
+
if $DRY_RUN; then
|
|
76
|
+
log "(dry-run) would run: npm publish --tag $NPM_TAG"
|
|
77
|
+
else
|
|
78
|
+
npm publish --tag "$NPM_TAG"
|
|
79
|
+
log "npm publish OK"
|
|
80
|
+
fi
|
|
81
|
+
else
|
|
82
|
+
warn "Skipping npm"
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# ── 3. GitHub release ───────────────────────────────────────────────
|
|
86
|
+
ZIP_URL=""
|
|
87
|
+
TAR_URL=""
|
|
88
|
+
|
|
89
|
+
GH_PRERELEASE=""
|
|
90
|
+
if [[ "$NEW_VERSION" == *-* ]]; then
|
|
91
|
+
GH_PRERELEASE="--prerelease"
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
RELEASE_CHANGELOG_ALLOW_GENERATE_NOTES="${RELEASE_CHANGELOG_ALLOW_GENERATE_NOTES:-0}"
|
|
95
|
+
VERSION_NOTES="handoffs/releases/${NEW_VERSION}-release-notes.md"
|
|
96
|
+
|
|
97
|
+
if command -v gh &>/dev/null; then
|
|
98
|
+
log "Creating GitHub release $TAG_NAME ..."
|
|
99
|
+
if [[ ! -f "$VERSION_NOTES" ]]; then
|
|
100
|
+
log "Version notes missing — deriving via release_changelog_backfill --ensure-version ..."
|
|
101
|
+
if ! $DRY_RUN; then
|
|
102
|
+
python scripts/release_changelog_backfill.py --repo "$REPO_ROOT" --ensure-version "$NEW_VERSION" || true
|
|
103
|
+
fi
|
|
104
|
+
fi
|
|
105
|
+
if $DRY_RUN; then
|
|
106
|
+
if [[ -f "$VERSION_NOTES" ]]; then
|
|
107
|
+
log "(dry-run) would run: python scripts/release_changelog_validate.py --repo . --enforce"
|
|
108
|
+
log "(dry-run) would run: gh release create $TAG_NAME -F $VERSION_NOTES --title $TAG_NAME $GH_PRERELEASE"
|
|
109
|
+
else
|
|
110
|
+
log "(dry-run) would fail-closed: RELEASE_CHANGELOG_VERSION_DOC_MISSING ($VERSION_NOTES)"
|
|
111
|
+
fi
|
|
112
|
+
elif [[ -f "$VERSION_NOTES" ]]; then
|
|
113
|
+
python scripts/release_changelog_validate.py --repo "$REPO_ROOT" --enforce
|
|
114
|
+
gh release create "$TAG_NAME" -F "$VERSION_NOTES" --title "$TAG_NAME" $GH_PRERELEASE || warn "gh release create failed – continuing"
|
|
115
|
+
elif [[ "$RELEASE_CHANGELOG_ALLOW_GENERATE_NOTES" == "1" ]]; then
|
|
116
|
+
warn "RELEASE_CHANGELOG_VERSION_DOC_MISSING: $VERSION_NOTES — falling back to --generate-notes (opt-in)"
|
|
117
|
+
gh release create "$TAG_NAME" --generate-notes --title "$TAG_NAME" $GH_PRERELEASE || warn "gh release create failed – continuing"
|
|
118
|
+
else
|
|
119
|
+
err "RELEASE_CHANGELOG_VERSION_DOC_MISSING: $VERSION_NOTES (set RELEASE_CHANGELOG_ALLOW_GENERATE_NOTES=1 to opt into --generate-notes)"
|
|
120
|
+
fi
|
|
121
|
+
REMOTE_URL=$(git remote get-url origin 2>/dev/null || true)
|
|
122
|
+
if [[ "$REMOTE_URL" =~ github\.com[:/](.+?)(\.git)?$ ]]; then
|
|
123
|
+
REPO_SLUG="${BASH_REMATCH[1]}"
|
|
124
|
+
REPO_SLUG="${REPO_SLUG%.git}"
|
|
125
|
+
ZIP_URL="https://github.com/$REPO_SLUG/archive/refs/tags/$TAG_NAME.zip"
|
|
126
|
+
TAR_URL="https://github.com/$REPO_SLUG/archive/refs/tags/$TAG_NAME.tar.gz"
|
|
127
|
+
fi
|
|
128
|
+
else
|
|
129
|
+
warn "gh CLI not found – skipping GitHub release"
|
|
130
|
+
ZIP_URL="https://github.com/USER/its-magic/archive/refs/tags/$TAG_NAME.zip"
|
|
131
|
+
TAR_URL="https://github.com/USER/its-magic/archive/refs/tags/$TAG_NAME.tar.gz"
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
# ── 4. Chocolatey ───────────────────────────────────────────────────
|
|
135
|
+
if ! $SKIP_CHOCO; then
|
|
136
|
+
NUSPEC="$REPO_ROOT/packaging/chocolatey/its-magic.nuspec"
|
|
137
|
+
CHOCO_INSTALL="$REPO_ROOT/packaging/chocolatey/tools/chocolateyInstall.ps1"
|
|
138
|
+
|
|
139
|
+
if [[ -f "$NUSPEC" ]]; then
|
|
140
|
+
log "Updating Chocolatey nuspec to $CHOCO_VERSION ..."
|
|
141
|
+
if ! $DRY_RUN; then
|
|
142
|
+
# Update version in nuspec (simple sed)
|
|
143
|
+
sed -i.bak "s|<version>[^<]*</version>|<version>$CHOCO_VERSION</version>|" "$NUSPEC"
|
|
144
|
+
rm -f "${NUSPEC}.bak"
|
|
145
|
+
|
|
146
|
+
# Update URL in install script
|
|
147
|
+
if [[ -n "$ZIP_URL" ]]; then
|
|
148
|
+
sed -i.bak "s|^\\$url[[:space:]]*=.*$|\\$url = '$ZIP_URL'|" "$CHOCO_INSTALL"
|
|
149
|
+
rm -f "${CHOCO_INSTALL}.bak"
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
# Compute checksum
|
|
153
|
+
if [[ -n "$ZIP_URL" ]]; then
|
|
154
|
+
TMP_ZIP="/tmp/its-magic-$TAG_NAME.zip"
|
|
155
|
+
if curl -fsSL "$ZIP_URL" -o "$TMP_ZIP" 2>/dev/null; then
|
|
156
|
+
SHA=$(shasum -a 256 "$TMP_ZIP" | awk '{print $1}')
|
|
157
|
+
sed -i.bak "s|^\\$checksum[[:space:]]*=.*$|\\$checksum = '$SHA'|" "$CHOCO_INSTALL"
|
|
158
|
+
rm -f "${CHOCO_INSTALL}.bak" "$TMP_ZIP"
|
|
159
|
+
log "Chocolatey checksum: $SHA"
|
|
160
|
+
else
|
|
161
|
+
warn "Could not download zip for checksum – set PLACEHOLDER manually"
|
|
162
|
+
fi
|
|
163
|
+
fi
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
if command -v choco &>/dev/null; then
|
|
167
|
+
log "Packing + pushing Chocolatey package ..."
|
|
168
|
+
if ! $DRY_RUN; then
|
|
169
|
+
(cd "$REPO_ROOT/packaging/chocolatey" && choco pack && \
|
|
170
|
+
NUPKG=$(ls -1t *.nupkg 2>/dev/null | head -1) && \
|
|
171
|
+
choco push "$NUPKG" --source https://push.chocolatey.org/ || warn "choco push failed")
|
|
172
|
+
fi
|
|
173
|
+
else
|
|
174
|
+
warn "choco not found – nuspec updated but not pushed"
|
|
175
|
+
fi
|
|
176
|
+
else
|
|
177
|
+
warn "nuspec not found – skipping Chocolatey"
|
|
178
|
+
fi
|
|
179
|
+
else
|
|
180
|
+
warn "Skipping Chocolatey"
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
# ── 5. Homebrew ─────────────────────────────────────────────────────
|
|
184
|
+
if ! $SKIP_BREW; then
|
|
185
|
+
# Detect pre-release: anything with a hyphen (e.g. 0.2.0-beta.1, 1.0.0-rc.1)
|
|
186
|
+
IS_PRERELEASE=false
|
|
187
|
+
if [[ "$NEW_VERSION" == *-* ]]; then
|
|
188
|
+
IS_PRERELEASE=true
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
if $IS_PRERELEASE; then
|
|
192
|
+
FORMULA="$REPO_ROOT/packaging/homebrew/its-magic-beta.rb"
|
|
193
|
+
log "Pre-release detected – using beta formula"
|
|
194
|
+
else
|
|
195
|
+
FORMULA="$REPO_ROOT/packaging/homebrew/its-magic.rb"
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
if [[ -f "$FORMULA" ]]; then
|
|
199
|
+
FORMULA_NAME=$(basename "$FORMULA")
|
|
200
|
+
log "Updating Homebrew formula ($FORMULA_NAME) to $NEW_VERSION ..."
|
|
201
|
+
if ! $DRY_RUN; then
|
|
202
|
+
if [[ -n "$TAR_URL" ]]; then
|
|
203
|
+
sed -i.bak "s|url \"https://github.com/[^\"]*\.tar\.gz\"|url \"$TAR_URL\"|" "$FORMULA"
|
|
204
|
+
rm -f "${FORMULA}.bak"
|
|
205
|
+
else
|
|
206
|
+
sed -i.bak "s|vVERSION|$TAG_NAME|g" "$FORMULA"
|
|
207
|
+
rm -f "${FORMULA}.bak"
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
# Update explicit version line for beta formula
|
|
211
|
+
if $IS_PRERELEASE; then
|
|
212
|
+
sed -i.bak "s|version \"[^\"]*\"|version \"$NEW_VERSION\"|" "$FORMULA"
|
|
213
|
+
rm -f "${FORMULA}.bak"
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
# Compute sha256
|
|
217
|
+
if [[ -n "$TAR_URL" ]]; then
|
|
218
|
+
TMP_TAR="/tmp/its-magic-$TAG_NAME.tar.gz"
|
|
219
|
+
if curl -fsSL "$TAR_URL" -o "$TMP_TAR" 2>/dev/null; then
|
|
220
|
+
SHA=$(shasum -a 256 "$TMP_TAR" | awk '{print $1}')
|
|
221
|
+
# Replace both PLACEHOLDER and any previous sha256 value
|
|
222
|
+
sed -i.bak "s|sha256 \"[^\"]*\"|sha256 \"$SHA\"|" "$FORMULA"
|
|
223
|
+
rm -f "${FORMULA}.bak" "$TMP_TAR"
|
|
224
|
+
log "Homebrew sha256: $SHA"
|
|
225
|
+
else
|
|
226
|
+
warn "Could not download tar.gz – set sha256 manually"
|
|
227
|
+
fi
|
|
228
|
+
fi
|
|
229
|
+
log "Homebrew formula updated: $FORMULA_NAME"
|
|
230
|
+
if $IS_PRERELEASE; then
|
|
231
|
+
log "Users install beta with: brew install USER/tap/its-magic-beta"
|
|
232
|
+
else
|
|
233
|
+
log "Users install stable with: brew install USER/tap/its-magic"
|
|
234
|
+
fi
|
|
235
|
+
fi
|
|
236
|
+
else
|
|
237
|
+
warn "Formula not found – skipping Homebrew"
|
|
238
|
+
fi
|
|
239
|
+
else
|
|
240
|
+
warn "Skipping Homebrew"
|
|
241
|
+
fi
|
|
242
|
+
|
|
243
|
+
# ── Done ─────────────────────────────────────────────────────────────
|
|
244
|
+
log "=========================================="
|
|
245
|
+
log "Release $NEW_VERSION complete!"
|
|
246
|
+
log " npm: $(if $SKIP_NPM; then echo 'SKIPPED'; else echo 'OK'; fi)"
|
|
247
|
+
log " Chocolatey: $(if $SKIP_CHOCO; then echo 'SKIPPED'; else echo 'OK (check warnings)'; fi)"
|
|
248
|
+
log " Homebrew: $(if $SKIP_BREW; then echo 'SKIPPED'; else echo 'OK (formula updated)'; fi)"
|
|
249
|
+
log "=========================================="
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Idempotent one-time backfill for release changelog tiers A/B/C (US-0100 / DEC-0085).
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
14
|
+
_REPO_ROOT = os.path.normpath(os.path.join(_SCRIPT_DIR, ".."))
|
|
15
|
+
if _REPO_ROOT not in sys.path:
|
|
16
|
+
sys.path.insert(0, _REPO_ROOT)
|
|
17
|
+
if _SCRIPT_DIR not in sys.path:
|
|
18
|
+
sys.path.insert(0, _SCRIPT_DIR)
|
|
19
|
+
|
|
20
|
+
import release_changelog_lib as rcl # noqa: E402
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
import yaml # type: ignore
|
|
24
|
+
except ImportError:
|
|
25
|
+
yaml = None # type: ignore
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
MANIFEST_REL = os.path.join(
|
|
29
|
+
"docs", "engineering", "context", "release-version-backfill.manifest.yaml"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _load_manifest(repo_root: str) -> dict:
|
|
34
|
+
path = os.path.join(repo_root, MANIFEST_REL)
|
|
35
|
+
if not os.path.isfile(path):
|
|
36
|
+
return {"schema_version": 1, "entries": []}
|
|
37
|
+
text = rcl.read_utf8(path)
|
|
38
|
+
if yaml is not None:
|
|
39
|
+
data = yaml.safe_load(text) or {}
|
|
40
|
+
else:
|
|
41
|
+
# Minimal stdlib fallback for schema_version + entries
|
|
42
|
+
data = {"schema_version": 1, "entries": []}
|
|
43
|
+
m = re.search(r"schema_version:\s*(\d+)", text)
|
|
44
|
+
if m:
|
|
45
|
+
data["schema_version"] = int(m.group(1))
|
|
46
|
+
for em in re.finditer(
|
|
47
|
+
r"-\s*sprint_id:\s*(S\d{4})\s*\n\s*semver:\s*[\"']?([^\"'\n]+)[\"']?",
|
|
48
|
+
text,
|
|
49
|
+
):
|
|
50
|
+
data.setdefault("entries", []).append(
|
|
51
|
+
{"sprint_id": em.group(1), "semver": em.group(2).strip()}
|
|
52
|
+
)
|
|
53
|
+
return data
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _synthetic_semver(sprint_id: str) -> str:
|
|
57
|
+
num = int(sprint_id[1:])
|
|
58
|
+
return f"0.0.0-wf.{num:03d}"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def assign_semvers(repo_root: str) -> dict[str, str]:
|
|
62
|
+
"""Tier A → B → C precedence; returns sprint_id → semver."""
|
|
63
|
+
rows = sorted(
|
|
64
|
+
[r for r in rcl.parse_queue_rows(repo_root) if r.status == "released"],
|
|
65
|
+
key=lambda r: r.last_updated,
|
|
66
|
+
)
|
|
67
|
+
manifest = _load_manifest(repo_root)
|
|
68
|
+
tier_b: dict[str, str] = {}
|
|
69
|
+
for entry in manifest.get("entries") or []:
|
|
70
|
+
sid = entry.get("sprint_id", "").strip()
|
|
71
|
+
sem = entry.get("semver", "").strip()
|
|
72
|
+
if sid and sem:
|
|
73
|
+
tier_b[sid] = rcl.normalize_semver(sem)
|
|
74
|
+
# collision detection
|
|
75
|
+
inv: dict[str, list[str]] = {}
|
|
76
|
+
for sid, sem in tier_b.items():
|
|
77
|
+
inv.setdefault(sem, []).append(sid)
|
|
78
|
+
for sem, sids in inv.items():
|
|
79
|
+
if len(sids) > 1:
|
|
80
|
+
raise rcl.ReleaseChangelogError(
|
|
81
|
+
rcl.RELEASE_CHANGELOG_BACKFILL_AMBIGUOUS,
|
|
82
|
+
f"manifest maps {sids} → {sem}",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
assignment: dict[str, str] = {}
|
|
86
|
+
for row in rows:
|
|
87
|
+
if row.release_version.strip():
|
|
88
|
+
assignment[row.sprint_id] = rcl.normalize_semver(row.release_version)
|
|
89
|
+
elif row.sprint_id in tier_b:
|
|
90
|
+
assignment[row.sprint_id] = tier_b[row.sprint_id]
|
|
91
|
+
for row in rows:
|
|
92
|
+
if row.sprint_id in assignment:
|
|
93
|
+
continue
|
|
94
|
+
assignment[row.sprint_id] = _synthetic_semver(row.sprint_id)
|
|
95
|
+
return assignment
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def run_backfill(repo_root: str, dry_run: bool = False) -> int:
|
|
99
|
+
rcl.ensure_changelog_stub(repo_root)
|
|
100
|
+
try:
|
|
101
|
+
assignment = assign_semvers(repo_root)
|
|
102
|
+
except rcl.ReleaseChangelogError as exc:
|
|
103
|
+
print(f"{exc.code}: {exc}", file=sys.stderr)
|
|
104
|
+
return 1
|
|
105
|
+
|
|
106
|
+
# coalesce by semver
|
|
107
|
+
groups: dict[str, list[str]] = {}
|
|
108
|
+
for sid, sem in assignment.items():
|
|
109
|
+
groups.setdefault(sem, []).append(sid)
|
|
110
|
+
|
|
111
|
+
for semver, sprint_ids in sorted(groups.items()):
|
|
112
|
+
if dry_run:
|
|
113
|
+
print(f"would build {semver} <- {sprint_ids}")
|
|
114
|
+
continue
|
|
115
|
+
try:
|
|
116
|
+
rcl.build_version_doc(semver, sprint_ids, repo_root)
|
|
117
|
+
rcl.promote_unreleased(semver, sprint_ids, repo_root)
|
|
118
|
+
rcl.bind_queue_release_version(sprint_ids, semver, repo_root)
|
|
119
|
+
except rcl.ReleaseChangelogError as exc:
|
|
120
|
+
if exc.code == rcl.RELEASE_CHANGELOG_DUPLICATE_VERSION:
|
|
121
|
+
print(rcl.RELEASE_CHANGELOG_IDEMPOTENCY_OK, file=sys.stderr)
|
|
122
|
+
continue
|
|
123
|
+
print(f"{exc.code}: {exc}", file=sys.stderr)
|
|
124
|
+
return 1
|
|
125
|
+
|
|
126
|
+
print("[RELEASE_CHANGELOG_BACKFILL_OK]")
|
|
127
|
+
return 0
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def main() -> int:
|
|
131
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
132
|
+
parser.add_argument("--repo", default=_REPO_ROOT)
|
|
133
|
+
parser.add_argument("--dry-run", action="store_true")
|
|
134
|
+
parser.add_argument(
|
|
135
|
+
"--ensure-version",
|
|
136
|
+
metavar="SEMVER",
|
|
137
|
+
help="Ensure handoffs/releases/{semver}-release-notes.md exists (release-all.sh).",
|
|
138
|
+
)
|
|
139
|
+
args = parser.parse_args()
|
|
140
|
+
root = os.path.abspath(args.repo)
|
|
141
|
+
if args.ensure_version:
|
|
142
|
+
try:
|
|
143
|
+
path = rcl.ensure_version_doc_for_release(args.ensure_version, root)
|
|
144
|
+
print(path)
|
|
145
|
+
return 0
|
|
146
|
+
except rcl.ReleaseChangelogError as exc:
|
|
147
|
+
print(f"{exc.code}: {exc}", file=sys.stderr)
|
|
148
|
+
return 1
|
|
149
|
+
return run_backfill(root, dry_run=args.dry_run)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
if __name__ == "__main__":
|
|
153
|
+
raise SystemExit(main())
|