Follow-up to #266: make the pull request template actually reach GitHub - #272
Merged
Conversation
#266 adds `-T/--template` and a `pr_template` config option, which are the right surface -- the names and the precedence are kept exactly as the contributor designed them. The mechanism cannot work, though, and I could not find a variant of it that does. The command already passes `--body <issue url>`, and #266 appends `--template` next to it. `gh` refuses that pair outright: `--template` is not supported when using `--body` or `--body-file` So every use of the new flag fails, and setting `pr_template` in config breaks `issue pr` on every invocation rather than only when the flag is passed. Dropping `--body` to make room for `--template` -- the obvious repair -- is worse. `gh` only consults a template when it is running interactively; without a body a non-TTY caller gets must provide `--title` and `--body` (or `--fill` ...) when not running interactively and no pull request at all. That would trade a broken flag for a command broken in CI, scripts, and agents. Handing `gh` a temporary file that already contains the template fails the same way, because the problem is the missing `--body`, not the file's contents. So the template is read here and folded into the body we already send, with the issue URL appended after it. The URL is what Linear matches on to attach the pull request to its issue, so it has to survive; putting it last leaves the template's prose as the first thing a reviewer reads. Every existing flag keeps working, because the argv shape is unchanged. Reading the file ourselves means we own its failures, and per CLAUDE.md an explicitly requested template that cannot be used is an error rather than a silent fallback to a URL-only body -- otherwise the user gets a pull request quietly missing the content they asked for. Missing paths, directories, non-regular files and unreadable files all produce a message naming the path. NUL bytes are rejected too: `Deno.readTextFile` does not refuse binary input, it substitutes U+FFFD and keeps the NULs, which `Deno.Command` then rejects with a bare TypeError that never mentions the file. One deliberate surface change: #266's description suggests `-T ""` to override a configured default. That worked only because an empty string happened to be falsy. It is now an explicit `--no-template` flag, and `-T ""` errors with a suggestion pointing at it. The generated skill docs under skills/ are left alone; they are produced from an installed binary out of band and are already stale on trunk.
schpetbot
marked this pull request as ready for review
September 1, 2026 00:27
schpetbot
force-pushed
the
oss-pr-lander/266
branch
from
September 1, 2026 00:27
af296fc to
fb0b0d9
Compare
Collaborator
Author
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.
Follow-up to #266. Draft until #266 lands — this branch currently contains @maparent's commit too, and will be rebased down to just the delta once #266 is merged.
#266 adds
-T/--templateand apr_templateconfig option. The surface is right and is kept exactly as designed — same flag letter, same config key, same precedence. The mechanism can't work, though, and I couldn't find a variant of it that does.The feature currently fails on every invocation
issue pralready passes--body <issue url>, and #266 appends--templatebeside it.ghrefuses that pair:Worse,
pr_templatein config applies unconditionally, so setting it breaksissue prentirely rather than only when the flag is passed.Why the obvious repair is worse
Dropping
--bodyto make room for--templatelooks right, butghonly consults a template when it's running interactively. Without a body, a non-TTY caller gets:…and no pull request at all. That trades a broken flag for a command broken in CI, scripts, and agents. Handing
gha temp file that already contains the template fails identically — the problem is the missing--body, not the file's contents.What this does instead
Read the template here and fold it into the body we already send, with the issue URL appended:
The URL has to survive — it's what Linear matches on to attach the PR to its issue. Putting it last leaves the template's prose as the first thing a reviewer reads. No
--templateflag ever reachesgh, so every existing flag (--web,--draft,--base,--head,--title) keeps working unchanged.Also fixed
<repo-root>/.linear.tomlfrom any subdirectory, but its relative value was resolved against the cwd — so the documentedpr_template = ".github/pull_request_template.md"worked at the root and failed everywhere below it. A config value now resolves against the config file that supplied it; a path typed on the command line stays cwd-relative, which is what a shell user expects.getOption()drops values that fail to parse, sopr_template = 123would quietly produce a URL-only PR. Now aValidationError, following the existingresolveIssueSortprecedent — per CLAUDE.md, explicit input works or errors.Deno.readTextFiledoesn't refuse binary input — it substitutes U+FFFD and keeps NULs, whichDeno.Commandthen rejects with a bareTypeErrorthat never names the file.One deliberate surface change
#266's description suggests
-T ""to override a configured default. That worked only because an empty string is falsy. It's now an explicit--no-templateflag;-T ""gets cliffy'sMissing value for option "--template", and--template x --no-templateis rejected before anything runs.Verification
573 tests pass;
deno task check,deno lint,deno fmt --checkclean. Newtest/commands/issue/issue-pull-request.test.tscovers body composition and every template-read rejection;test/config.test.tscovers strict validation and config-relative resolution.QA'd against a
ghshim capturing real argv: config default,--no-template,--web+ template, missing/directory/binary/empty template,--template+--no-template, and a relative config path from a nested subdirectory — confirming the composed body and that--templatenever reachesgh.Generated skill docs under
skills/are untouched; they're produced from an installed binary out of band and are already stale on trunk (they don't list--idfrom #268 either). CI only verifies they generate, not that they're current.