From 48f05a4873f09fada50f8298618c86eb88b94e03 Mon Sep 17 00:00:00 2001 From: Mikola Lysenko Date: Tue, 2 Jun 2026 11:21:17 -0400 Subject: [PATCH] ci: remove changelog rollover from release workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The changelog-rollover job committed a "roll [Unreleased] over to vX.Y.Z" change directly back to main after publishing. Branch protection rejects that push (required workflow not satisfied, changes must go through a PR, commits must be signed), so the job failed every release without adding value. Remove the job and its scripts/rollover-changelog.sh helper. Maintainers now rename `## [Unreleased]` to `## [X.Y.Z] — DATE` by hand in the version-bump PR. The version job's CHANGELOG check is simplified to require an explicit `## [X.Y.Z]` heading (it no longer accepts a non-empty [Unreleased] section as a stand-in for the post-publish stamp). Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/release.yml | 62 ++--------------------------- scripts/rollover-changelog.sh | 73 ----------------------------------- 2 files changed, 3 insertions(+), 132 deletions(-) delete mode 100755 scripts/rollover-changelog.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f2adc33..2a9f184 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,27 +43,14 @@ jobs: echo "::error::CHANGELOG.md does not exist at the repository root." exit 1 fi - # A release is valid two ways: - # 1. An explicit `## [X.Y.Z]` / `## X.Y.Z` heading already exists - # (notes written by hand), OR - # 2. The `## [Unreleased]` section is non-empty — the - # post-publish `changelog-rollover` job stamps it as - # `## [X.Y.Z] — DATE` after publishing. + # A release requires an explicit `## [X.Y.Z]` / `## X.Y.Z` heading + # (notes written by hand in the version-bump PR). if grep -qE "^## \[?${VERSION}\]?( |$)" CHANGELOG.md; then echo "Found explicit CHANGELOG heading for ${VERSION}." exit 0 fi - unreleased_content=$(awk ' - /^## \[Unreleased\]/ { inblock=1; next } - inblock && /^## / { inblock=0 } - inblock && NF { print } - ' CHANGELOG.md) - if [ -n "$unreleased_content" ]; then - echo "No explicit ${VERSION} heading, but [Unreleased] has content — it will be rolled over after publish." - exit 0 - fi echo "::error::CHANGELOG.md has no release notes for ${VERSION}." - echo "::error::Add entries under \`## [Unreleased]\` (preferred — they roll over automatically), or a \`## [${VERSION}] — $(date +%Y-%m-%d)\` heading, before re-running." + echo "::error::Add a \`## [${VERSION}] — $(date +%Y-%m-%d)\` heading with release notes before re-running." exit 1 build: @@ -443,46 +430,3 @@ jobs: uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 with: packages-dir: dist/ - - # After every artifact has published, stamp the CHANGELOG: promote the - # `## [Unreleased]` section to `## [] — ` and leave a fresh - # empty `[Unreleased]` for the next cycle, then commit it back to the - # release branch. Idempotent — a no-op when a `## []` heading was - # written by hand. Runs last so a failed publish never rewrites history. - changelog-rollover: - needs: [version, github-release, cargo-publish, npm-publish, pypi-publish] - if: ${{ !inputs.dry-run }} - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Checkout release branch - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.ref_name }} - # persist-credentials defaults to true so the rollover commit can - # be pushed back to the branch below. - - - name: Roll [Unreleased] over to the released version - env: - VERSION: ${{ needs.version.outputs.version }} - run: bash scripts/rollover-changelog.sh "$VERSION" - - - name: Commit and push if changed - # Pass workflow contexts through env vars (never interpolate - # `${{ }}` directly into the shell) so a branch name can't inject - # code into this run block — see zizmor's template-injection audit. - env: - VERSION: ${{ needs.version.outputs.version }} - REF_NAME: ${{ github.ref_name }} - run: | - if git diff --quiet -- CHANGELOG.md; then - echo "CHANGELOG.md unchanged (heading already present); nothing to commit." - exit 0 - fi - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add CHANGELOG.md - # [skip ci] so this housekeeping commit doesn't retrigger CI. - git commit -m "chore(changelog): roll [Unreleased] over to v${VERSION} [skip ci]" - git push origin "HEAD:${REF_NAME}" diff --git a/scripts/rollover-changelog.sh b/scripts/rollover-changelog.sh deleted file mode 100755 index 17fb6aa..0000000 --- a/scripts/rollover-changelog.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Roll the CHANGELOG.md `## [Unreleased]` section over to a released version. -# -# Usage: rollover-changelog.sh [date] -# the version just published (e.g. 3.3.0) -# [date] release date, YYYY-MM-DD (default: today, UTC) -# -# Effect: inserts a `## [] — ` heading immediately below the -# `## [Unreleased]` heading. Everything that was under `[Unreleased]` now -# lives under the new versioned heading, and `[Unreleased]` is left empty -# and ready for the next cycle — the standard Keep a Changelog rollover. -# -# Idempotent + safe by design (it runs *after* publish, so it must never -# fail the release): -# - If a `## []` heading already exists (a maintainer wrote the -# release notes by hand), this is a no-op. -# - If there is no `## [Unreleased]` heading, or it has no content to -# promote, this is a no-op. -# In every no-op case the file is left byte-for-byte unchanged so the -# caller's `git diff` check sees nothing to commit. - -VERSION="${1:?Usage: rollover-changelog.sh [date]}" -DATE="${2:-$(date -u +%Y-%m-%d)}" - -REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" -FILE="${CHANGELOG_FILE:-$REPO_ROOT/CHANGELOG.md}" - -if [ ! -f "$FILE" ]; then - echo "::warning::$FILE not found; skipping changelog rollover." - exit 0 -fi - -# Already stamped (manual flow) — nothing to do. Matches `## [X.Y.Z]` or -# `## X.Y.Z`, with the version followed by a space or end-of-line, mirroring -# the release workflow's version-check. -if grep -qE "^## \[?${VERSION}\]?( |\$)" "$FILE"; then - echo "CHANGELOG already has a heading for ${VERSION}; nothing to roll over." - exit 0 -fi - -if ! grep -qE '^## \[Unreleased\]' "$FILE"; then - echo "::warning::No '## [Unreleased]' heading in $FILE; skipping rollover." - exit 0 -fi - -# Is there anything under [Unreleased] worth promoting? (any non-blank line -# between the [Unreleased] heading and the next `## ` heading) -unreleased_content="$(awk ' - /^## \[Unreleased\]/ { inblock=1; next } - inblock && /^## / { inblock=0 } - inblock && NF { print } -' "$FILE")" -if [ -z "$unreleased_content" ]; then - echo "::warning::[Unreleased] is empty; nothing to roll over for ${VERSION}." - exit 0 -fi - -tmp="$(mktemp)" -awk -v ver="$VERSION" -v date="$DATE" ' - /^## \[Unreleased\]/ && !done { - print - print "" - print "## [" ver "] — " date - done = 1 - next - } - { print } -' "$FILE" >"$tmp" -mv "$tmp" "$FILE" - -echo "Rolled [Unreleased] over to [${VERSION}] — ${DATE}."