Skip to content

Commit ae86058

Browse files
committed
docs(changelog): convert CHANGES.txt to Keep a Changelog
CHANGES.txt held twenty years of releases in a format that grew by accretion: 93 sections under six different heading shapes, 51 dates in five formats, sixteen entry prefixes, and product sub-headings that partitioned a version between core, the Eclipse plug-in and the IDEA plug-in. It is now CHANGELOG.md, following Keep a Changelog 1.1.0. The conversion is a format change. Every entry, sub-bullet, issue reference and attribution is carried over word for word; the 751 GITHUB- and TESTNG- references come out at 751, none lost and none invented, and the 93 sections stay 93. What the file recorded about itself is kept too: a release that was pulled keeps its own words and gains a [YANKED] marker, and the version that [Unreleased] is heading for is named rather than dropped. The sixteen prefixes fold into the six Keep a Changelog categories: New and Added become Added, Update and Improved behavior become Changed, Fix a typo for Fixed. A product sub-heading becomes a bold prefix on each of its entries, since the format has no place for it. The breaking-change block, which three entries refer to by name, stays a block, nested under Changed as a fourth-level heading. Three things the file could not state are now stated: - Dates. Sections stopped carrying one after 6.11, so 6.12 through 7.12.0 had none. They are reconstructed from tags, GitHub releases and Maven Central publication records. 4.6 was recorded as 2006/27/02, and month 27 does not exist; in an era spelling dates YYYY/MM/DD the day and month are swapped, so it reads 2006-02-27. The dates of 4.5 and 5.0 are chronologically impossible and are carried unchanged, because correcting them would be inventing. - 7.5.1. It was released from the release_7.5 branch and never reached this file. Its section is reconstructed from the two commits between the 7.5 and 7.5.1 tags, and sits where its date puts it, between 7.8.0 and 7.7.1. - 5.0.1 was declared twice, in two sections separated by a rule. One version, one section. Comparison links close the file for the 50 versions whose boundaries are both tagged. 7.1.0 is not among them: it was published to Maven Central and never tagged. Each link's base is the release the section's changes are measured against, read from the release lineage rather than from the section above it. For a release made from master that base is also a Git ancestor; it is not for 7.5.1, whose branch forked before the 7.5 tag, and 7.5...7.5.1 is a divergent comparison that still shows exactly the two commits that release shipped. Ordering by section would have compared 7.8.0 against 7.5.1 and answered with 150 commits of divergence instead of its 25. The header does not claim Semantic Versioning. The version being prepared is a minor one and its own Possible backward incompatible changes block breaks org.testng, not only org.testng.internal, so the header says what the project does instead: a minor release may carry a breaking change, and each is listed in the section that ships it. Around the file: .gitattributes carries the union merge strategy to the new path, so two pull requests can still add a line each without conflicting. AGENTS.md and the pull request template name the new file and its categories. RELEASE_PROCESS.md gains the step that promotes [Unreleased] into a dated section, which until now existed only in the habit of doing it. It runs after tagging, not before, because the promotion adds commits and the tag has to point at what was built -- which is the order 7.12.0 was actually released in. Its tag command adopts a v prefix: the release stays 7.10.0, which is what gradle.properties and Maven Central carry, and v7.10.0 is the name of the ref. Releases up to 7.12.0 carry no prefix, so the first v tag compares against a bare one and the rest follow. It also names the commit the publish workflow built rather than whatever local HEAD happens to be: that workflow is dispatched by hand and checks out the branch head of the moment, while the tagging happens half an hour later. The run is named outright rather than taken as the most recent successful one, which a re-run, a staged USER_MANAGED run or a second dispatch of the same version would each satisfy, and its conclusion and the version its commit declares are both checked before the tag is written. Promoting the changelog now starts by diffing it against that commit. A pull request merging while Central syncs leaves its entry under Unreleased with its code outside the artifacts, and dating the section wholesale would file that work under a version that never carried it while removing it from the one that will. README.md now points at the changelog above its list of mailing list announcements, which it keeps: the archive makes a single release hard to find, which is why the list exists. The release step that updated README.md is now about that list alone -- the four other things it named, a Maven example, a Gradle example, download links and version badges, are none of them in that file -- and it moves after the announcement, which is when the thread it links to starts existing.
1 parent f23ffc9 commit ae86058

8 files changed

