feat: add --description-file to issue, MR, and work item commands
Description
--description had no file-based counterpart on any of the issue, merge request, or
work item commands. Anything longer than a line required
--description "$(cat file.md)", which strips trailing newlines, hits ARG_MAX on
large descriptions, and offers no non-interactive path at all: --description -
opens an editor, so it fails in CI.
Only glab mr note create / mr note update accepted a body from stdin, and only
glab release create --notes-file had a file flag. This brings the same capability
to descriptions.
Adds --description-file to:
| Command | Notes |
|---|---|
glab issue create |
also mutually exclusive with --template |
glab issue update |
|
glab mr create |
also mutually exclusive with --template |
glab mr update |
|
glab work-items create |
|
glab work-items update |
A path of - reads from standard input, matching the wording and behaviour of the
existing release create --notes-file.
Design notes
Both flags are wired through one shared helper, internal/cmdutils/description_file.go:
AddDescriptionFileFlag(cmd, resource)registers the flag and marks it mutually exclusive with--descriptionviaMarkFlagsMutuallyExclusive.ResolveDescriptionFile(ios, cmd)resolves the file (or stdin) into the--descriptionflag value.
Resolving into --description rather than into each command's own field means the six
call sites keep reading a single flag, and their existing Changed("description")
checks keep working whether the description came from a flag or a file. That matters
for the create commands, where Changed("description") drives whether the interactive
prompt is skipped.
Two deliberate choices:
- No shorthand.
-Fis the canonical shorthand for--outputviacmdutils.EnableJSONOutput, whichwork-items createandwork-items updateboth call, so-Fwould have collided there. - A file containing only
-is rejected rather than passed through, since it would otherwise be indistinguishable from the--description -editor sentinel and would silently launch an editor.
Not changed
project createandsnippet createalso take a free-form--descriptionbut are left for a follow-up; long-form descriptions there are rare.- An empty description file is a no-op rather than clearing the description, because
all six commands gate on
if description != "". Same as--description ""today.
Related Issues
No tracking issue — this was raised in review discussion. Happy to link one if it exists.
How has this been tested?
New unit tests for the helper in internal/cmdutils/description_file_test.go cover
reading a multi-line file, an empty file, stdin, a missing file, the --only file
rejection, the no-op case, and the mutual-exclusion error.
End-to-end command tests were added to work-items update and mr update
specifically, because those two exercise the different consumption shapes — a bound
opts field versus cmd.Flags().GetString — and both assert the file content reaches
the API call options.
make lint # 0 issues
make test # 3113 tests, 8 skipped
lefthook run pre-push # build, lint, test, check-generated, markdownlint, vale, lychee all passManually verified against the built binary:
# reads the file
glab issue update 42 --description-file description.md
# reads stdin
cat description.md | glab issue update 42 --description-file -
# rejects the conflict
$ glab issue update 42 --description x --description-file description.md
If any flags in the group [description description-file] are set none of the others can be
# reports a missing file clearly
$ glab issue update 42 --description-file nope.md
Failed to read description file: open nope.md: no such file or directory.