From 82855a362350eef240652abd5f97e54cd5ebb5a8 Mon Sep 17 00:00:00 2001 From: Sekers <46898253+Sekers@users.noreply.github.com> Date: Wed, 16 Sep 2026 14:25:57 -0500 Subject: [PATCH] Keep the [Unreleased] changelog section off main [Unreleased] now exists only on develop, and only while it has entries. The release-prep pull request renames it to the version heading without adding a new empty one, and the first later pull request with a changelog entry adds it back. The Release workflow's tag check refuses to publish a CHANGELOG.md that still has an [Unreleased] section. - Release.yml: the validate job fails on an [Unreleased] heading. - RELEASING.md, AGENTS.md, CONTRIBUTING.md, pull request template: add the section only with an entry, never during release prep, and merge nothing else into develop between release prep and the release merge. - CHANGELOG.md: remove the empty [Unreleased] section added by the 1.1.1 release prep. --- .github/pull_request_template.md | 2 +- .github/workflows/Release.yml | 3 +++ AGENTS.md | 4 ++++ CHANGELOG.md | 4 ---- CONTRIBUTING.md | 4 +++- RELEASING.md | 20 ++++++++++++++------ 6 files changed, 25 insertions(+), 12 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index cdc8ace..c3b250c 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -14,7 +14,7 @@ ## Checklist - [ ] Targets `develop`, or `main` for a hotfix -- [ ] `CHANGELOG.md` has an entry under `[Unreleased]` in the right section, or the change is not user-visible +- [ ] `CHANGELOG.md` has an entry in the right section under `[Unreleased]` (added if missing), or the change is not user-visible - [ ] `.\Tests\Invoke-Tests.ps1` passes - [ ] Contains no real tenant names, email domains, secrets, or configuration files - [ ] Any live Microsoft Graph testing was done in a test tenant and is described above diff --git a/.github/workflows/Release.yml b/.github/workflows/Release.yml index eaa3435..5f2fc86 100644 --- a/.github/workflows/Release.yml +++ b/.github/workflows/Release.yml @@ -53,6 +53,9 @@ jobs: $HeadingPattern = '^## \[' + [regex]::Escape($Tag) + '\](\([^)]*\))? - \d{4}-\d{2}-\d{2}\s*$' if (-not (Select-String -Path 'CHANGELOG.md' -Pattern $HeadingPattern -Quiet)) { throw "CHANGELOG.md has no dated section for $Tag, such as '## [$Tag] - 2026-01-31'." } + # [Unreleased] exists only on develop, so a released CHANGELOG.md never carries one. + if (Select-String -Path 'CHANGELOG.md' -Pattern '^## \[Unreleased\]' -Quiet) { throw "CHANGELOG.md still has an [Unreleased] section. The release-prep pull request renames it to the $Tag heading and adds no new one." } + # Stable releases come only from main; prereleases may also come from develop. $AllowedBranches = if ($Prerelease) { @('origin/main', 'origin/develop') } else { @('origin/main') } $OnAllowedBranch = $false diff --git a/AGENTS.md b/AGENTS.md index 6f2ac09..5dc8a3e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -120,6 +120,10 @@ covers when `[Unreleased]` becomes a version and how that version is chosen. - **New entries go under `## [Unreleased]`** at the top of the file. Never invent a version number for them; the release-prep pull request turns `[Unreleased]` into a dated version heading. +- **Add the `[Unreleased]` section only with an entry.** Right after a release there is none, so the first new + entry also adds `## [Unreleased](https://github.com/Sekers/ScriptMessage/compare/...develop)` at the + top, where `` is `$Release` from the baseline commands above. Never add an empty one, and never leave + one in a release: the Release workflow refuses to publish while it is there. - Newest version first. A released version uses this heading shape, and a `---` line separates it from the next (older) version: diff --git a/CHANGELOG.md b/CHANGELOG.md index a74de7a..60882dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,5 @@ # Changelog for ScriptMessage PowerShell Module -## [Unreleased](https://github.com/Sekers/ScriptMessage/compare/1.1.1...develop) - ---- - ## [1.1.1](https://github.com/Sekers/ScriptMessage/tree/1.1.1) - 2026-09-16 ### Changed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fb815f9..a9a6c5a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,9 @@ Thank you for helping improve ScriptMessage. This guide covers the everyday work .\Tests\Invoke-Tests.ps1 ``` -5. If users will notice the change, add an entry to `CHANGELOG.md` under `[Unreleased]`. +5. If users will notice the change, add an entry to `CHANGELOG.md` under `[Unreleased]`. If that section is not + there yet, as right after a release, add it at the top ([RELEASING.md](./RELEASING.md#changelog) shows the + heading). 6. Open a pull request into `develop` (or into `main` for a hotfix) and complete the pull request template, including the release classification. diff --git a/RELEASING.md b/RELEASING.md index 04769c4..0c252a2 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -105,15 +105,22 @@ user-visible change adds its entry under `## [Unreleased]`, in one of these sect `Changed`, `Deprecated`, `Removed`, `Fixed`, `Security`. The rules for what an entry says are in the `CHANGELOG.md` section of [AGENTS.md](./AGENTS.md), and they apply to everyone, not only to AI assistants. +`[Unreleased]` exists only on `develop`, and only while it has entries. A release renames it to the version +heading, so right after a release there is none. The next pull request that adds an entry also adds the section at +the top, linked to a comparison from the last release: +`## [Unreleased](https://github.com/Sekers/ScriptMessage/compare/1.2.0...develop)`. The Release workflow refuses to +publish a tag whose `CHANGELOG.md` still has an `[Unreleased]` section. + ## Releasing a version 1. **Choose the version** from the `[Unreleased]` entries, following [Deciding hard cases](#deciding-hard-cases). 2. **Open a release-prep pull request into `develop`** that changes only: - `ModuleVersion`, and `Prerelease` for a prerelease, in `ScriptMessage/ScriptMessage.psd1`. - `CHANGELOG.md`: rename `## [Unreleased](...)` to a dated heading such as - `## [1.2.0](https://github.com/Sekers/ScriptMessage/tree/1.2.0) - 2026-09-20`, and add a new, empty - `## [Unreleased](https://github.com/Sekers/ScriptMessage/compare/1.2.0...develop)` above it. -3. **Open a pull request from `develop` into `main`**, and merge it with a merge commit once the checks pass. + `## [1.2.0](https://github.com/Sekers/ScriptMessage/tree/1.2.0) - 2026-09-20`. Do not add a new + `[Unreleased]` section. +3. **Open a pull request from `develop` into `main`**, and merge it with a merge commit once the checks pass. Merge + nothing else into `develop` until then, so no new `[Unreleased]` section reaches `main`. 4. **Tag the merge commit with an annotated tag** (add `-s` to sign it when you have signing set up), and push the tag: @@ -125,8 +132,8 @@ user-visible change adds its entry under `## [Unreleased]`, in one of these sect 5. **The Release workflow** (`.github/workflows/Release.yml`) runs these jobs in order and stops at the first failure: - 1. Confirms the tag is annotated, matches the manifest version, has a dated `CHANGELOG.md` section, and - points at a commit on `main` (or on `develop`, for a prerelease). + 1. Confirms the tag is annotated, matches the manifest version, has a dated `CHANGELOG.md` section and no + `[Unreleased]` section, and points at a commit on `main` (or on `develop`, for a prerelease). 2. Runs the test suite (`.github/workflows/Tests.yml`). 3. Builds the package once and publishes it to the PowerShell Gallery, in the `psgallery` environment. 4. Creates the GitHub release from the version's changelog section, attaches the same package, and @@ -180,7 +187,8 @@ enforceable: - [ ] Every change listed under `[Unreleased]` has been reviewed and merged into `develop`. - [ ] The version increment follows [Versioning](#versioning). -- [ ] The release-prep pull request changed only the manifest version and the changelog headings. +- [ ] The release-prep pull request changed only the manifest version and the changelog heading, and added no + `[Unreleased]` section. - [ ] `develop` was merged into `main` with a merge commit. - [ ] The tag is annotated, has no `v` prefix, and is on the merge commit. - [ ] Every job in the Release workflow passed.