Skip to content

ci(release-pr): move the PR body to a file - #90

Merged
michen00 merged 1 commit into
mainfrom
ci/release-pr-body-file
Sep 16, 2026
Merged

michen00 merged 1 commit into
mainfrom
ci/release-pr-body-file

Conversation

@michen00

Copy link
Copy Markdown
Owner

Every release PR so far has shipped hard-wrapped prose — #83 is the current example. The cause is not GitHub and not the prose tooling; it is this workflow.

Why it happened

The body was an inline YAML block, and yamllint caps release-pr.yml at 100 columns. Those two rules are in direct conflict: the wrapping that keeps the workflow readable is exactly what GitHub renders as a <br> in a pull request body. Unwrapped in place, the longest paragraph is 208 characters.

The prose tooling was never going to catch this either, on two counts. It was broken until #89, and even working it skips bot-authored pull requests by design — a bot's body comes from a template the pull request cannot change, so rewriting it there gets undone on the next release. Upstream is right about that, which is why the fix belongs here.

Why a file rather than a lint exemption

Adding release-pr.yml to yamllint's ignore list would trade a real column check on a 340-line release-critical workflow for prose formatting. That is the wrong direction.

A file gives up nothing. body-path takes precedence over body, and the body becomes a Markdown document that prettier, markdownlint and the unwrap hook already run over — the same tooling the prose is being fixed to satisfy. The template stops being a special case.

Details worth knowing

  • Substitution, not interpolation. body-path reads the file literally, so @RELEASE_TAG@ is replaced by sed. The tag has already passed parse-version.sh, so it is three dot-separated runs of digits and carries no sed metacharacter.
  • MD041. The rule wants a top-level heading on line 1, which is a whole-document rule applied to a fragment — a good illustration of why file linters need curating before they are pointed at PR bodies. Handled the way cliff.toml already handles its own header fragment: a markdownlint-configure-file directive, dropped at render time so lint scaffolding never reaches the pull request.
  • One correction. The first bullet claimed the run "refreshes CHANGELOG.md unreleased section". It does not — stamp-changelog.sh stamps the pending version as its own section, and the difference between those two operations is what this workflow's own comments exist to explain. Flagging it separately since it is a content change rather than a formatting one; say the word and I will drop it.

Test plan

  • The render step was extracted from the YAML and executed with RELEASE_TAG=v0.1.2. Asserted on the output: directive stripped (0 occurrences), no @RELEASE_TAG@ left unsubstituted, no leading blank line, and unwrap-markdown-prose --fail-on-change clean — so the rendered body carries no manual line breaks.
  • prettier, markdownlint and the unwrap hook all pass on the new file, which is the point of moving it.
  • actionlint clean, yamllint clean, make check green.

The end-to-end proof is the next release PR: its body should arrive as four paragraphs rather than eleven lines.

🤖 Generated with Claude Code

Every release PR has shipped hard-wrapped prose, because the body was
an inline YAML block and yamllint caps this file at 100 columns. The
two rules were in direct conflict: the wrapping that keeps the
workflow readable is what GitHub renders as a <br> in a pull request
body, so the release PR reached its reader ragged.

Exempting release-pr.yml from the column rule would trade a real lint
on a 340-line release-critical workflow for prose formatting. A file
gives up nothing: `body-path` takes precedence over `body`, and the
body becomes a Markdown document that prettier, markdownlint and the
unwrap hook already run over -- the same tooling the prose is being
fixed to satisfy.

The tag is substituted rather than interpolated, since `body-path`
reads the file literally. It has already passed parse-version.sh, so
it is three dot-separated runs of digits and carries no sed
metacharacter.

MD041 wants a top-level heading on line 1, which is a whole-document
rule applied to a fragment. Handled the way cliff.toml already handles
its own header: a markdownlint-configure-file directive, dropped at
render time so lint scaffolding does not reach the pull request.

Also corrects the first bullet. It claimed the run refreshes the
Unreleased section; stamp-changelog.sh stamps the pending version as
its own section, and the difference between those two is what this
workflow's own comments exist to explain.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Render release PR bodies from a Markdown template

🐞 Bug fix ⚙️ Configuration changes 🕐 10-20 Minutes

Grey Divider

AI Description

• Moves release PR prose into a separately linted Markdown template.
• Renders release tags before passing the body to the pull-request action.
• Corrects changelog wording to describe version-section stamping.
Diagram

graph TD
  TAG["Resolved tag"] --> RENDER["Render body"] --> OUTPUT["Rendered Markdown"] --> ACTION["Create PR action"] --> PR["Release PR"]
  TEMPLATE["Body template"] --> RENDER
Loading
High-Level Assessment

The file-backed template is the strongest approach because it preserves yamllint coverage for the release-critical workflow while allowing existing Markdown tooling to format and validate the body. Keeping the body inline would require a lint exemption or retain hard wrapping, while direct action interpolation is unavailable with body-path; the focused substitution step is therefore appropriate.

Files changed (2) +41 / -16

Bug fix (1) +28 / -16
release-pr.ymlRender and consume the release PR body file +28/-16

Render and consume the release PR body file

• Adds a workflow step that substitutes the validated release tag, removes lint-only scaffolding and leading blank lines, and exports the rendered temporary path. Replaces the hard-wrapped inline body with create-pull-request's body-path input.

.github/workflows/release-pr.yml

Other (1) +13 / -0
release-pr-body.mdAdd a lintable release PR body template +13/-0

Add a lintable release PR body template

• Introduces the release PR prose as standalone Markdown with release-tag placeholders. It includes an MD041 exception for fragment linting and corrects the changelog description to say the release is stamped as its own section.

.github/release-pr-body.md

@codecov-commenter

codecov-commenter commented Sep 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 51.57%. Comparing base (c37364e) to head (b8ca42b).

Additional details and impacted files
@@           Coverage Diff           @@
##             main      #90   +/-   ##
=======================================
  Coverage   51.57%   51.57%           
=======================================
  Files          31       31           
  Lines        2092     2092           
=======================================
  Hits         1079     1079           
  Misses       1013     1013           
Flag Coverage Δ
integration 0.97% <ø> (ø)
unit 50.98% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can group findings by type and pick your Finding display, from Minimal to Full

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@michen00
michen00 merged commit 8b0303c into main Sep 16, 2026
13 checks passed
@michen00
michen00 deleted the ci/release-pr-body-file branch September 16, 2026 00:50
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.

2 participants