From d5051429ddfe2ca41ef237a08b23d2c577e210a0 Mon Sep 17 00:00:00 2001 From: Hunter Bown Date: Wed, 13 May 2026 18:09:57 -0500 Subject: [PATCH] ci(release): harden changelog drift checks --- crates/tui/CHANGELOG.md | 5 +++ scripts/release/check-versions.sh | 68 +++++++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/crates/tui/CHANGELOG.md b/crates/tui/CHANGELOG.md index 1a2cad5a..9b5ede68 100644 --- a/crates/tui/CHANGELOG.md +++ b/crates/tui/CHANGELOG.md @@ -13,6 +13,11 @@ A post-0.8.34 cleanup release focused on prompt hygiene, context-pressure guidance, and keeping the next release branch clearly separated from the already-published v0.8.34 tag. +> **Note on v0.8.34 contributor credits:** Horace Liu ([@liuhq](https://github.com/liuhq)) +> contributed Nix package support and install documentation in the v0.8.34 +> cycle but was inadvertently omitted from that release's changelog. The +> README contributor list and this note correct the record. + ### Changed - **First-turn prompt context is leaner and easier to audit.** The diff --git a/scripts/release/check-versions.sh b/scripts/release/check-versions.sh index 3fbda7ed..776af6f4 100755 --- a/scripts/release/check-versions.sh +++ b/scripts/release/check-versions.sh @@ -9,8 +9,10 @@ # `version` in the root `Cargo.toml`. # 3. Internal `deepseek-*` path dependency pins match the workspace version. # 4. The TUI crate's packaged changelog copy matches root `CHANGELOG.md`. -# 5. `SECURITY.md` keeps the dedicated security contact. -# 6. `Cargo.lock` is in sync with the manifests (`cargo metadata --locked` +# 5. The current release has a dated Keep a Changelog entry and compare link. +# 6. README contributor additions are mentioned in the current release entry. +# 7. `SECURITY.md` keeps the dedicated security contact. +# 8. `Cargo.lock` is in sync with the manifests (`cargo metadata --locked` # fails if not). set -euo pipefail @@ -52,7 +54,65 @@ if ! cmp -s CHANGELOG.md crates/tui/CHANGELOG.md; then fail=1 fi -# 5) Security contact guard. +# 5) Current release-note shape. +current_section="$( + awk -v version="${workspace_version}" ' + index($0, "## [" version "] - ") == 1 { in_section = 1; print; next } + in_section && /^## \[/ { exit } + in_section { print } + ' CHANGELOG.md +)" +if [[ -z "${current_section}" ]]; then + echo "::error::CHANGELOG.md must contain a section for ${workspace_version}." >&2 + fail=1 +else + if ! grep -qE "^## \\[${workspace_version}\\] - [0-9]{4}-[0-9]{2}-[0-9]{2}$" <<<"${current_section}"; then + echo "::error::CHANGELOG.md section ${workspace_version} must use '## [${workspace_version}] - YYYY-MM-DD'." >&2 + fail=1 + fi + if ! grep -qE "^### (Added|Changed|Deprecated|Removed|Fixed|Security)$" <<<"${current_section}"; then + echo "::error::CHANGELOG.md section ${workspace_version} must contain at least one Keep a Changelog subsection." >&2 + fail=1 + fi +fi + +compare_line="$(grep -E "^\\[${workspace_version}\\]: " CHANGELOG.md || true)" +if [[ -z "${compare_line}" ]]; then + echo "::error::CHANGELOG.md must include a compare link for ${workspace_version}." >&2 + fail=1 +fi + +# 6) Contributor-credit cross-check for README additions on the release branch. +# This cannot prove every external PR author has been credited, but it does +# catch the common release-polish failure mode: adding a README contributor row +# without mentioning that credit/correction in the current release entry. +previous_tag="" +current_tag="v${workspace_version}" +if [[ "${compare_line}" =~ compare/(v[0-9]+\.[0-9]+\.[0-9]+)\.\.\.${current_tag} ]]; then + previous_tag="${BASH_REMATCH[1]}" +fi +if [[ -n "${previous_tag}" ]]; then + if ! git rev-parse -q --verify "refs/tags/${previous_tag}" >/dev/null; then + git fetch --quiet --depth=1 origin "refs/tags/${previous_tag}:refs/tags/${previous_tag}" || true + fi + if git rev-parse -q --verify "refs/tags/${previous_tag}" >/dev/null; then + while IFS= read -r line; do + [[ -z "${line}" ]] && continue + handle="$(sed -E 's#.*github.com/([^)/]+).*#\1#' <<<"${line}")" + if [[ -n "${handle}" && "${handle}" != "${line}" ]]; then + if ! grep -Fq "github.com/${handle}" <<<"${current_section}" && ! grep -Fq "@${handle}" <<<"${current_section}"; then + echo "::error::README.md adds contributor @${handle}, but CHANGELOG.md ${workspace_version} does not mention that credit." >&2 + fail=1 + fi + fi + done < <( + git diff "${previous_tag}..HEAD" -- README.md \ + | grep -E '^\+[-*] \*\*\[[^]]+\]\(https://github.com/[^)]+\)\*\*' || true + ) + fi +fi + +# 7) Security contact guard. security_email="security@deepseek-tui.com" if ! grep -qF "${security_email}" SECURITY.md; then echo "::error::SECURITY.md must list ${security_email} as the security contact." >&2 @@ -63,7 +123,7 @@ if grep -qF "hmbown.dev@gmail.com" SECURITY.md; then fail=1 fi -# 6) Cargo.lock in sync. +# 8) 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 deepseek-tui' or 'cargo build' and commit the result." >&2 fail=1