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 push

Custom 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 = Documentation

When 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.