Add --json and --jq flags to gh pr create#13348
Conversation
Add --json and --jq flags to gh pr create so users can get structured output (id, url, number) instead of parsing the plain-text URL. - Add AddJSONAndJQFlags helper in cmdutil for commands where --template is already taken for a different purpose. - Make checkJSONFlags template-aware so the host command's --template flag is not confused with JSON output templating. - Add number field to CreatePullRequest GraphQL mutation. - Wire up flags in pr create with --dry-run mutual exclusion. - Export JSON at end of submitPR when Exporter is set. Fixes cli#11247
- Fix mutation closing brace indentation in queries_pr.go - Add SetFilter method to jsonExporter for test use - Add tty JSON output test (AC1: interactive flow) - Add --jq filter end-to-end test (AC3) - Add --json + --template coexistence test (AC5) - Add --web + --json mutual exclusion test
There was a problem hiding this comment.
Pull request overview
Adds structured output support to gh pr create by introducing --json and --jq flags, enabling callers to consume PR metadata (id/url/number) without parsing human-oriented output.
Changes:
- Extend the
CreatePullRequestGraphQL mutation response to includenumber. - Add
cmdutil.AddJSONAndJQFlags(no--template) and update JSON flag parsing to avoid colliding with host commands’ existing--templateflags. - Wire JSON exporting into
gh pr create, including--dry-run/--jsonmutual exclusion and suppression of plain URL output when JSON output is requested.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
api/queries_pr.go |
Adds number to the PR create mutation response fields. |
pkg/cmdutil/json_flags.go |
Introduces AddJSONAndJQFlags, adjusts JSON flag checking to optionally ignore --template, and adds a test helper setter for jq filtering. |
pkg/cmdutil/json_flags_test.go |
Adds coverage for the new AddJSONAndJQFlags helper across common scenarios and error cases. |
pkg/cmd/pr/create/create.go |
Adds exporter plumbing and output behavior changes for --json/--jq, plus --dry-run conflict handling. |
pkg/cmd/pr/create/create_test.go |
Adds PR create JSON output behavior tests and updates GraphQL mock responses to include Number. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| name: "nontty json output", | ||
| httpStubs: func(reg *httpmock.Registry, t *testing.T) { | ||
| reg.Register( | ||
| httpmock.GraphQL(`mutation PullRequestCreate\b`), | ||
| httpmock.GraphQLMutation(` | ||
| { "data": { "createPullRequest": { "pullRequest": { | ||
| "Number": 12, | ||
| "URL": "https://github.com/OWNER/REPO/pull/12" | ||
| } } } }`, | ||
| func(input map[string]interface{}) { | ||
| assert.Equal(t, "REPOID", input["repositoryId"]) | ||
| assert.Equal(t, "my title", input["title"]) | ||
| assert.Equal(t, "my body", input["body"]) | ||
| })) | ||
| }, | ||
| setup: func(opts *CreateOptions, t *testing.T) func() { | ||
| opts.TitleProvided = true | ||
| opts.BodyProvided = true | ||
| opts.Title = "my title" | ||
| opts.Body = "my body" | ||
| opts.HeadBranch = "feature" | ||
| exporter := cmdutil.NewJSONExporter() | ||
| exporter.SetFields(createOutputFields) | ||
| opts.Exporter = exporter | ||
| return func() {} | ||
| }, | ||
| expectedOut: "{\"id\":\"\",\"number\":12,\"url\":\"https://github.com/OWNER/REPO/pull/12\"}\n", | ||
| }, |
| name: "tty json output", | ||
| tty: true, | ||
| httpStubs: func(reg *httpmock.Registry, t *testing.T) { | ||
| reg.Register( | ||
| httpmock.GraphQL(`mutation PullRequestCreate\b`), | ||
| httpmock.GraphQLMutation(` | ||
| { "data": { "createPullRequest": { "pullRequest": { | ||
| "Number": 12, | ||
| "URL": "https://github.com/OWNER/REPO/pull/12" | ||
| } } } }`, | ||
| func(input map[string]interface{}) {})) | ||
| }, | ||
| setup: func(opts *CreateOptions, t *testing.T) func() { | ||
| opts.TitleProvided = true | ||
| opts.BodyProvided = true | ||
| opts.Title = "my title" | ||
| opts.Body = "my body" | ||
| opts.HeadBranch = "feature" | ||
| exporter := cmdutil.NewJSONExporter() | ||
| exporter.SetFields(createOutputFields) | ||
| opts.Exporter = exporter | ||
| return func() {} | ||
| }, | ||
| expectedOut: "{\"id\":\"\",\"number\":12,\"url\":\"https://github.com/OWNER/REPO/pull/12\"}\n", | ||
| expectedErrOut: "\nCreating pull request for feature into master in OWNER/REPO\n\n", | ||
| }, |
| name: "json output with pr template", | ||
| httpStubs: func(reg *httpmock.Registry, t *testing.T) { | ||
| reg.Register( | ||
| httpmock.GraphQL(`mutation PullRequestCreate\b`), | ||
| httpmock.GraphQLMutation(` | ||
| { "data": { "createPullRequest": { "pullRequest": { | ||
| "Number": 12, | ||
| "URL": "https://github.com/OWNER/REPO/pull/12" | ||
| } } } }`, | ||
| func(input map[string]interface{}) { | ||
| assert.Equal(t, "my title", input["title"]) | ||
| })) | ||
| }, | ||
| setup: func(opts *CreateOptions, t *testing.T) func() { | ||
| opts.TitleProvided = true | ||
| opts.BodyProvided = true | ||
| opts.Title = "my title" | ||
| opts.Body = "my body" | ||
| opts.HeadBranch = "feature" | ||
| opts.Template = "bug_fix.md" | ||
| exporter := cmdutil.NewJSONExporter() | ||
| exporter.SetFields(createOutputFields) | ||
| opts.Exporter = exporter | ||
| return func() {} | ||
| }, | ||
| expectedOut: "{\"id\":\"\",\"number\":12,\"url\":\"https://github.com/OWNER/REPO/pull/12\"}\n", | ||
| }, |
Address Copilot review feedback: mock responses for JSON output tests now include a realistic PR node ID instead of relying on the zero value, ensuring the id field mapping is validated end-to-end.
|
Hi @babakks -- PR #12622 has been inactive for ~2 months with review feedback unaddressed, so I've opened this PR to cover the same feature. This implementation incorporates all 7 items from your review of #12622 (indentation, godoc, test coverage, |
Summary
Add
--jsonand--jqflags togh pr createso callers can get structured output (id, url, number) instead of parsing a plain-text URL.Fixes #11247
Changes
api/queries_pr.gonumberfield to theCreatePullRequestGraphQL mutation response.pkg/cmdutil/json_flags.goAddJSONAndJQFlagshelper for commands where--template(or-t) is already taken by another flag. Registers--jsonand--jqwithout--template.withTemplateFlagparameter tocheckJSONFlagsso the host command's--templateflag is not confused with JSON output templating.SetFiltermethod tojsonExporterfor test use.pkg/cmd/pr/create/create.goExporterfield toCreateOptionsandcreateOutputFieldsvariable (id,url,number).AddJSONAndJQFlagsintoNewCmdCreate.--dry-run+--jsonmutual exclusion.submitPR, suppress plain URL and write JSON when Exporter is set.Tests
pkg/cmd/pr/create/create_test.go:TestJSONFields- canary test ensuring registered fields match expectations.tty json output- interactive flow outputs JSON to stdout, status to stderr (AC1).nontty json output- non-interactive flow outputs JSON (AC2).json output with jq filter---jq .numberoutputs just the number (AC3).dry-run and json- mutual exclusion error (AC4).json output with pr template---json+--templatecoexist (AC5).json output does not print URL- URL suppressed when Exporter active.jq without json- error without--json.web and json---web+--jsonconflict.Numberfield.pkg/cmdutil/json_flags_test.go:TestAddJSONAndJQFlags- 5 cases covering the new helper: basic, jq filter, host template coexistence, jq-without-json error, invalid field error.Acceptance Criteria Coverage
--jsonoutputs JSONtty json output--jsonoutputs JSONnontty json output--jq .numberfilters outputjson output with jq filter--dry-run+--jsonerrorsdry-run and json--json+--templatecoexistjson output with pr template