Add --query flag to project item-list#12696
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a --query flag to gh project item-list to allow server-side filtering of project items using the GitHub Projects filter syntax (e.g. assignee:..., -status:...), addressing issue #12664.
Changes:
- Extend the project items GraphQL query plumbing to accept a query string and pass it to
ProjectV2.items. - Add
--queryflag parsing + end-to-end command behavior (including output) tests. - Introduce feature detection for whether a host’s schema supports the
ProjectV2.items(query:)argument (with mocks/tests).
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/cmd/project/shared/queries/queries.go | Adds query arg support to ProjectItems GraphQL query and method signature. |
| pkg/cmd/project/shared/queries/queries_test.go | Updates expected GraphQL variables and call signature for ProjectItems. |
| pkg/cmd/project/item-list/item_list.go | Adds --query flag and gates it behind feature detection. |
| pkg/cmd/project/item-list/item_list_test.go | Adds parsing + runtime tests for --query and unsupported-host behavior. |
| internal/featuredetection/feature_detection.go | Adds ProjectFeatures() to detect ProjectV2.items(query:) support on enterprise hosts. |
| internal/featuredetection/feature_detection_test.go | Adds tests validating ProjectFeatures() behavior across hosts/schema variants. |
| internal/featuredetection/detector_mock.go | Updates detector mocks to implement the new interface method. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
babakks
left a comment
There was a problem hiding this comment.
Didn't fully test it, but I'm concerned about the query: $queryItems arg add to the GraphQL tag on Project.Items field.
| if host == "" { | ||
| host = ghinstance.Default() | ||
| } | ||
| config.detector = fd.NewDetector(api.NewCachedHTTPClient(httpClient, time.Hour*24), host) |
There was a problem hiding this comment.
I think in other places we populate the detector in the runX func, ofc if it's nil.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
6ab0b3c to
40ce0be
Compare
| Long: heredoc.Doc(` | ||
| List the items in a project. | ||
|
|
||
| If supported by the API host (github.com and GHES 3.20+), the --query option can |
There was a problem hiding this comment.
question: are we sure about GHES 3.20 support for this?
There was a problem hiding this comment.
Well it's not in now in 3.19 and schema changes are always pulled in on release.
| @@ -147,6 +173,27 @@ type Project struct { | |||
| } | |||
| } | |||
|
|
|||
| func newProjectFromWithoutItemQuery(source projectWithoutItemQuery) *Project { | |||
| project := &Project{ | |||
| Number: source.Number, | |||
| URL: source.URL, | |||
| ShortDescription: source.ShortDescription, | |||
| Public: source.Public, | |||
| Closed: source.Closed, | |||
| Title: source.Title, | |||
| ID: source.ID, | |||
| Readme: source.Readme, | |||
| Fields: source.Fields, | |||
| } | |||
| project.Items.PageInfo = source.Items.PageInfo | |||
| project.Items.TotalCount = source.Items.TotalCount | |||
| project.Items.Nodes = source.Items.Nodes | |||
| project.Owner.TypeName = source.Owner.TypeName | |||
| project.Owner.User.Login = source.Owner.User.Login | |||
| project.Owner.Organization.Login = source.Owner.Organization.Login | |||
| return project | |||
| } | |||
|
|
|||
| func (p Project) DetailedItems() map[string]interface{} { | |||
| return map[string]interface{}{ | |||
| "items": serializeProjectWithItems(&p), | |||
| @@ -508,8 +555,10 @@ func (p ProjectItem) ExportData(_ []string) map[string]interface{} { | |||
| } | |||
|
|
|||
| // ProjectItems returns the items of a project. If the OwnerType is VIEWER, no login is required. | |||
| // If limit is 0, the default limit is used. | |||
| func (c *Client) ProjectItems(o *Owner, number int32, limit int) (*Project, error) { | |||
| // If limit is 0, the default limit is used. The queryStr parameter is passed as a server-side | |||
| // filter to the items connection, using the same syntax as the GitHub Projects filter bar | |||
| // (e.g. "assignee:octocat", "status:done"). | |||
| func (c *Client) ProjectItems(o *Owner, number int32, limit int, queryStr string) (*Project, error) { | |||
| project := &Project{} | |||
| if limit == 0 { | |||
| limit = LimitDefault | |||
| @@ -528,20 +577,35 @@ func (c *Client) ProjectItems(o *Owner, number int32, limit int) (*Project, erro | |||
| "afterFields": (*githubv4.String)(nil), | |||
| "number": githubv4.Int(number), | |||
| } | |||
| if queryStr != "" { | |||
| variables["queryItems"] = githubv4.String(queryStr) | |||
| } | |||
|
|
|||
| var query pager[ProjectItem] | |||
| var queryName string | |||
| switch o.Type { | |||
| case UserOwner: | |||
| variables["login"] = githubv4.String(o.Login) | |||
| query = &userOwnerWithItems{} // must be a pointer to work with graphql queries | |||
| if queryStr == "" { | |||
| query = &userOwnerWithItemsNoQuery{} // must be a pointer to work with graphql queries | |||
| } else { | |||
| query = &userOwnerWithItems{} // must be a pointer to work with graphql queries | |||
| } | |||
| queryName = "UserProjectWithItems" | |||
| case OrgOwner: | |||
| variables["login"] = githubv4.String(o.Login) | |||
| query = &orgOwnerWithItems{} // must be a pointer to work with graphql queries | |||
| if queryStr == "" { | |||
| query = &orgOwnerWithItemsNoQuery{} // must be a pointer to work with graphql queries | |||
| } else { | |||
| query = &orgOwnerWithItems{} // must be a pointer to work with graphql queries | |||
| } | |||
| queryName = "OrgProjectWithItems" | |||
| case ViewerOwner: | |||
| query = &viewerOwnerWithItems{} // must be a pointer to work with graphql queries | |||
| if queryStr == "" { | |||
| query = &viewerOwnerWithItemsNoQuery{} // must be a pointer to work with graphql queries | |||
| } else { | |||
| query = &viewerOwnerWithItems{} // must be a pointer to work with graphql queries | |||
| } | |||
There was a problem hiding this comment.
These are getting out of our hands. We already have had 2-3 types per owner type, and we're now adding 3 more.
I'm not blocking this PR, but we should prioritise a follow up to simplify this approach. Perhaps we should fallback to plain text queries?
There was a problem hiding this comment.
Sorry, the range is wrong. I meant the last switch statement.
Co-authored-by: Babak K. Shandiz <babakks@github.com>
Co-authored-by: Babak K. Shandiz <babakks@github.com>
Co-authored-by: Babak K. Shandiz <babakks@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| return err | ||
| } | ||
| if !features.ProjectItemQuery { | ||
| return fmt.Errorf("the `--query` flag is not supported on this GitHub host; most likely you are targeting a version of GHES that does not yet have the query field available") |
There was a problem hiding this comment.
nit: this seems extra for an error message
| if queryStr == "" { | ||
| query = &userOwnerWithItemsNoQuery{} // must be a pointer to work with graphql queries | ||
| } else { | ||
| query = &userOwnerWithItems{} // must be a pointer to work with graphql queries | ||
| } | ||
| queryName = "UserProjectWithItems" | ||
| case OrgOwner: | ||
| variables["login"] = githubv4.String(o.Login) | ||
| query = &orgOwnerWithItems{} // must be a pointer to work with graphql queries | ||
| if queryStr == "" { | ||
| query = &orgOwnerWithItemsNoQuery{} // must be a pointer to work with graphql queries | ||
| } else { | ||
| query = &orgOwnerWithItems{} // must be a pointer to work with graphql queries | ||
| } | ||
| queryName = "OrgProjectWithItems" | ||
| case ViewerOwner: | ||
| query = &viewerOwnerWithItems{} // must be a pointer to work with graphql queries | ||
| if queryStr == "" { | ||
| query = &viewerOwnerWithItemsNoQuery{} // must be a pointer to work with graphql queries | ||
| } else { | ||
| query = &viewerOwnerWithItems{} // must be a pointer to work with graphql queries | ||
| } |
There was a problem hiding this comment.
nit: code comments here could probably be just one comment for all of them
This MR contains the following updates: | Package | Update | Change | |---|---|---| | [cli/cli](https://github.com/cli/cli) | minor | `v2.86.0` → `v2.87.3` | MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot). **Proposed changes to behavior should be submitted there as MRs.** --- ### Release Notes <details> <summary>cli/cli (cli/cli)</summary> ### [`v2.87.3`](https://github.com/cli/cli/releases/tag/v2.87.3): GitHub CLI 2.87.3 [Compare Source](cli/cli@v2.87.2...v2.87.3) #### What's Changed - Fix project mutation query variable usage by [@​williammartin](https://github.com/williammartin) in [#​12757](cli/cli#12757) **Full Changelog**: <cli/cli@v2.87.2...v2.87.3> ### [`v2.87.2`](https://github.com/cli/cli/releases/tag/v2.87.2): GitHub CLI 2.87.2 [Compare Source](cli/cli@v2.87.1...v2.87.2) #### ℹ️ Note This release was cut primarily to resolve a publishing issue. We recommend reviewing [the v2.87.1 release notes](https://github.com/cli/cli/releases/tag/v2.87.1) for the complete set of latest features and fixes. #### What's Changed - chore(deps): bump golang.org/x/crypto from 0.47.0 to 0.48.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​12659](cli/cli#12659) **Full Changelog**: <cli/cli@v2.87.1...v2.87.2> ### [`v2.87.1`](https://github.com/cli/cli/releases/tag/v2.87.1): GitHub CLI 2.87.1 [Compare Source](cli/cli@v2.87.0...v2.87.1) ####⚠️ Incomplete Release The v2.87.1 release experienced a failure in our workflow and is not fully published to the designated package managers/repositories. This is resolved in [v2.87.2](https://github.com/cli/cli/releases/tag/v2.87.2), so we recommend using that release instead. #### What's Changed - Remove license bundling debris by [@​williammartin](https://github.com/williammartin) in [#​12716](cli/cli#12716) - fix(agent-task/capi): use a fixed CAPI API version by [@​babakks](https://github.com/babakks) in [#​12731](cli/cli#12731) **Full Changelog**: <cli/cli@v2.87.0...v2.87.1> ### [`v2.87.0`](https://github.com/cli/cli/releases/tag/v2.87.0): GitHub CLI 2.87.0 [Compare Source](cli/cli@v2.86.0...v2.87.0) #### `gh workflow run` immediately returns workflow run URL One of our most requested features - with the latest changes in GitHub API, `gh workflow run` will immediately print the created workflow run URL. #### Improved `gh auth login` experience in VM/WSL environments We have observed rare cases of time drift between the wall and monotonic clocks, mostly in WSL or VM environments, causing failures during polling for the OAuth token. This new release implements measures to account for such situations. If you continue to experience `gh auth login` issues in WSL, please comment in [#​9370](cli/cli#9370) ####Request Copilot Code Review from `gh` + performance improvements `gh pr edit` now supports [Copilot Code Review](https://docs.github.com/en/copilot/using-github-copilot/code-review/using-copilot-code-review) as a reviewer. You can request a review from Copilot using the `--add-reviewer @​copilot` flag or interactively by selecting reviewers in the prompts. This release also introduces a new search experience for selecting reviewers and assignees in `gh pr edit`. Instead of loading all collaborators and teams upfront, results are now fetched based on inputs to a new search option. Initial options are suggestions based on those involved with the pull request already. ``` ? Reviewers [Use arrows to move, space to select, <right> to all, <left> to none, type to filter] [ ] Search (7472 more) [x] BagToad (Kynan Ware) > [x] Copilot (AI) ``` This experience will follow in `gh pr create` and `gh issue` for assignees in a later release. #### What's Changed ##### ✨ Features - Bundle licenses at release time by [@​williammartin](https://github.com/williammartin) in [#​12625](cli/cli#12625) - Add `--query` flag to `project item-list` by [@​williammartin](https://github.com/williammartin) in [#​12696](cli/cli#12696) - feat(workflow run): retrieve workflow dispatch run details by [@​babakks](https://github.com/babakks) in [#​12695](cli/cli#12695) - Pin REST API version to 2022-11-28 by [@​williammartin](https://github.com/williammartin) in [#​12680](cli/cli#12680) - Respect `--exit-status` with `--log` and `--log-failed` in `run view` by [@​williammartin](https://github.com/williammartin) in [#​12679](cli/cli#12679) - Fork with default branch only during pr create by [@​williammartin](https://github.com/williammartin) in [#​12673](cli/cli#12673) - `gh pr edit`: Add support for Copilot as reviewer with search capability, performance and accessibility improvements by [@​BagToad](https://github.com/BagToad) in [#​12567](cli/cli#12567) - `gh pr edit`: new interactive prompt for assignee selection, performance and accessibility improvements by [@​BagToad](https://github.com/BagToad) in [#​12526](cli/cli#12526) ##### 📚 Docs & Chores - Clean up project item-list query addition changes by [@​williammartin](https://github.com/williammartin) in [#​12714](cli/cli#12714) - `gh release upload`: Clarify `--clobber` flag deletes assets before re-uploading by [@​BagToad](https://github.com/BagToad) in [#​12711](cli/cli#12711) - Add usage examples to `gh gist edit` command by [@​BagToad](https://github.com/BagToad) in [#​12710](cli/cli#12710) - Remove feedback issue template by [@​BagToad](https://github.com/BagToad) in [#​12708](cli/cli#12708) - Migrate issue triage workflows to shared workflows by [@​BagToad](https://github.com/BagToad) in [#​12677](cli/cli#12677) - Migrate MR triage workflows to shared workflows by [@​BagToad](https://github.com/BagToad) in [#​12707](cli/cli#12707) - Add missing TODO comments for featuredetection if-statements by [@​BagToad](https://github.com/BagToad) in [#​12701](cli/cli#12701) - Add manual dispatch to bump-go workflow by [@​BagToad](https://github.com/BagToad) in [#​12631](cli/cli#12631) - typo: dont to don't by [@​cuiweixie](https://github.com/cuiweixie) in [#​12554](cli/cli#12554) - Fix fmt.Errorf format argument in ParseFullReference by [@​mikelolasagasti](https://github.com/mikelolasagasti) in [#​12516](cli/cli#12516) - Lint source.md by [@​Sethispr](https://github.com/Sethispr) in [#​12521](cli/cli#12521) #####
Dependencies - chore(deps): bump golang.org/x/text from 0.32.0 to 0.33.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​12468](cli/cli#12468) - chore(deps): bump golang.org/x/term from 0.38.0 to 0.39.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​12616](cli/cli#12616) - Bump go to 1.25.7 by [@​BagToad](https://github.com/BagToad) in [#​12630](cli/cli#12630) - chore(deps): bump golang.org/x/crypto from 0.46.0 to 0.47.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​12629](cli/cli#12629) - chore: bump `cli/oauth` to `v1.2.2` by [@​babakks](https://github.com/babakks) in [#​12573](cli/cli#12573) - update Go to 1.25.6 by [@​BagToad](https://github.com/BagToad) in [#​12580](cli/cli#12580) - chore(deps): bump actions/attest-build-provenance from 3.1.0 to 3.2.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​12558](cli/cli#12558) - chore(deps): bump github.com/sigstore/rekor from 1.4.3 to 1.5.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​12524](cli/cli#12524) - chore(deps): bump github.com/theupdateframework/go-tuf/v2 from 2.3.1 to 2.4.1 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​12555](cli/cli#12555) - chore(deps): bump github.com/gdamore/tcell/v2 from 2.13.4 to 2.13.7 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​12469](cli/cli#12469) - chore(deps): bump github.com/sigstore/sigstore from 1.10.0 to 1.10.4 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​12525](cli/cli#12525) - chore(deps): bump github.com/theupdateframework/go-tuf/v2 from 2.3.0 to 2.3.1 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​12515](cli/cli#12515) - chore(deps): bump actions/download-artifact from 6 to 7 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​12314](cli/cli#12314) - chore(deps): bump actions/upload-artifact from 5 to 6 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​12315](cli/cli#12315) - chore(deps): bump goreleaser/goreleaser-action from 6.0.0 to 6.4.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​12354](cli/cli#12354) #### New Contributors - [@​Sethispr](https://github.com/Sethispr) made their first contribution in [#​12521](cli/cli#12521) - [@​cuiweixie](https://github.com/cuiweixie) made their first contribution in [#​12554](cli/cli#12554) **Full Changelog**: <cli/cli@v2.86.0...v2.87.0> </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this MR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box --- This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNC4yIiwidXBkYXRlZEluVmVyIjoiNDMuMzEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90IiwiYXV0b21hdGlvbjpib3QtYXV0aG9yZWQiLCJkZXBlbmRlbmN5LXR5cGU6Om1pbm9yIl19-->

Description
Fixes #12664
Acceptance Criteria
Given I am targeting github.com
When I run
project item-listwith the--queryflagThen the query is respected in the results
Given I am targeting a version of GHES that doesn't support the query flag
When I run
project item-listwith the--queryflagThen I receive an informative error