Lines changed: 2468 additions & 2147 deletions

File tree

‎.gitattributes‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
*.yaml text
88
*.yml text
99
*.bat text eol=crlf
10-
/CHANGES.txt merge=union
10+
/CHANGELOG.md merge=union

‎.github/PULL_REQUEST_TEMPLATE.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Fixes # .
33
### Did you remember to?
44

55
- [ ] Add test case(s)
6-
- [ ] Update `CHANGES.txt`
6+
- [ ] Update `CHANGELOG.md`, under `## [Unreleased]`
77
- [ ] Auto applied styling via `./gradlew autostyleApply`
88
- [ ] Checked the wording of any javadoc, comments or docs you touched, via
99
`./gradlew writingStyleCheckChanges` ([writing style](../docs/WRITING_STYLE.md))

‎AGENTS.md‎

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -248,15 +248,22 @@ From a fork, pull requests are cross-fork:
248248
gh pr create --repo testng-team/testng --base master --head <fork-owner>:<branch>
249249
```
250250

251-
Conventional Commits. Record user-visible changes in `CHANGES.txt`, newest first, under the current
252-
version heading.
253-
254-
A change under `org.testng.internal` earns two `CHANGES.txt` entries: the `Changed:` line in prose,
255-
and a bullet under `Possible backward incompatible changes:` naming what stops compiling and what to
256-
use instead. The package is OSGi exported yet excluded from javadoc, which reads as a contradiction
257-
and invites deprecated bridges; the project does the opposite, breaking them and recording it, each
258-
entry naming the export and doing it anyway. Grep `CHANGES.txt` for `internal` before arguing
259-
about compatibility — the precedents are the argument.
251+
Conventional Commits. Record user-visible changes in `CHANGELOG.md`, newest first, under
252+
`## [Unreleased]`, in the `### Added`, `### Changed`, `### Deprecated`, `### Removed`, `### Fixed`
253+
or `### Security` subsection the change belongs to. The file follows
254+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
255+
256+
Put your entry under a subsection that is already there whenever you can. `.gitattributes` merges
257+
the changelog with the `union` strategy, which takes both sides of a concurrent edit rather than
258+
raising a conflict: two pull requests that each open the same missing subsection land two copies of
259+
that heading, silently, and nothing reads the file to catch it.
260+
261+
A change under `org.testng.internal` earns two `CHANGELOG.md` entries: the `### Changed` line in
262+
prose, and a bullet under `#### Possible backward incompatible changes` naming what stops compiling
263+
and what to use instead. The package is OSGi exported yet excluded from javadoc, which reads as a
264+
contradiction and invites deprecated bridges; the project does the opposite, breaking them and
265+
recording it, each entry naming the export and doing it anyway. Grep `CHANGELOG.md` for `internal`
266+
before arguing about compatibility — the precedents are the argument.
260267

261268
Split a pull request that mixes a large mechanical sweep with changes that need judgement. The two
262269
halves have different review costs: a tool's output — an OpenRewrite run, a formatter pass, a
@@ -313,7 +320,7 @@ edits that follow it, so a reviewer can tell which hunks a human actually judged
313320
- Running a documented command proves it executes, not that it does what the text claims. If the
314321
text promises an effect — filtering, failing, producing a value — measure that effect.
315322
- Report what you actually observed. If a run was flaky, say so and show both runs.
316-
- **What the characterization made fail is the minimum the fix has to cover.** A `CHANGES.txt`
323+
- **What the characterization made fail is the minimum the fix has to cover.** A `CHANGELOG.md`
317324
entry that has to disclaim part of it — "not covered until X migrates" — is the tell that the fix
318325
sits too high. Count the failing sites, then enumerate the callers of the shared primitive one
319326
level down. On GITHUB-2830 that primitive was `Utils.toString`, located by its already-failsafe

‎CHANGELOG.md‎

Lines changed: 2269 additions & 0 deletions
Large diffs are not rendered by default.

‎CHANGES.txt‎

Lines changed: 0 additions & 2094 deletions
This file was deleted.

