From f7cafc50f44216d90f0c266b6c0a6874bb285a9b Mon Sep 17 00:00:00 2001 From: 4gray Date: Thu, 27 Aug 2026 11:02:49 +0200 Subject: [PATCH] fix(release): keep the suggested Reddit title within its own limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit bounded the post body but left the title unbounded, and Reddit caps titles separately and far tighter. Five highlights at the validated 60-character maximum produce a 328-character title against the 300-character cap, while the body stays nowhere near its own limit — so the renderer returned a draft that looked fine and could not be submitted. The title now names as many highlights as fit and counts the rest. 195 tests passing. Co-Authored-By: Claude Fable 5 --- docs/architecture/release-pipeline.md | 7 +++- tools/release/release-announcements.mjs | 44 ++++++++++++++++++-- tools/release/release-announcements.test.mjs | 32 ++++++++++++++ 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/docs/architecture/release-pipeline.md b/docs/architecture/release-pipeline.md index 05beb6f2c..a48b2c75b 100644 --- a/docs/architecture/release-pipeline.md +++ b/docs/architecture/release-pipeline.md @@ -83,8 +83,11 @@ Highlights drive three behaviors: longer post. If the breaking changes alone cannot fit the 4096-character limit, the render fails with an actionable error rather than dropping one. - **Reddit** gives each one an `## Highlights` subsection, with the remaining - changes grouped below. Bounded the same way at Reddit's 40,000-character - post limit — this repository's accumulated notes already render ~37,000 — + changes grouped below. Its suggested title names as many highlights as + Reddit's separate 300-character title cap allows and counts the rest — five + highlights at the validated 60-character maximum already overshoot it while + the body stays nowhere near its own limit. The body is bounded the same way + at Reddit's 40,000-character post limit — this repository's accumulated notes already render ~37,000 — dropping from the tail of the grouped list, which is ordered breaking → feature → fix → perf so the least consequential go first. A breaking change is never dropped there either. diff --git a/tools/release/release-announcements.mjs b/tools/release/release-announcements.mjs index f0074146a..21a1cdd11 100644 --- a/tools/release/release-announcements.mjs +++ b/tools/release/release-announcements.mjs @@ -27,6 +27,45 @@ export const TELEGRAM_MESSAGE_LIMIT = 4096; */ export const REDDIT_POST_LIMIT = 40000; +/** + * Reddit caps a post title separately from its body, and far tighter: five + * highlights at the validated 60-character maximum already overshoot it while + * the body stays nowhere near its own limit. + */ +export const REDDIT_TITLE_LIMIT = 300; + +/** + * Names as many highlights as the title limit allows, counting the rest. + * + * @param {string} version + * @param {object[]} highlights + * @returns {string} + */ +export function buildRedditTitle(version, highlights) { + const prefix = `IPTVnator v${version}`; + + if (highlights.length === 0) { + return `${prefix} released`; + } + + const names = highlights.map((note) => note.highlight); + + for (let visible = names.length; visible > 0; visible -= 1) { + const hidden = names.length - visible; + const title = `${prefix} — ${names.slice(0, visible).join(', ')}${ + hidden > 0 ? `, and ${hidden} more` : '' + }`; + + if (title.length <= REDDIT_TITLE_LIMIT) { + return title; + } + } + + // Unreachable while the highlight cap stays well under the title limit, + // but a title is never worth failing a release over. + return `${prefix} released`; +} + const TYPE_EMOJI = { breaking: '⚠️', feature: '✨', @@ -179,10 +218,7 @@ export function renderRedditPost(notes, { version }) { return null; } - const title = - highlights.length > 0 - ? `IPTVnator v${version} — ${highlights.map((note) => note.highlight).join(', ')}` - : `IPTVnator v${version} released`; + const title = buildRedditTitle(version, highlights); const buildPost = (visibleRest, droppedCount) => { const blocks = [`Suggested title: ${title}`, '---']; diff --git a/tools/release/release-announcements.test.mjs b/tools/release/release-announcements.test.mjs index 5c89242c3..66f5a4b9f 100644 --- a/tools/release/release-announcements.test.mjs +++ b/tools/release/release-announcements.test.mjs @@ -2,7 +2,9 @@ import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { + buildRedditTitle, REDDIT_POST_LIMIT, + REDDIT_TITLE_LIMIT, renderRedditPost, renderTelegramPost, splitHighlights, @@ -235,6 +237,36 @@ describe('renderRedditPost', () => { assert.doesNotMatch(post, /Internal churn/); }); + it('keeps the suggested title inside the Reddit title limit', () => { + // Five highlights at the validated 60-character maximum overshoot the + // 300-character title cap while the body stays far below its own. + const notes = Array.from({ length: 5 }, (_, index) => + note({ + highlight: `H${index}`.padEnd(60, 'x'), + sourcePath: `.changes/feature-${index}.md`, + }) + ); + const post = renderRedditPost(notes, { version: '0.24.0' }); + const title = post.split('\n')[0].replace('Suggested title: ', ''); + + assert.ok( + title.length <= REDDIT_TITLE_LIMIT, + `title is ${title.length} characters` + ); + assert.match(title, /, and \d+ more$/); + }); + + it('names every highlight when they fit', () => { + assert.equal( + buildRedditTitle('0.24.0', [ + { highlight: 'Up Next rail' }, + { highlight: 'Faster imports' }, + ]), + 'IPTVnator v0.24.0 — Up Next rail, Faster imports' + ); + assert.equal(buildRedditTitle('0.24.0', []), 'IPTVnator v0.24.0 released'); + }); + it('stays inside the Reddit post limit and says what it dropped', () => { const notes = [ note({ highlight: 'Up Next rail' }),