|
| 1 | +# Generate Release Notes |
| 2 | + |
| 3 | +When asked to generate release notes, follow this procedure. |
| 4 | + |
| 5 | +## Inputs |
| 6 | + |
| 7 | +- `PREV_TAG`: The previous release tag (e.g., `3.4.0`) |
| 8 | +- `RELEASE_REF`: The release tag or branch to generate notes for (e.g., `3.5.0` or `upstream/3.5`) |
| 9 | + |
| 10 | +If not provided, infer from context. Use `git tag --sort=-creatordate` to find recent tags. |
| 11 | + |
| 12 | +All temporary files throughout this process should be written to `/tmp/release-notes-VERSION/` (e.g., `/tmp/release-notes-3.6.0/`). Create this directory at the start. This makes cleanup easy: `rm -rf /tmp/release-notes-VERSION/`. |
| 13 | + |
| 14 | +## Step 1: Identify commits to exclude |
| 15 | + |
| 16 | +The previous release tag lives on a release branch, not on main. Commits merged to main during the release window (between the branch cut and the tag) may have been backported to the release branch. These already shipped in the previous release and must be excluded. |
| 17 | + |
| 18 | +``` |
| 19 | +PREV_BRANCH_CUT=$(git merge-base "$PREV_TAG" "$RELEASE_REF") |
| 20 | +``` |
| 21 | + |
| 22 | +Extract all PR numbers from commits in the previous release window: |
| 23 | + |
| 24 | +``` |
| 25 | +git log ${PREV_BRANCH_CUT}..${PREV_TAG} --format='%s' |
| 26 | +``` |
| 27 | + |
| 28 | +Parse PR numbers (pattern `#NNNNN`) from these commit subjects. This is the exclusion set. |
| 29 | + |
| 30 | +## Step 2: Collect candidate commits |
| 31 | + |
| 32 | +``` |
| 33 | +git log ${PREV_TAG}..${RELEASE_REF} --format='%s' --reverse |
| 34 | +``` |
| 35 | + |
| 36 | +Remove any commit whose PR number appears in the exclusion set from step 1. Log which commits were excluded and why. |
| 37 | + |
| 38 | +Detect commit/revert pairs (a commit followed by a revert of that commit) and exclude both — the net effect is no change. |
| 39 | + |
| 40 | +## Step 3: Fetch PR metadata |
| 41 | + |
| 42 | +For each non-dependency-bump commit, fetch the PR title, labels, and description in a single call and write the results to a temp file per PR: |
| 43 | + |
| 44 | +``` |
| 45 | +gh pr view NNNNN --json title,body,labels --jq '{title, labels: [.labels[].name], body}' > /tmp/release-notes-VERSION/pr-NNNNN.json |
| 46 | +``` |
| 47 | + |
| 48 | +Dependency bumps (commit message matches "Bump X from Y to Z" or similar) do not need a GitHub API call — the commit message is sufficient. These go directly into the Dependencies category. |
| 49 | + |
| 50 | +## Step 4: Filter and categorize |
| 51 | + |
| 52 | +Using the cached PR metadata from step 3: |
| 53 | + |
| 54 | +1. **Hard exclude** any PR with the `skip-changelog` label, regardless of content. |
| 55 | + |
| 56 | +2. **Exclude non-user-facing changes** per this policy: |
| 57 | + - Test additions, modifications, or fixes (flaky test fixes, test refactoring) |
| 58 | + - Incremental PRs for a larger feature (these should already have one entry from the main PR) |
| 59 | + - Documentation changes or internal code refactoring |
| 60 | + - Build-related changes (CI config, Gradle changes) |
| 61 | + - Release machinery (release notes commits, README edits) |
| 62 | + - Maintainer list changes |
| 63 | + |
| 64 | + Use the commit message, PR description, and labels to make this judgment. When uncertain whether a change is user-facing, include it — a human reviewer can remove it later. |
| 65 | + |
| 66 | +3. **Categorize** each surviving commit using [Keep A Changelog](https://keepachangelog.com/en/1.0.0/) categories. PR labels are useful here (e.g., `enhancement`, `bug`, `breaking`, `dependencies`, `deprecation`): |
| 67 | + - **Added** — new features and capabilities |
| 68 | + - **Changed** — changes to existing functionality |
| 69 | + - **Deprecated** — features that will be removed in a future release |
| 70 | + - **Removed** — features that were removed |
| 71 | + - **Fixed** — bug fixes |
| 72 | + - **Dependencies** — dependency version updates. List Apache Lucene updates first. |
| 73 | + |
| 74 | +4. **Write entries.** For each entry, write a concise one-line summary. Use the commit message and PR description as input but rewrite for clarity and consistency. The target audience for these notes are OpenSearch users, such as end-users, operators, and system administrators. |
| 75 | + |
| 76 | + Format each entry as: |
| 77 | + |
| 78 | + ``` |
| 79 | + - Summary of change ([#NNNNN](https://github.com/opensearch-project/OpenSearch/pull/NNNNN)) |
| 80 | + ``` |
| 81 | + |
| 82 | + Group related commits into a single entry when appropriate (e.g., multiple PRs implementing parts of the same feature, or a series of dependency bumps for the same library). |
| 83 | + |
| 84 | +## Step 5: Track borderline calls |
| 85 | + |
| 86 | +During steps 1–4, record any judgment calls where the categorization or inclusion/exclusion decision is debatable. Examples: |
| 87 | + |
| 88 | +- A PR labeled `bug` that reads more like a behavioral change |
| 89 | +- Grouping multiple PRs into a single entry |
| 90 | +- Including a PR that could reasonably be considered non-user-facing |
| 91 | +- Choosing one category over another when both fit |
| 92 | + |
| 93 | +Write these to `/tmp/release-notes-VERSION/borderline.md` as a bulleted list. Each entry should reference the PR number(s) and briefly explain the decision and alternatives. |
| 94 | + |
| 95 | +## Step 6: Output |
| 96 | + |
| 97 | +Write the release notes file to `release-notes/opensearch.release-notes-VERSION.md` using this template: |
| 98 | + |
| 99 | +```markdown |
| 100 | +## Version VERSION Release Notes |
| 101 | + |
| 102 | +Compatible with OpenSearch and OpenSearch Dashboards version VERSION |
| 103 | + |
| 104 | +### Added |
| 105 | +... |
| 106 | + |
| 107 | +### Changed |
| 108 | +... |
| 109 | + |
| 110 | +### Deprecated |
| 111 | +... |
| 112 | + |
| 113 | +### Removed |
| 114 | +... |
| 115 | + |
| 116 | +### Fixed |
| 117 | +... |
| 118 | + |
| 119 | +### Dependencies |
| 120 | +... |
| 121 | +``` |
| 122 | + |
| 123 | +Omit empty categories. Use `- ` (dash space) for list items. |
| 124 | + |
| 125 | +## Step 7: Commit and push |
| 126 | + |
| 127 | +Identify the user's fork remote from `git remote -v` — look for a remote whose URL contains the user's GitHub username rather than `opensearch-project/OpenSearch`. If ambiguous, ask the user which remote to use. |
| 128 | + |
| 129 | +``` |
| 130 | +git checkout -b release-notes-VERSION |
| 131 | +git add release-notes/opensearch.release-notes-VERSION.md |
| 132 | +git commit -s -m "Add release notes for VERSION" |
| 133 | +git push <fork-remote> release-notes-VERSION |
| 134 | +``` |
| 135 | + |
| 136 | +## Step 8: Open PR |
| 137 | + |
| 138 | +Create the PR body in a temp file, then open the PR: |
| 139 | + |
| 140 | +``` |
| 141 | +gh pr create \ |
| 142 | + --base main \ |
| 143 | + --head <github-user>:release-notes-VERSION \ |
| 144 | + --title "Add release notes for VERSION" \ |
| 145 | + --body-file /tmp/release-notes-VERSION/pr-body.md |
| 146 | +``` |
| 147 | + |
| 148 | +The PR body should follow this structure: |
| 149 | + |
| 150 | +```markdown |
| 151 | +### Description |
| 152 | +Release notes for OpenSearch VERSION, covering commits from PREV_TAG to RELEASE_REF. |
| 153 | + |
| 154 | +### Borderline calls |
| 155 | +- #NNNNN: Placed in **Category** — rationale. Could also be **Other Category**. |
| 156 | +- #AAAAA + #BBBBB: Grouped into one entry — rationale. |
| 157 | +- ... |
| 158 | + |
| 159 | +### Check List |
| 160 | +- [x] Functionality includes testing. |
| 161 | + |
| 162 | +By submitting this pull request, I confirm that my contribution is made under |
| 163 | +the terms of the Apache 2.0 license. |
| 164 | +``` |
| 165 | + |
| 166 | +The borderline calls section gives reviewers specific items to validate. If there are no borderline calls, omit the section. |
0 commit comments