‎README.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ Documentation available at [TestNG's main web site](https://testng.org). Visit [
1212
> **Note for contributors:** Building TestNG from source requires JDK 21 or higher. See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for details.
1313
1414
### Release Notes
15+
16+
[CHANGELOG.md](CHANGELOG.md) covers every release, back to 0.2. The announcements below carry each
17+
release as it was sent to the [testng-users](https://groups.google.com/g/testng-users) mailing list,
18+
where a single release is otherwise hard to find.
19+
1520
* [7.12.0](https://groups.google.com/g/testng-users/c/KLK7I7jagTQ)
1621
* [7.11.0](https://groups.google.com/g/testng-users/c/2z_tdavjZbA)
1722
* [7.10.0](https://groups.google.com/g/testng-users/c/6DmFaKUjIxY)

‎docs/RELEASE_PROCESS.md‎

Lines changed: 170 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,20 @@ TestNG uses GitHub Actions to automate the release process. The workflow:
6363
│ │
6464
└───────────┬───────────────┘
6565
▼
66-
┌───────────────────────┐
67-
│ Artifacts on Maven │
68-
│ Central (~30 minutes) │
69-
└───────────────────────┘
66+
┌─────────────────────────┐
67+
│ Artifacts on Maven │
68+
│ Central (~30 minutes) │
69+
└─────────────────────────┘
7070
│
7171
▼
72-
┌───────────────────────┐
73-
│ Post-Release Tasks │
74-
│ - Create Git tag │
75-
│ - Send announcement │
76-
│ - Update README │
77-
└───────────────────────┘
72+
┌─────────────────────────┐
73+
│ Post-Release Tasks │
74+
│ - Create Git tag │
75+
│ - Promote changelog │
76+
│ - Create GitHub release │
77+
│ - Send announcement │
78+
│ - Update README + docs │
79+
└─────────────────────────┘
7880
```
7981

8082
## Prerequisites
@@ -102,6 +104,10 @@ The following secrets must be configured in GitHub repository settings. The GitH
102104

103105
## Release Workflow
104106

107+
Releases are made from `master`, and only from `master`. The branches that carry a release tag
108+
further back, `release_7.5` above all, are history rather than a supported path: they explain how a
109+
tag such as 7.5.1 came about, and nothing is published from them today.
110+
105111
### Option 1: Automatic Release (Recommended)
106112

107113
This is the simplest approach - artifacts are automatically published to Maven Central without manual intervention.
@@ -113,7 +119,7 @@ This is the simplest approach - artifacts are automatically published to Maven C
113119
3. Click **"Run workflow"** button
114120
4. Select:
115121

116-
- **Branch**: `master` (or your release branch)
122+
- **Branch**: `master`
117123
- **Publishing type**: `AUTOMATIC`
118124

119125
5. Click **"Run workflow"**.
@@ -173,7 +179,7 @@ This approach uploads artifacts to Central Portal but waits for you to manually
173179
3. Click **"Run workflow"** button
174180
4. Select:
175181

176-
- **Branch**: `master` (or your release branch)
182+
- **Branch**: `master`
177183
- **Publishing type**: `USER_MANAGED`
178184

179185
5. Click **"Run workflow"**
@@ -313,35 +319,163 @@ After artifacts are published to Maven Central, complete these tasks:
313319

314320
### 1. Create Git Tag
315321

316-
Every release must be tagged in Git.
322+
Every release must be tagged in Git, and tagging comes first: the next step adds commits, and the
323+
tag has to point at the commit the artifacts were built from.
324+
325+
That commit is not always your local `HEAD`. `Publish to Maven Central` is a `workflow_dispatch`,
326+
and its checkout takes the head of the branch it was dispatched on, at the moment it was dispatched
327+
— while you reach this step half an hour later, once Central has synced, on a branch that may have
328+
moved. Read the commit off the run instead of assuming it:
329+
330+
Name the run outright. Picking the newest successful one would be a guess: a re-run, a
331+
`USER_MANAGED` run that staged but was never published, and a second dispatch of the same version
332+
are all successful runs, and all of them build a commit that declares this version.
317333

318334
```bash
319-
# Get the version number from the release
320335
VERSION="7.10.0" # Replace with actual version
321336

322-
# Create and push the tag
323-
git tag -a v${VERSION} -m "Release ${VERSION}"
324-
git push origin v${VERSION}
337+
# Find the run you dispatched and watched above; its id is also the last path
338+
# segment of its URL, .../actions/runs/<id>
339+
gh run list --repo testng-team/testng --workflow "Publish to Maven Central" --limit 5 \
340+
--json databaseId,headSha,conclusion,createdAt
341+
342+
RUN_ID="21236984900" # Replace with that run's id
343+
```
344+
345+
Two guards before the tag is written. The first is the run, the second the commit, and neither is
346+
implied by the other:
347+
348+
```bash
349+
gh run view "${RUN_ID}" --repo testng-team/testng --json conclusion --jq .conclusion \
350+
| grep -qx success || { echo "Run ${RUN_ID} did not succeed"; exit 1; }
351+
352+
RELEASE_COMMIT=$(gh run view "${RUN_ID}" --repo testng-team/testng --json headSha --jq .headSha)
353+
354+
git show "${RELEASE_COMMIT}:gradle.properties" | grep -qx "testng.version=${VERSION}" \
355+
|| { echo "Run ${RUN_ID} built $(git show "${RELEASE_COMMIT}:gradle.properties" \
356+
| grep '^testng.version='), not ${VERSION}"; exit 1; }
357+
358+
git log -1 --oneline "${RELEASE_COMMIT}"
359+
git tag -a "v${VERSION}" "${RELEASE_COMMIT}" -m "TestNG ${VERSION}"
360+
git push origin "v${VERSION}"
361+
```
362+
363+
**Note**: The tag is the version with a `v` in front, `v7.10.0`. The `v` belongs to the tag name and
364+
not to the version: the release is `7.10.0`, which is what `gradle.properties` and Maven Central
365+
carry. Prefixing marks a ref as a version at a glance, keeps release tags apart from every other
366+
ref, and is what the surrounding tooling expects — `npm version`, GoReleaser and Go modules all
367+
assume it, and the Semantic Versioning FAQ names it as the usual way to write a version tag.
368+
369+
Releases up to and including `7.12.0` are tagged without the prefix, so the first `v` tag compares
370+
against a bare one. Only that one link is mixed; the rest follow.
371+
372+
### 2. Promote the Changelog
373+
374+
`CHANGELOG.md` follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), so releasing turns
375+
the section contributors have been filling in into a dated one, and opens a fresh one for the
376+
version that comes next.
377+
378+
All of it happens on `master`, in a working copy, in one commit. The example below releases
379+
`7.10.0` and opens `7.11.0`.
380+
381+
```bash
382+
git checkout master
383+
git pull origin master
384+
385+
# Still holding RELEASE_COMMIT from step 1. Anything the changelog gained since that
386+
# commit is not in the release.
387+
git diff "${RELEASE_COMMIT}" HEAD -- CHANGELOG.md
388+
```
389+
390+
An empty diff means the whole `## [Unreleased]` section is what shipped, and the steps below apply
391+
to it as written. A non-empty one means a pull request merged while Central was syncing: its entries
392+
are under `## [Unreleased]` but its code is not in the artifacts. Dating the section wholesale would
393+
file that work under a version that never carried it, and remove it from the version that will —
394+
wrong in both directions, and invisible afterwards. Leave those entries under the new
395+
`## [Unreleased]` you open in step 2.1, and date only the rest.
396+
397+
#### 2.1 Date the section being released
398+
399+
At the top of `CHANGELOG.md`, turn this:
400+
401+
```markdown
402+
## [Unreleased]
403+
404+
Next release: 7.10.0.
405+
406+
### Added
407+
```
408+
409+
into this, using the date the artifacts reached Maven Central:
410+
411+
```markdown
412+
## [Unreleased]
413+
414+
Next release: 7.11.0.
415+
416+
## [7.10.0] - 2024-04-07
417+
418+
### Added
419+
```
420+
421+
The `### Added`, `### Changed` and other subsections stay where they are: they describe the release
422+
you just dated. The new `## [Unreleased]` starts empty, with only its `Next release:` line.
423+
424+
A release that had to be pulled keeps its section and gains a `[YANKED]` marker in the heading,
425+
with the reason on the line underneath:
426+
427+
```markdown
428+
## [7.10.0] - 2024-04-07 [YANKED]
429+
430+
Bad release: wrong internal version.
431+
```
432+
433+
#### 2.2 Update the link definitions
434+
435+
At the foot of `CHANGELOG.md`, point `[Unreleased]` at the tag you just pushed and add a line for
436+
the release itself. The base is the release this one follows in the commit graph, which for a
437+
release made from `master` is the one before it there:
438+
439+
```markdown
440+
[Unreleased]: https://github.com/testng-team/testng/compare/v7.10.0...HEAD
441+
[7.10.0]: https://github.com/testng-team/testng/compare/7.9.0...v7.10.0
325442
```
326443

327-
**Note**: The tag should point to the exact commit that was released.
444+
It is not the section above it in the file. Sections are ordered by release date, while a comparison
445+
link describes a Git delta, and the two orders part company for the maintenance releases already in
446+
the file: 7.5.1 was cut from a branch and shipped after 7.7.1, so its section sits between 7.8.0 and
447+
7.7.1 while its changes belong to the 7.5 line. That is why `[7.8.0]` compares against 7.7.1 rather
448+
than the section above it — taking 7.5.1 there would answer with 150 commits of divergence instead
449+
of the 25 that release delivered. Since releases now come from `master` only, a new entry cannot
450+
land in that position.
451+
452+
#### 2.3 Bump the version
453+
454+
In `gradle.properties`, set `testng.version` to the version you named in `Next release:`:
455+
456+
```properties
457+
testng.version=7.11.0
458+
```
328459

329-
### 2. Create GitHub Release
460+
#### 2.4 Commit and push
461+
462+
```bash
463+
git add CHANGELOG.md gradle.properties
464+
git commit -m "Bumping version for next release"
465+
git push origin master
466+
```
467+
468+
### 3. Create GitHub Release
330469

331470
1. Go to https://github.com/testng-team/testng/releases
332471
2. Click **"Draft a new release"**
333472
3. Select the tag you just created (`v7.10.0`)
334473
4. Set release title: `TestNG 7.10.0`
335-
5. Add release notes:
336-
337-
- Highlight major features
338-
- List bug fixes
339-
- Link to issues/PRs
340-
- Credit contributors
341-
474+
5. Add release notes, taken from the section you just dated in `CHANGELOG.md`. That file is the
475+
source; the release page repeats it rather than restating it in other words.
342476
6. Click **"Publish release"**
343477

344-
### 3. Send Release Announcement
478+
### 4. Send Release Announcement
345479

346480
Send an email to the TestNG users mailing list:
347481

@@ -382,24 +516,22 @@ Thanks to all contributors who made this release possible!
382516
[Your Name]
383517
```
384518

385-
### 4. Update README.md
519+
### 5. Link the Announcement from README.md
386520

387-
Update the version badge and links in README.md:
521+
The `### Release Notes` list in `README.md` points at one mailing list thread per release, because a
522+
single release is hard to find in the archive otherwise. Add the thread you just sent, at the top:
388523

389-
```bash
390-
# Edit README.md
391-
# Update version numbers in:
392-
# - Maven dependency example
393-
# - Gradle dependency example
394-
# - Download links
395-
# - Version badges
524+
```markdown
525+
* [7.10.0](https://groups.google.com/g/testng-users/c/<thread-id>)
526+
```
396527

528+
```bash
397529
git add README.md
398-
git commit -m "Update README for 7.10.0 release"
530+
git commit -m "Add 7.10.0 release notes link"
399531
git push origin master
400532
```
401533

402-
### 5. Update Documentation
534+
### 6. Update Documentation
403535

404536
If there are documentation changes:
405537

‎docs/WRITING_STYLE.md‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -325,9 +325,11 @@ without checking.
325325
- **Terminology replacements.** See step 2 of
326326
[Adding or changing a rule](#adding-or-changing-a-rule). The measurement kills it.
327327

328-
- **Checking `CHANGES.txt`.** It holds twenty years of contributor names and issue numbers. On its
329-
own it produced more than nine tenths of every finding in the repository. So `.vale.ini` covers
330-
`*.md` and `*.java` only.
328+
- **Checking `CHANGELOG.md`.** It holds twenty years of contributor names and issue numbers. On its
329+
own it produced more than nine tenths of every finding in the repository. `.vale.ini` covers
330+
`*.md` and `*.java`, so the changelog gets a `[**/CHANGELOG.md]` section of its own with an
331+
empty `BasedOnStyles`, which switches every style off for that one file and no other. The
332+
`**/` matters: the Gradle task passes absolute paths, which a bare `CHANGELOG.md` misses.
331333

332334
- **Keeping the rules on this page.** Tools do not reliably follow links, so the rules have to sit
333335
in the file the tool already reads. That is why they are in `AGENTS.md`.

0 commit comments

Comments
 (0)