Skip to content

[feature] Render the text of a BREAKING CHANGE footer - #155

Merged
namolnad merged 2 commits into
mainfrom
feature/breaking-change-footer
Aug 14, 2026
Merged

[feature] Render the text of a BREAKING CHANGE footer#155
namolnad merged 2 commits into
mainfrom
feature/breaking-change-footer

Conversation

@namolnad

Copy link
Copy Markdown
Owner

0.5.0 learned to recognise a BREAKING CHANGE: footer, but only far enough to tag the commit. The footer's own words — the part telling somebody what to do about it — became a separate, unattached entry. Which is why 0.5.0's release notes needed a hand-written section afterwards.

What changes

A footer describes the commit rather than standing on its own, so it is now read as the commit's breaking change instead of as an entry:

  • its text rides with the entry it describes, and wrapped footers are joined
  • the entry earns the breaking tag exactly as a ! would, so chore: tidy analytics with a footer files under Breaking Changes
  • footers are read whichever style the subjects use, delimited included

RawLog carries the description alongside the sha and author it already carries, which keeps it available to a per-line component without the commit body having to survive entry extraction.

Printing it

<< breaking_change >> is a new format component:

- title: Breaking Changes
  tags: [breaking]
  format_string: ' - << tags >> << message >> — << breaking_change >>'
### Breaking Changes
 - |breaking||feat||api| drop the v1 endpoints — v1 is gone. Move to v2, which takes the same parameters and returns an envelope.
 - |breaking||chore| tidy analytics — the express_start event is gone
 - |breaking||feat||ui| restyle the header

That third entry has no footer, and its is dropped rather than left dangling. The rule is general, not specific to this component: a separator preceding a component which renders nothing is omitted, which also tidies << tags >> on an untagged commit. The separator opening a line is kept, being a bullet rather than a join — otherwise an untagged entry would lose its -.

Finch describes what it breaks

.finch/config.release.yml takes a Breaking Changes section. Run against commits shaped like this release cycle's:

### Breaking Changes
 - Adopt the Swift 6 language mode — building from source now requires a Swift 6.0 toolchain. - [PR #152]
 - Read Conventional Commits by default — commit messages are read as Conventional Commits. Projects on the original convention set commit_style: delimited. - [PR #153]

Had this existed a day earlier, 0.5.0's notes would have carried both breaking changes on their own.

Test plan

  • 44 tests, 0 failures. New coverage for footer extraction, the hyphenated BREAKING-CHANGE: spelling, footers under the delimited style, and that a footer no longer opens an entry.
  • The end-to-end conventional snapshot now includes a !-plus-wrapped-footer commit, a footer-only commit, and a breaking commit with no footer — the three cases that differ.
  • Ran the real binary over each shape, and over commits shaped like this cycle's, through Finch's own release config.
  • swiftformat --lint . clean.

🤖 Generated with Claude Code

0.5.0 learned to recognise a `BREAKING CHANGE:` footer, but only far enough to
tag the commit. The footer's own words — the part telling somebody what to do
about it — became a separate, unattached entry, so the release notes for that
very version had to be written by hand afterwards.

A footer describes the commit rather than standing on its own, so it is now
read as the commit's breaking change instead of as an entry: its text rides
with the entry it describes, wrapped footers are joined, and the entry earns
the `breaking` tag exactly as a `!` would. `RawLog` carries the description
alongside the sha and author it already carries, which keeps it available to a
per-line component without the body having to survive entry extraction.

`<< breaking_change >>` prints it. A commit with a footer reads

    - |breaking||feat||api| drop the v1 endpoints — v1 is gone. Move to v2.

and one without prints nothing where the description would be — along with the
separator introducing it, which would otherwise dangle. That rule is general
rather than specific to this component: a separator preceding a component which
renders nothing is dropped, which also tidies `<< tags >>` on an untagged
commit. The separator opening a line is kept, being a bullet rather than a join.

Footers are read whichever style the subjects use, so .finch/config.release.yml
takes a Breaking Changes section despite reading its own history as delimited.
Finch can now describe what it breaks in its own release notes, which is what
started this.
SwiftFormat wraps the brace of a multi-line condition onto its own line, which
SwiftLint's opening_brace rule then rejects — the two disagree, and only CI runs
both. Naming the condition sidesteps the disagreement and reads better than five
clauses in an if.
@namolnad
namolnad merged commit c0c8ddc into main Aug 14, 2026
3 checks passed
@namolnad
namolnad deleted the feature/breaking-change-footer branch August 14, 2026 15:48
namolnad added a commit that referenced this pull request Aug 14, 2026
SwiftFormat moves the brace of a multiline condition onto its own line;
SwiftLint's opening_brace rule rejects exactly that. Only CI runs both, so the
disagreement shows up as a failed lint job after `swiftformat --lint` has
passed locally — which is how it found #155.

Disabling the rule settles it in SwiftLint's favour. No existing file changes
shape, since none currently carries a wrapped condition.
namolnad added a commit that referenced this pull request Aug 14, 2026
A condition long enough for SwiftFormat to wrap is long enough to deserve a
name, so a SwiftLint custom rule now asks for one — the shape settled on in
#155.

Scoped to `if`: the multi-line `guard … else { return }` form reads fine and
appears across five files, so it is untouched. Nothing in Sources or Tests
matches today.

CI proved the rule fires before it landed, against a deliberate violation which
was then removed, since SwiftLint runs nowhere but CI and a green run would have
shown only that the config parses.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant