Skip to content

Commit 96c1416

Browse files
OEvgenyEugene Olonov
andauthored
Changelog fragments, merged into CHANGELOG.md on a changelog-unreleased branch (#5934)
* Add changelog fragments, merged into CHANGELOG.md on the changelog-unreleased branch * Put issue references first in unreleased changelog entries * Add the changelog fragment of this pull request --------- Co-authored-by: Eugene Olonov <eugeneolonov@microsoft.com>
1 parent 506adb4 commit 96c1416

19 files changed

Lines changed: 2641 additions & 16 deletions
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Changed
2+
3+
- 👷🏻 Changelog entries are now one file per pull request in `.changelog/`, merged into `CHANGELOG.md` on the [`changelog-unreleased`](https://github.com/microsoft/BotFramework-WebChat/blob/changelog-unreleased/CHANGELOG.md#unreleased) branch, in PR [#5934](https://github.com/microsoft/BotFramework-WebChat/pull/5934), by [@OEvgeny](https://github.com/OEvgeny)

‎.changelog/README.md‎

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Changelog fragments
2+
3+
Each pull request adds one file here, `<PR#>-<slug>.md`, with its changelog entries. Pull requests no longer edit `CHANGELOG.md`, so they do not conflict with each other.
4+
5+
On every push to `main`, a workflow recreates the `changelog-unreleased` branch from `main`, plus one commit that merges all fragments into the `[Unreleased]` section of [`CHANGELOG.md`](https://github.com/microsoft/BotFramework-WebChat/blob/changelog-unreleased/CHANGELOG.md#unreleased) and deletes them. When there are no fragments, the branch is the same as `main`, and its "Built from" line, if any, is left as it was. The workflow always pushes, so it also recreates the branch after it was deleted.
6+
7+
## Merging back
8+
9+
From time to time, a maintainer moves the entries into `CHANGELOG.md` on `main` by merging back a copy of the branch:
10+
11+
```sh
12+
git fetch origin
13+
git push origin origin/changelog-unreleased:refs/heads/<user>/changelog-merge-back
14+
```
15+
16+
Then open a pull request from `<user>/changelog-merge-back` and squash-merge it. Do not open it from `changelog-unreleased` itself: the workflow pushes it with `GITHUB_TOKEN`, which does not run the required checks, and rebuilds it on every push to `main`, which resets approvals. If a pull request from `changelog-unreleased` is merged anyway, GitHub deletes the branch, and the resulting push to `main` recreates it right away, so the "Show most recent changelog" link keeps working.
17+
18+
The copy may fall behind `main`: the check still passes, and fragments merged since then stay for the next merge-back.
19+
20+
## Adding an entry
21+
22+
After `gh pr create`, run:
23+
24+
```sh
25+
node ./scripts/changelog/new.mjs --issue 5734 "animation cards showing an empty video player for GIF URLs with a query"
26+
```
27+
28+
It writes `.changelog/5930-fix-animated-image-formats.md`, using the number and the author of the pull request, and the branch name:
29+
30+
```md
31+
### Fixed
32+
33+
- Fixed [#5734](https://github.com/microsoft/BotFramework-WebChat/issues/5734), animation cards showing an empty video player for GIF URLs with a query, in PR [#5930](https://github.com/microsoft/BotFramework-WebChat/pull/5930), by [@OEvgeny](https://github.com/OEvgeny)
34+
```
35+
36+
| Option | Default |
37+
| ----------------------------------------------------------- | ----------------------------- |
38+
| `--section added\|changed\|removed\|fixed` | `fixed` |
39+
| `--issue <issue#>`, repeatable | none |
40+
| `--marker breaking\|known-issue\|experimental\|sample\|dev` | none |
41+
| `--pr <PR#>` | `gh pr view` |
42+
| `--author <login>` | `gh pr view` |
43+
| `--slug <slug>` | branch name without `<user>/` |
44+
45+
## Format
46+
47+
A fragment is a small changelog: `### Added`, `### Changed`, `### Removed`, or `### Fixed`, each with one or more entries. Nested bullets are indented by 3 spaces per level, the check fails on other indentation. Entries are published as written, so edit the file to add entries or sections.
48+
49+
- Fixed, with issues: `- Fixed [#1](…) and [#2](…), <what was wrong>, in PR [#3](…), by [@user](…)`
50+
- Fixed, without issue: `- Fixed <what was wrong>, in PR [#3](…), by [@user](…)`
51+
- Added, changed, or removed, with issues: `- Added [#1](…), <what>, in PR [#3](…), by [@user](…)`
52+
- Added, changed, or removed, without issue: `- <Scope>: added <what>, in PR [#3](…), by [@user](…)`
53+
- Markers go first: 💥 breaking changes, 🐛 known issues, 🧪 experimental changes, 🏫 samples, 👷🏻 development environment changes
54+
55+
Pull requests that only change the development environment can add a 👷🏻 entry, or be labeled `no-changelog` instead.
56+
57+
## Checks
58+
59+
The "Changelog" check of a pull request passes for:
60+
61+
- A change: it adds `.changelog/<PR#>-<slug>.md` with its own number, or is labeled `no-changelog`. It must not edit the `[Unreleased]` section of `CHANGELOG.md` or delete fragments.
62+
- A merge-back of `changelog-unreleased`: it deletes fragments, adds none, changes no other files, and its `CHANGELOG.md` is exactly the one of `main` with the deleted fragments merged in, except for the "Built from" line.
63+
- A hand edit by a maintainer, labeled `changelog-edit`: it may edit `CHANGELOG.md` and delete fragments, for example a release or a typo fix in merged-back entries. Fragments are still validated.
64+
65+
A pull request may also edit the fragment of another pull request, for example to fix a typo before it is merged back.
66+
67+
## Scripts
68+
69+
| Command | What it does |
70+
| ------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
71+
| `node ./scripts/changelog/new.mjs` | Writes the fragment of a pull request |
72+
| `node ./scripts/changelog/consume.mjs` | Merges the fragments into `CHANGELOG.md` in the order they were merged, deletes them |
73+
| `node ./scripts/changelog/check.mjs --pr <PR#>` | Checks a pull request against `origin/main`, prints its entries |
74+
| `node ./scripts/changelog/publish.mjs` | Recreates and force-pushes `changelog-unreleased`, used by the workflow |
75+
| `node ./scripts/changelog/release.mjs <version> <date>` | Merges the fragments, then moves `[Unreleased]` into a new release, label it `changelog-edit` |
76+
| `node --test 'scripts/changelog/*.spec.mjs'` | Runs the tests |
77+
78+
Except for `new.mjs`, the scripts read fragments from Git commits, not from the working tree. They need Node.js 22.18, 24.2, or later.
79+
80+
Only the "Publish changelog-unreleased" workflow writes the `changelog-unreleased` branch, and it always force-pushes: do not protect the branch against force pushes.

‎.github/PULL_REQUEST_TEMPLATE.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
## Changelog Entry
66

7-
<!-- Please paste your new entry from CHANGELOG.MD here. Entry is not required for work only related to development purposes. -->
7+
<!-- Please paste the entries of your `.changelog/<PR#>-<slug>.md` here, see `.changelog/README.md`. Entry is not required for work only related to development purposes, label the pull request `no-changelog` instead. Do not edit `CHANGELOG.md`, maintainers label hand edits `changelog-edit`. -->
88

99
## Description
1010

@@ -23,7 +23,7 @@
2323
<!-- For bugs, add the bug repro as a test. Otherwise, add tests to futureproof your work. -->
2424

2525
- [ ] I have added tests and executed them locally
26-
- [ ] I have updated `CHANGELOG.md`
26+
- [ ] I have added `.changelog/<PR#>-<slug>.md`
2727
- [ ] I have updated documentation
2828

2929
## Review Checklist
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: Publish changelog-unreleased
2+
3+
# On every push to main, recreates the "changelog-unreleased" branch from the pushed commit: one commit that merges
4+
# .changelog/*.md into the "[Unreleased]" section of CHANGELOG.md and deletes them, or the pushed commit itself when
5+
# there are no fragments.
6+
#
7+
# To merge the entries back into main, push a copy of the branch and open the pull request from the copy, see
8+
# .changelog/README.md: pushes made with GITHUB_TOKEN do not run checks, and this workflow rewrites the branch.
9+
#
10+
# Only this workflow writes the branch, always with a force push: do not protect it against force pushes.
11+
12+
on:
13+
push:
14+
branches:
15+
- main
16+
17+
workflow_dispatch: {}
18+
19+
concurrency:
20+
# The latest run rebuilds everything, older runs can be cancelled.
21+
cancel-in-progress: true
22+
group: changelog-unreleased
23+
24+
defaults:
25+
run:
26+
shell: bash
27+
28+
env:
29+
node-version: 24
30+
31+
permissions: {}
32+
33+
jobs:
34+
publish:
35+
# Not on forks, which would publish their own branch.
36+
if: github.repository == 'microsoft/BotFramework-WebChat' && github.ref == 'refs/heads/main'
37+
name: Publish changelog-unreleased
38+
runs-on: ubuntu-latest
39+
40+
permissions:
41+
contents: write # To force-push the changelog-unreleased branch.
42+
43+
steps:
44+
- uses: actions/checkout@v4
45+
with:
46+
fetch-depth: 0 # Entries are ordered by the time their fragment was merged.
47+
48+
- name: Use Node.js ${{ env.node-version }}
49+
uses: actions/setup-node@v4
50+
with:
51+
node-version: ${{ env.node-version }}
52+
cache: npm
53+
54+
- run: npm clean-install --ignore-scripts --strict-peer-deps
55+
56+
- name: Merge .changelog/ into CHANGELOG.md and push to changelog-unreleased
57+
env:
58+
GIT_AUTHOR_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
59+
GIT_AUTHOR_NAME: github-actions[bot]
60+
GIT_COMMITTER_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
61+
GIT_COMMITTER_NAME: github-actions[bot]
62+
run: node ./scripts/changelog/publish.mjs --branch changelog-unreleased | tee --append "$GITHUB_STEP_SUMMARY"
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: Pull request changelog
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
8+
# "labeled" and "unlabeled" for the "changelog-edit" and "no-changelog" labels.
9+
types:
10+
- labeled
11+
- opened
12+
- reopened
13+
- synchronize
14+
- unlabeled
15+
16+
defaults:
17+
run:
18+
shell: bash
19+
20+
env:
21+
node-version: 24
22+
23+
permissions:
24+
contents: read
25+
26+
jobs:
27+
changelog:
28+
name: Changelog
29+
runs-on: ubuntu-latest
30+
31+
steps:
32+
- uses: actions/checkout@v4
33+
with:
34+
fetch-depth: 0 # To compare with the base, and to order entries by the time their fragment was merged.
35+
36+
- name: Use Node.js ${{ env.node-version }}
37+
uses: actions/setup-node@v4
38+
with:
39+
node-version: ${{ env.node-version }}
40+
cache: npm
41+
42+
- run: npm clean-install --ignore-scripts --strict-peer-deps
43+
44+
- name: Run unit tests of scripts/changelog
45+
run: node --test 'scripts/changelog/*.spec.mjs'
46+
47+
- name: Check the changelog and preview the entries
48+
env:
49+
CHANGELOG_EDIT: ${{ contains(github.event.pull_request.labels.*.name, 'changelog-edit') }}
50+
NO_CHANGELOG: ${{ contains(github.event.pull_request.labels.*.name, 'no-changelog') }}
51+
PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
52+
run: |
53+
# HEAD is the pull request merged into the base, its first parent is the base.
54+
OPTIONS=(--base HEAD^1 --pr "$PULL_REQUEST_NUMBER")
55+
56+
[[ "$CHANGELOG_EDIT" == true ]] && OPTIONS+=(--changelog-edit)
57+
[[ "$NO_CHANGELOG" == true ]] && OPTIONS+=(--no-changelog)
58+
59+
if node ./scripts/changelog/check.mjs "${OPTIONS[@]}" --output "$RUNNER_TEMP/preview.md" 2> "$RUNNER_TEMP/error.txt"
60+
then
61+
tee --append "$GITHUB_STEP_SUMMARY" < "$RUNNER_TEMP/preview.md"
62+
else
63+
# Also on the summary page, where contributors look first.
64+
cat "$RUNNER_TEMP/error.txt" >&2
65+
printf '## Changelog\n\n~~~text\n%s\n~~~\n' "$(cat "$RUNNER_TEMP/error.txt")" >> "$GITHUB_STEP_SUMMARY"
66+
exit 1
67+
fi

‎AGENTS.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export { MyComponentPropsSchema, type MyComponentProps };
110110

111111
- Run new test and all of them must be green
112112
- Run `npm run precommit` to make sure it pass all linting process
113-
- Add changelog entry to `CHANGELOG.md`, follow our existing format
113+
- Add changelog entries to `.changelog/<PR#>-<slug>.md` with `node ./scripts/changelog/new.mjs` after creating the PR, do not edit `CHANGELOG.md` (only maintainers do, with the `changelog-edit` label), see `.changelog/README.md`
114114

115115
## Code review
116116

0 commit comments

Comments
 (0)