Skip to content

Regression: <...> inside inline code spans in commit subjects still break PR-body parsing, silently skipping a component release (manifest mode) #2801

@eungkyu

Description

@eungkyu

This is a regression / incomplete fix of #1659 (fixed by #1661). HTML tags inside inline code spans in commit subjects are intentionally left unescaped by htmlEscape, but they still break the PR-body re-parsing step, causing a component's release to be silently skipped.

Environment

  • release-please 17.6.0 (googleapis/release-please-action@v5)
  • Manifest mode, multi-package monorepo, include-component-in-tag: true

Root cause

  • src/changelog-notes/default.ts htmlEscape() escapes bare </>, but via the regex /[^`].*[^`]|`[^`]*`|<|>/g with match.length > 1 ? match : ... it leaves </> inside inline code spans untouched. So a subject like add `check --report <path>` keeps a raw <path> in the generated release-PR body.
  • On release creation, src/util/pull-request-body.ts extractMultipleReleases() re-parses the PR body with node-html-parser. It does not understand markdown backticks, so <path> is treated as an unclosed HTML tag and the following <details> block(s) get nested inside it and dropped from root.getElementsByTagName('details') — the exact mechanism described in HTML tags in commit messages can break PR message parsing, causing missing package releases #1659.
  • src/strategies/base.ts then logs Pull request contains releases, but not for component: <X> and skips that component: no git tag, no GitHub release, no publish — while the workflow still reports success.
  • The next release-please run can no longer find that component's release boundary (tag missing) and re-walks its entire history, producing a bogus major bump in the next release PR.

Steps to reproduce

  1. In a manifest repo with ≥2 packages, land a commit whose subject contains an inline code span with an HTML-tag-looking token, e.g. feat: add `--report <path>` flag (affecting multiple packages so it appears in multiple <details> blocks).
  2. Merge the release PR.
  3. Observe that the component carrying the 2nd occurrence is not released (no tag/release/publish), though the run succeeds.

Minimal reproduction

// npm i release-please@17.6.0
const { PullRequestBody } = require('release-please/build/src/util/pull-request-body.js');
const body = [
  ':robot: release', '---', '',
  '<details><summary>pkg-a: 1.0.0</summary>\n\n### Features\n\n* add `--report <path>` flag\n</details>',
  '<details><summary>pkg-b: 2.0.0</summary>\n\n### Features\n\n* add `--report <path>` flag\n</details>',
  '---', 'footer',
].join('\n');

console.log(PullRequestBody.parse(body).releaseData.map(d => d.component));
// => [ 'pkg-a' ]            <-- pkg-b silently dropped

console.log(PullRequestBody.parse(body.replaceAll('<path>', '&lt;path&gt;')).releaseData.map(d => d.component));
// => [ 'pkg-a', 'pkg-b' ]   <-- escaping the angle brackets fixes it

Real-world case: an 11-package release PR dropped exactly the package whose <details> block contained the 2nd <path> occurrence.

Suggested fix

Since the PR body is later HTML-parsed (not rendered as markdown), </> should be escaped even when inside code spans when generating the body. Alternatively, make extractMultipleReleases extract <details> blocks robustly so an unclosed tag in note text cannot corrupt the DOM and drop sibling blocks.

Metadata

Metadata

Assignees

Labels

priority: p2Moderately-important priority. Fix may not be included in next release.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions