Repository: cli/cli
Pull request #10942 (2025-05-07 13:57:33)

Feature detect v1 projects on pr edit
## Description

Relates to: https://github.com/cli/cli/issues/10714
Builds on: https://github.com/cli/cli/pull/10915

This PR tackles projectv1 deprecation on `pr edit`.

### Reviewer Notes

**DO NOT MERGE** into base branch, wait for base branch to be merged into trunk.

You can verify the presence or absence of `projectCards` by running:

```
GH_DEBUG=api ./bin/gh pr edit --repo cli/cli 10927 --add-project "non-existent" 2>&1 | grep "projectCards"
```

And if editing projects interactively (proceeding through project metadata muliselect). Note that this was already addressed in `issue edit` because the code path is shared, so running against released `gh` will show no difference.

```
GH_DEBUG=api ./bin/gh pr edit --repo cli/cli 10927 2>&1
```


Patch:
diff --git a/pkg/cmd/pr/edit/edit.go b/pkg/cmd/pr/edit/edit.go
index 3c8d73ad3..0428d13d6 100644
--- a/pkg/cmd/pr/edit/edit.go
+++ b/pkg/cmd/pr/edit/edit.go
@@ -6,6 +6,7 @@ import (
 
 	"github.com/MakeNowJust/heredoc"
 	"github.com/cli/cli/v2/api"
+	fd "github.com/cli/cli/v2/internal/featuredetection"
 	"github.com/cli/cli/v2/internal/gh"
 	"github.com/cli/cli/v2/internal/ghrepo"
 	shared "github.com/cli/cli/v2/pkg/cmd/pr/shared"
@@ -19,6 +20,9 @@ import (
 type EditOptions struct {
 	HttpClient func() (*http.Client, error)
 	IO         *iostreams.IOStreams
+	// TODO projectsV1Deprecation
+	// Remove this detector since it is only used for test validation.
+	Detector fd.Detector
 
 	Finder          shared.PRFinder
 	Surveyor        Surveyor
@@ -193,6 +197,7 @@ func editRun(opts *EditOptions) error {
 	findOptions := shared.FindOptions{
 		Selector: opts.SelectorArg,
 		Fields:   []string{"id", "url", "title", "body", "baseRefName", "reviewRequests", "assignees", "labels", "projectCards", "projectItems", "milestone"},
+		Detector: opts.Detector,
 	}
 	pr, repo, err := opts.Finder.Find(findOptions)
 	if err != nil {


Test patch:
diff --git a/pkg/cmd/pr/edit/edit_test.go b/pkg/cmd/pr/edit/edit_test.go
index 3c4882961..b09ee104e 100644
--- a/pkg/cmd/pr/edit/edit_test.go
+++ b/pkg/cmd/pr/edit/edit_test.go
@@ -9,6 +9,7 @@ import (
 	"testing"
 
 	"github.com/cli/cli/v2/api"
+	fd "github.com/cli/cli/v2/internal/featuredetection"
 	"github.com/cli/cli/v2/internal/ghrepo"
 	shared "github.com/cli/cli/v2/pkg/cmd/pr/shared"
 	"github.com/cli/cli/v2/pkg/cmdutil"
@@ -696,3 +697,73 @@ func (s testSurveyor) EditFields(e *shared.Editable, _ string) error {
 func (t testEditorRetriever) Retrieve() (string, error) {
 	return "vim", nil
 }
+
+// TODO projectsV1Deprecation
+// Remove this test.
+func TestProjectsV1Deprecation(t *testing.T) {
+	t.Run("when projects v1 is supported, is included in query", func(t *testing.T) {
+		ios, _, _, _ := iostreams.Test()
+
+		reg := &httpmock.Registry{}
+		reg.Register(
+			httpmock.GraphQL(`projectCards`),
+			// Simulate a GraphQL error to early exit the test.
+			httpmock.StatusStringResponse(500, ""),
+		)
+
+		f := &cmdutil.Factory{
+			IOStreams: ios,
+			HttpClient: func() (*http.Client, error) {
+				return &http.Client{Transport: reg}, nil
+			},
+		}
+
+		// Ignore the error because we have no way to really stub it without
+		// fully stubbing a GQL error structure in the request body.
+		_ = editRun(&EditOptions{
+			IO: ios,
+			HttpClient: func() (*http.Client, error) {
+				return &http.Client{Transport: reg}, nil
+			},
+			Detector: &fd.EnabledDetectorMock{},
+
+			Finder: shared.NewFinder(f),
+
+			SelectorArg: "https://github.com/cli/cli/pull/123",
+		})
+
+		// Verify that our request contained projectCards
+		reg.Verify(t)
+	})
+
+	t.Run("when projects v1 is not supported, is not included in query", func(t *testing.T) {
+		ios, _, _, _ := iostreams.Test()
+
+		reg := &httpmock.Registry{}
+		reg.Exclude(t, httpmock.GraphQL(`projectCards`))
+
+		f := &cmdutil.Factory{
+			IOStreams: ios,
+			HttpClient: func() (*http.Client, error) {
+				return &http.Client{Transport: reg}, nil
+			},
+		}
+
+		// Ignore the error because we have no way to really stub it without
+		// fully stubbing a GQL error structure in the request body.
+		_ = editRun(&EditOptions{
+			IO: ios,
+			HttpClient: func() (*http.Client, error) {
+				return &http.Client{Transport: reg}, nil
+			},
+			Detector: &fd.DisabledDetectorMock{},
+
+			Finder: shared.NewFinder(f),
+
+			SelectorArg: "https://github.com/cli/cli/pull/123",
+		})
+
+		// Verify that our request did not contain projectCards
+		reg.Verify(t)
+	})
+}