Add support for reading issue field values - #14130
Conversation
There was a problem hiding this comment.
Pull request overview
Adds read-only custom issue-field support across API queries, issue commands, JSON export, and terminal rendering.
Changes:
- Queries and normalizes issue field definitions and values.
- Adds
gh issue field list. - Adds feature detection and test coverage for view/list behavior.
Show a summary per file
| File | Description |
|---|---|
api/export_pr.go |
Exports normalized issue fields. |
api/export_pr_test.go |
Tests field-value export. |
api/issue_fields.go |
Fetches paginated field definitions. |
api/issue_fields_test.go |
Tests definition pagination. |
api/queries_issue.go |
Defines issue-field models. |
api/query_builder.go |
Builds field-value queries. |
api/query_builder_test.go |
Tests query generation. |
internal/featuredetection/feature_detection.go |
Detects API support. |
internal/featuredetection/feature_detection_test.go |
Tests detection. |
pkg/cmd/issue/field/field.go |
Adds the field command group. |
pkg/cmd/issue/field/list/list.go |
Implements field listing. |
pkg/cmd/issue/field/list/list_test.go |
Tests list output. |
pkg/cmd/issue/issue.go |
Registers the command. |
pkg/cmd/issue/list/http_test.go |
Tests issue-list queries. |
pkg/cmd/issue/view/view.go |
Renders field values. |
pkg/cmd/issue/view/view_test.go |
Tests view and JSON output. |
Review details
Tip
Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 16/16 changed files
- Comments generated: 6
- Review effort level: Balanced
| IssueFields issueFieldDefinitionConnection | ||
| } | ||
| } | ||
| if err := client.GraphQL(repo.RepoHost(), query, variables, &result); err != nil { |
There was a problem hiding this comment.
Review details
Suppressed comments (3)
api/issue_fields.go:27
- 🛑 Requirement: Enable the Issue Fields GraphQL preview for field discovery
This repository-field query also uses the public-preview schema, but the shared API client currently sends only the merge_queue GraphQL feature (api/client.go:23,59). Without GraphQL-Features: issue_fields, gh issue field list will fail against the live API even though the mocked response succeeds. Add the preview feature to the shared GraphQL header and assert it in the request test.
query RepositoryIssueFields($owner: String!, $name: String!, $endCursor: String) {
repository(owner: $owner, name: $name) {
issueFields(first: 100, after: $endCursor) {
internal/featuredetection/feature_detection.go:209
- 🛑 Requirement: Keep issue feature detection to one API round trip
Every GHES call to IssueFeatures now launches a second introspection request, affecting unrelated existing flows such as issue create/edit and PR create. This violates the repository's “avoid extra round-trips” API rule and makes those commands fail if either request fails. Add the two aliased __type selections to the existing featureDetection struct and resolve all flags from one Query call instead.
wg.Go(func() error {
return gql.Query(d.host, "Issue_fields", &featureDetection, nil)
})
wg.Go(func() error {
return gql.Query(d.host, "Issue_field_types", &issueFieldFeatureDetection, nil)
pkg/cmd/issue/view/view.go:140
- 💭 Commentary: Avoid fetching values that the non-TTY renderer discards
This branch runs for both TTY and redirected output, but printRawIssuePreview never emits issue fields. As a result, scripted/default non-TTY views request up to 100 extra values and take on another API failure mode without any user-visible result. Gate this lookup on IsStdoutTTY() (or deliberately add a documented raw-output contract, which would be breaking).
if issueFeatures.IssueFieldsSupported {
- Files reviewed: 16/16 changed files
- Comments generated: 1
- Review effort level: Balanced
There was a problem hiding this comment.
Review details
Suppressed comments (2)
pkg/cmd/issue/view/view.go:136
- 🛑 Requirement: Keep the cleanup marker directly above the feature-gated branch
The repository feature-detection convention requires the cleanup TODO immediately above the if that tests the capability (AGENTS.md:161-172). This refactor leaves IssueRelationshipsCleanup above the detector call instead, so move it onto this branch.
if issueFeatures.IssueRelationshipsSupported {
internal/featuredetection/feature_detection.go:208
- 🛑 Requirement: Fold these type checks into the existing introspection request
Every IssueFeatures call on GHES now makes two HTTP requests, including commands such as issue/PR create and edit that already depend on this detector. This violates the repository's “Avoid extra round-trips” API rule (AGENTS.md:176-186). Add these two aliased __type selections to featureDetection and run one gql.Query instead; concurrency only hides latency and does not remove the extra server/cache load.
var wg errgroup.Group
wg.Go(func() error {
return gql.Query(d.host, "Issue_fields", &featureDetection, nil)
})
wg.Go(func() error {
- Files reviewed: 16/16 changed files
- Comments generated: 0 new
- Review effort level: Balanced
The other half of #99. The library could answer the question; nothing could supply it with a real change. Now roz codeowners --pr does the whole thing in one command. github.Change fetches three things: the paths a pull request touches, the CODEOWNERS governing them, and who has already approved. The approvals matter as much as the files -- the useful question is what is left, and having to look that up separately and type it back in is most of the friction. CODEOWNERS is read from the base ref, not the default branch, because a change into a release branch is governed by that branch's rules. That is why it is two round trips: the base ref is not known until the pull request has been read. The three locations GitHub looks in are tried as three aliases in one query, since a repository has at most one of them. The file list is paginated to completion. A partial list could turn "no single owner covers this" into "one does", and being wrong in that direction is worse than being slow. Deliberately not part of the batched poll, which is what #99 assumed and is right for a different reason than it gives: the cost is not that it cannot be batched, it is that the file list is large, changes only when someone pushes, and is wanted when a person asks a question rather than every minute. Verified against cli/cli#14130: sixteen files, rules read from .github/CODEOWNERS@trunk, one team covering all of it. Say plainly when no rule matches any file, rather than reporting that no single owner covers everything. Found running this against elasticsearch, whose CODEOWNERS covers eighty specific paths in a very large repository, so most changes match nothing: the old wording read as a problem and sat beside a contradictory "nothing outstanding".
|
OK |
Matches tuicr, which takes the pull request as a subcommand argument rather than a flag -- it is the object you are acting on, not a modifier on some other action. `<target>` accepts the three forms people actually have in hand: diffident pr 123 diffident pr cli/cli#13758 diffident pr cli/cli#13758 The last two carry their own repo, which is the point: pasting a URL should not also require saying which repository it came from. URLs still parse with whatever GitHub appends as you click around -- /files, #discussion_r123, ?w=1. A bare number uses the checkout you are in. `--repo` still overrides, because a flag is a statement where a target's repo is only an inference. `--repo`/`--pr` keep working unchanged. 473 tests, clippy clean. All three forms verified against real pull requests: cli/cli#13758 by URL, cli/cli#14130 by slug, and #89 by bare number in this checkout.
Related to https://github.com/github/issues/issues/21241
Design: https://github.com/github/gh-cli-and-desktop/issues/273
Description
Issue fields are not currently available when reading issues through
gh, and there is no command for discovering the issue fields defined for a repository.This adds read-only issue-field support for text, number, date, single-select, and multi-select values.
gh issue viewrenders field values in terminal output, whilegh issue view --json issueFieldsandgh issue list --json issueFieldsreturn normalized JSON. It also addsgh issue field list, including table output and--json id,name,dataType,options, with pagination over repository field definitions.Default issue views use feature detection before requesting issue fields so older GHES instances continue to work. Issue fields remain issue-only and are filtered from pull request field lists and GraphQL queries.
How did you test this change?
Key points
This PR is intentionally read-only. Setting and clearing field values will be handled separately. Repository field definitions are fully paginated; values attached to an individual issue currently use the API's first 100 values. Explicit field discovery and
--json issueFieldsrequests surface API errors on unsupported hosts, while the default terminal view is compatibility-gated because it is not opt-in.Notes for reviewers
Start with
api/query_builder.goandapi/queries_issue.gofor the GraphQL and normalized value model, thenpkg/cmd/issue/view/view.goandpkg/cmd/issue/field/list/list.gofor the user-facing behavior. The tracking issue and design proposal linked above describe the broader create/edit work that is deliberately outside this PR.Authorship and follow-up
Who wrote this:
Who answers review comments: