fix(orchestrator): auto-truncate release PR body when it exceeds provider limit#38
Merged
Conversation
…ider limit Adds release.RenderPreviewCompact (header + version table + footer, no per-package sections) and a renderBodyWithinLimit fallback in the orchestrator that picks the rendering size that fits. Three-tier fallback: 1. Full RenderPreview (the default; full release notes per package). 2. RenderPreviewCompact (when full body > MaxPRBodyBytes = 65536). 3. Hard byte-truncation with a trailing marker (last-resort safety net for releases with hundreds of packages where even the compact form blows past the limit). GitHub PR descriptions cap at 65,536 bytes; Gitea matches; GitLab allows ~1 MiB. We use the conservative GitHub value across all providers so a release plan that fits one provider fits all. Tests: - TestRenderPreviewCompact: verifies the compact form preserves the table, drops Released changes, drops the changeset body, keeps the consumed footer. - TestRenderPreviewCompact_EmptyPlan: nil and empty plan still produce the no-pending-changesets message. - TestRun_LongBodyFallsBackToCompact: builds a synthetic plan with 30 packages × 5KB changeset bodies (~150KB rendered) and verifies the orchestrator falls back to compact and the resulting body fits within MaxPRBodyBytes. Fixes #37.
Important: - Drop the unused today parameter from RenderPreviewCompact; the compact form has no per-package date-stamped CHANGELOG content, so today is dead weight on the signature. Update the call site in orchestrator + the existing tests. - Add a hard-truncation test (TestTruncateWithMarker_Tier3). The test directly exercises the tier-3 path with a small max so we don't have to build a release plan large enough to drive the orchestrator there organically. Covers the multi-byte rune case that motivated the UTF-8 boundary walk-back. Suggestions: - Walk back to a UTF-8 codepoint boundary on the hard-truncate path so a multi-byte rune never splits and renders as a U+FFFD replacement character on GitHub. Cheap to add; just imports unicode/utf8 and adds 3 lines. - Export the elided-notes marker as release.CompactElidedNotesMarker so downstream tooling that scrapes release-PR bodies has a stable detection target. - Extract truncation into a private truncateWithMarker helper and expose it via export_test.go so the test suite gets direct coverage of the bounds + UTF-8 logic without needing a synthetic 200-package fake plan.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #37. The always-open release PR's body now auto-truncates when its rendered length would exceed the provider's PR-body limit (65,536 chars on GitHub / Gitea, conservative bound on GitLab). Replaces a hard
422 Validation Failed: body is too longwith a graceful fallback to a compact form.What changed
internal/release/render.go: newRenderPreviewCompactfunction. Same header + version table + consumed footer asRenderPreview, but the per-package "Released changes" sections are elided with a pointer to each package'sCHANGELOG.md.internal/orchestrator/orchestrator.go: newMaxPRBodyBytesconstant (65536) andrenderBodyWithinLimithelper. Three-tier fallback: full → compact → hard byte-truncation with a trailing marker.Repro
The loglayer-go v2 cascade (loglayer/loglayer-go#61) shipped 27 packages at
:majorfrom a single multi-package changeset whose body was ~3.4 KB. 27 × 3.4 KB + table overhead = ~92 KB rendered, well over GitHub's 65,536-char cap. The release-pr workflow failed with the 422 error.After this PR, the same plan would render compact (~2 KB) and the workflow would succeed.
Test plan
go test -count=1 ./...passes.TestRenderPreviewCompact(verifies compact form preserves the table + footer, drops the per-package sections + changeset body).TestRenderPreviewCompact_EmptyPlan(empty / nil plans still render the no-pending-changesets message).TestRun_LongBodyFallsBackToCompact(synthetic 30-package × 5 KB body plan; verifies orchestrator falls back to compact and resulting body fits withinMaxPRBodyBytes).🤖 Generated with Claude Code