feat(release): one-command version bump via prepare-release.sh; close version-drift gaps
- scripts/release/prepare-release.sh bumps workspace + crate pins + npm wrapper + README install tags, refreshes Cargo.lock, regenerates the TUI changelog slice and web facts, then runs check-versions.sh - check-versions.sh now also gates web/lib/facts.generated.ts and the README install-tag examples (both drifted silently before) - .cnb.yml validates the pushed tag against Cargo.toml before generating mirror release notes - RELEASE_CHECKLIST/RUNBOOK updated accordingly (v0.8.56 needed 9 fix commits for exactly these sync points)
This commit is contained in:
@@ -145,6 +145,11 @@ $:
|
||||
tag_name="$(git describe --tags --exact-match 2>/dev/null || true)"
|
||||
fi
|
||||
version="${tag_name#v}"
|
||||
cargo_version="$(grep -E '^version = "' Cargo.toml | head -n1 | sed -E 's/^version = "([^"]+)".*/\1/')"
|
||||
if [ -n "$tag_name" ] && [ "$version" != "$cargo_version" ]; then
|
||||
echo "ERROR: tag ${tag_name} does not match Cargo.toml version ${cargo_version}" >&2
|
||||
exit 1
|
||||
fi
|
||||
commit_sha="${CNB_COMMIT:-$(git rev-parse HEAD)}"
|
||||
{
|
||||
echo "# ${tag_name:-CNB release}"
|
||||
|
||||
@@ -38,14 +38,15 @@ not enumerate.
|
||||
|
||||
## 2. Version pins are in sync
|
||||
|
||||
- [ ] `Cargo.toml` workspace `version` is bumped.
|
||||
- [ ] All per-crate `crates/*/Cargo.toml` path-dependency `version = "..."`
|
||||
pins match the new workspace version.
|
||||
- [ ] `npm/codewhale/package.json` `version` AND `codewhaleBinaryVersion`
|
||||
are both bumped.
|
||||
- [ ] Run `./scripts/release/prepare-release.sh X.Y.Z` — it bumps the
|
||||
workspace version, every per-crate dependency pin,
|
||||
`npm/codewhale/package.json` (`version` + `codewhaleBinaryVersion`),
|
||||
the README install-tag examples, refreshes `Cargo.lock`, regenerates
|
||||
`crates/tui/CHANGELOG.md` and `web/lib/facts.generated.ts`, and ends
|
||||
by running `check-versions.sh`. Write the CHANGELOG entry **before**
|
||||
running it.
|
||||
- [ ] `npm/deepseek-tui/package.json` remains private/compatibility-only and
|
||||
is **not** bumped or published.
|
||||
- [ ] `Cargo.lock` is refreshed (`cargo update --workspace --offline`).
|
||||
- [ ] `./scripts/release/check-versions.sh` reports
|
||||
`Version state OK: workspace=X.Y.Z, npm=X.Y.Z, lockfile in sync.`
|
||||
- [ ] `./scripts/release/check-ohos-deps.sh` reports that the OpenHarmony
|
||||
|
||||
@@ -113,9 +113,12 @@ Crate publishing to crates.io is **manual** — there is no automated
|
||||
`scripts/release/` from a developer workstation that has `cargo login`
|
||||
configured.
|
||||
|
||||
1. Update the workspace version in [Cargo.toml](../Cargo.toml).
|
||||
2. Run `./scripts/release/check-versions.sh` and
|
||||
`./scripts/release/publish-crates.sh dry-run` locally; both must be clean.
|
||||
1. Write the CHANGELOG entry, then run
|
||||
`./scripts/release/prepare-release.sh X.Y.Z` — it bumps every
|
||||
version-bearing file (workspace + crate pins + npm wrapper + README
|
||||
install tags), refreshes the lockfile and generated files, and runs
|
||||
`check-versions.sh`.
|
||||
2. Run `./scripts/release/publish-crates.sh dry-run` locally; it must be clean.
|
||||
3. Tag the release as `vX.Y.Z` (typically by pushing the version bump to
|
||||
`main` and letting `auto-tag.yml` create the tag — see the npm wrapper
|
||||
release section below for the `RELEASE_TAG_PAT` requirement).
|
||||
|
||||
@@ -149,7 +149,24 @@ if grep -qF "hmbown.dev@gmail.com" SECURITY.md; then
|
||||
fail=1
|
||||
fi
|
||||
|
||||
# 8) Cargo.lock in sync.
|
||||
# 8) Generated web facts carry the workspace version.
|
||||
facts_version="$(grep -oE '"version": "[0-9]+\.[0-9]+\.[0-9]+"' web/lib/facts.generated.ts | head -n1 | sed -E 's/.*"([0-9.]+)".*/\1/')"
|
||||
if [[ "${facts_version}" != "${workspace_version}" ]]; then
|
||||
echo "::error::web/lib/facts.generated.ts version (${facts_version}) does not match workspace (${workspace_version}). Run: node web/scripts/derive-facts.mjs" >&2
|
||||
fail=1
|
||||
fi
|
||||
|
||||
# 9) README install-tag examples point at the current release.
|
||||
for readme in README.md README.zh-CN.md README.ja-JP.md README.vi.md; do
|
||||
stale_tags="$(grep -nE -- "--tag v[0-9]+\.[0-9]+\.[0-9]+" "${readme}" | grep -v -- "--tag v${workspace_version}" || true)"
|
||||
if [[ -n "${stale_tags}" ]]; then
|
||||
echo "::error::${readme} has install examples pinned to an old tag (want v${workspace_version}):" >&2
|
||||
echo "${stale_tags}" >&2
|
||||
fail=1
|
||||
fi
|
||||
done
|
||||
|
||||
# 10) Cargo.lock in sync.
|
||||
if ! cargo metadata --locked --format-version 1 --no-deps >/dev/null 2>&1; then
|
||||
echo "::error::Cargo.lock is out of sync with the manifests. Run 'cargo update -p codewhale-tui' or 'cargo build' and commit the result." >&2
|
||||
fail=1
|
||||
|
||||
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env bash
|
||||
# Bump every version-bearing file for a release in one shot.
|
||||
#
|
||||
# Usage: ./scripts/release/prepare-release.sh <new-version>
|
||||
#
|
||||
# Touches: Cargo.toml (workspace version), crates/*/Cargo.toml (internal
|
||||
# codewhale-* dependency pins), npm/codewhale/package.json (version +
|
||||
# codewhaleBinaryVersion), README*.md install-tag examples, Cargo.lock,
|
||||
# crates/tui/CHANGELOG.md (via sync-changelog.sh) and
|
||||
# web/lib/facts.generated.ts (via derive-facts.mjs).
|
||||
#
|
||||
# It does NOT write the CHANGELOG entry — add the `## [X.Y.Z] - YYYY-MM-DD`
|
||||
# section first (see docs/RELEASE_CHECKLIST.md), then run this script, then
|
||||
# let check-versions.sh (run at the end here) confirm everything agrees.
|
||||
set -euo pipefail
|
||||
|
||||
new="${1:?usage: $0 <new-version>}"
|
||||
if ! [[ "${new}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "error: '${new}' is not a plain X.Y.Z version" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
repo="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "${repo}"
|
||||
|
||||
old="$(grep -E '^version = "' Cargo.toml | head -n1 | sed -E 's/^version = "([^"]+)".*/\1/')"
|
||||
if [[ "${old}" == "${new}" ]]; then
|
||||
echo "workspace is already at ${new}; nothing to bump"
|
||||
exit 0
|
||||
fi
|
||||
echo "Bumping ${old} -> ${new}"
|
||||
|
||||
if ! grep -q "^## \[${new}\]" CHANGELOG.md; then
|
||||
echo "warning: CHANGELOG.md has no '## [${new}]' entry yet — add it before tagging" >&2
|
||||
fi
|
||||
|
||||
OLD_VERSION="${old}" NEW_VERSION="${new}" python3 - <<'PY'
|
||||
import os, pathlib, re, sys
|
||||
|
||||
old, new = os.environ["OLD_VERSION"], os.environ["NEW_VERSION"]
|
||||
old_re = re.escape(old)
|
||||
|
||||
def bump(path, pattern, repl, minimum):
|
||||
p = pathlib.Path(path)
|
||||
text = p.read_text()
|
||||
out, n = re.subn(pattern, repl, text)
|
||||
if n < minimum:
|
||||
sys.exit(f"error: expected >= {minimum} replacement(s) in {path}, made {n}")
|
||||
p.write_text(out)
|
||||
print(f" {path}: {n} replacement(s)")
|
||||
|
||||
# 1) Workspace version.
|
||||
bump("Cargo.toml", rf'^version = "{old_re}"$', f'version = "{new}"', 1)
|
||||
|
||||
# 2) Internal codewhale-* dependency pins in every crate manifest.
|
||||
total = 0
|
||||
for manifest in sorted(pathlib.Path("crates").glob("*/Cargo.toml")):
|
||||
text = manifest.read_text()
|
||||
out, n = re.subn(
|
||||
rf'(codewhale-[a-z0-9-]+\s*=\s*\{{[^}}]*version = "){old_re}(")',
|
||||
rf"\g<1>{new}\g<2>",
|
||||
text,
|
||||
)
|
||||
if n:
|
||||
manifest.write_text(out)
|
||||
print(f" {manifest}: {n} pin(s)")
|
||||
total += n
|
||||
if total == 0:
|
||||
sys.exit("error: no internal dependency pins were bumped — wrong old version?")
|
||||
|
||||
# 3) npm wrapper.
|
||||
bump(
|
||||
"npm/codewhale/package.json",
|
||||
rf'("(?:version|codewhaleBinaryVersion)": "){old_re}(")',
|
||||
rf"\g<1>{new}\g<2>",
|
||||
2,
|
||||
)
|
||||
|
||||
# 4) README install-tag examples (all translations).
|
||||
for readme in ["README.md", "README.zh-CN.md", "README.ja-JP.md", "README.vi.md"]:
|
||||
bump(readme, rf"--tag v{old_re}\b", f"--tag v{new}", 1)
|
||||
PY
|
||||
|
||||
echo "Refreshing Cargo.lock..."
|
||||
cargo update --workspace --offline >/dev/null
|
||||
|
||||
echo "Regenerating crates/tui/CHANGELOG.md slice..."
|
||||
./scripts/sync-changelog.sh
|
||||
|
||||
echo "Regenerating web/lib/facts.generated.ts..."
|
||||
node web/scripts/derive-facts.mjs
|
||||
|
||||
echo "Validating..."
|
||||
./scripts/release/check-versions.sh
|
||||
echo "Done. Review 'git diff', commit, and follow docs/RELEASE_CHECKLIST.md."
|
||||
Reference in New Issue
Block a user