assemble-news
Collate news.d/ fragment files into NEWS.md under section headings (by default ## Breaking changes, ## New features, ## Bug fixes, ## Minor improvements) and delete consumed fragment files.
Pairs with check-news.yml, allowing pull requests to add fragment files under news.d/ instead of directly editing NEWS.md, eliminating merge conflicts between concurrent PRs.
Inputs
| Input | Type | Default | Description |
|---|---|---|---|
fragments-dir |
string | 'news.d' |
Directory containing fragment files (<slug>.<category>.md). |
news-file |
string | 'NEWS.md' |
Target NEWS.md file path. |
headings |
string | '' |
Category-to-heading map, as newline-separated category = Heading pairs. Empty keeps the built-in map. |
bullet-style |
string | '' |
List-marker character (-, *, or +) every inserted fragment bullet is normalized to. Empty auto-detects from news-file’s own first bullet, falling back to - when it has none; see Bullet-marker normalization for what counts as that first bullet. |
Example
# Copy to .github/workflows/assemble-news.yml in your repo.
name: Assemble NEWS.md
on:
workflow_dispatch:
jobs:
assemble:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Assemble news fragments
uses: Morrison-Lab/gha/.github/actions/assemble-news@v2
with:
fragments-dir: news.d
news-file: NEWS.md
- name: Commit assembled NEWS.md
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add NEWS.md news.d/
git diff --staged --quiet || git commit -m "chore(release): assemble news fragments into NEWS.md"
git pushCustom headings
The built-in map is breaking to Breaking changes; added and feature to New features; fixed and bug to Bug fixes; and changed, minor, deprecated, removed, and security to Minor improvements.
A repo whose NEWS.md uses a different taxonomy supplies its own:
- name: Assemble news fragments
uses: Morrison-Lab/gha/.github/actions/assemble-news@v2
with:
fragments-dir: news.d
headings: |
breaking = Breaking changes
added = New features
fixed = Bug fixes
infrastructure = Infrastructure
docs = DocumentationWhen set, headings replaces the built-in map rather than extending it, defining both the complete set of recognized categories and the order the headings are written in. Several categories may share a heading, which then takes the position of its first-listed category.
A # comments out a line only when it starts the line, so a heading may contain one (csharp = C# interop). A category may not contain a dot – it is a single segment of <slug>.<category>.md, and a dotted one would collate the same fragment twice whenever its suffix is also configured.
A fragment whose category is outside the active map fails the step, naming the offending files, rather than being skipped – the check runs before anything is consumed, so a mixed batch fails without deleting the valid fragments beside it.
Bullet-marker normalization
markdownlint’s MD004 rule defaults to style: consistent, which takes its required marker (-, *, or +) from the assembled file’s first bullet. Fragments are spliced in at the top of news-file, so a fragment authored with a different marker than the file’s dominant one would flip that requirement for every other bullet already in the file.
To prevent that, every inserted bullet’s top-of-line marker is normalized to one target, in this order: the bullet-style input when set; otherwise news-file’s own first existing bullet marker; otherwise -, when news-file has no bullet yet to take a style from (so fragments authored with different markers still collate under one consistent style even on a fresh file). Only the marker character is rewritten – the rest of each line, including nested-list indentation, is left untouched.
Detection considers only lines matching a marker followed by whitespace, and among those it skips a spaced thematic break rather than reading it as the first bullet – so a NEWS.md opening with - - - above a *-styled list normalizes to *. A run of fewer than three markers is a list item rather than a break, so an empty list item still sets the style. + is never treated as a break character, so + + + is a list at any length.
Two details of that test are the detector’s own rather than CommonMark’s, and both are deliberate:
- It tests only
-and*. The spec’s third break character,_, is never reached, because a_line is not a marker-followed-by-whitespace candidate in the first place. - It ignores leading indentation, where the spec caps a break at three spaces. A more deeply indented separator is indented-code or nested-list content, which should not set the whole file’s style either.
One consequence is worth knowing before hand-editing a NEWS.md: a marker alone on its line, with no trailing whitespace, is not a detection candidate at all, so it sets no style and the fall-back - applies. An empty list item therefore has to carry its trailing space to count (#746).
See assemble-news.yml in examples/ for the caller stub.