From 242a2ed6f6b17b3f93bde8196723abb879fc1711 Mon Sep 17 00:00:00 2001 From: Gary Holtz Date: Mon, 25 Mar 2024 08:14:44 +0000 Subject: [PATCH] feat(checkout): Fixing bug with private forks --- commands/mr/checkout/mr_checkout.go | 19 ++++++-- commands/mr/checkout/mr_checkout_test.go | 58 ++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/commands/mr/checkout/mr_checkout.go b/commands/mr/checkout/mr_checkout.go index 97c582ce..a83da626 100644 --- a/commands/mr/checkout/mr_checkout.go +++ b/commands/mr/checkout/mr_checkout.go @@ -6,6 +6,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/spf13/cobra" + "github.com/xanzy/go-gitlab" "gitlab.com/gitlab-org/cli/api" "gitlab.com/gitlab-org/cli/commands/cmdutils" "gitlab.com/gitlab-org/cli/commands/mr/mrutils" @@ -67,12 +68,24 @@ func NewCmdCheckout(f *cmdutils.Factory) *cobra.Command { mrCheckoutCfg.branch = mr.SourceBranch } - mrProject, err := api.GetProject(apiClient, mr.SourceProjectID) + var mrRef string + var mrProject *gitlab.Project + + mrProject, err = api.GetProject(apiClient, mr.SourceProjectID) if err != nil { - return err + // If we don't have access to the source project, let's try the target project + mrProject, err = api.GetProject(apiClient, mr.TargetProjectID) + if err != nil { + return err + } else { + // We found the target project, let's find the ref another way + mrRef = fmt.Sprintf("refs/merge-requests/%d/head", mr.IID) + } + + } else { + mrRef = fmt.Sprintf("refs/heads/%s", mr.SourceBranch) } - mrRef := fmt.Sprintf("refs/heads/%s", mr.SourceBranch) fetchRefSpec := fmt.Sprintf("%s:%s", mrRef, mrCheckoutCfg.branch) if err := git.RunCmd([]string{"fetch", mrProject.SSHURLToRepo, fetchRefSpec}); err != nil { return err diff --git a/commands/mr/checkout/mr_checkout_test.go b/commands/mr/checkout/mr_checkout_test.go index a20ba9a7..74828d8e 100644 --- a/commands/mr/checkout/mr_checkout_test.go +++ b/commands/mr/checkout/mr_checkout_test.go @@ -122,6 +122,64 @@ func TestMrCheckout(t *testing.T) { "git checkout feat-new-mr", }, }, + { + name: "when a valid MR comes from a forked private project", + commandArgs: "123", + branch: "main", + httpMocks: []httpMock{ + { + http.MethodGet, + "/api/v4/projects/OWNER/REPO/merge_requests/123", + http.StatusOK, + `{ + "id": 123, + "iid": 123, + "project_id": 3, + "source_project_id": 3, + "target_project_id": 4, + "title": "test mr title", + "description": "test mr description", + "allow_collaboration": false, + "state": "opened", + "source_branch":"feat-new-mr" + }`, + }, + { + http.MethodGet, + "/api/v4/projects/4", + http.StatusOK, + `{ + "id": 4, + "ssh_url_to_repo": "git@gitlab.com:OWNER/REPO.git" + }`, + }, + { + http.MethodGet, + "/api/v4/projects/3", + http.StatusNotFound, + `{ + "message":"404 Project Not Found" + }`, + }, + }, + shelloutStubs: []string{ + "HEAD branch: master\n", + "\n", + "\n", + heredoc.Doc(` + deadbeef HEAD + deadb00f refs/remotes/upstream/feat-new-mr + deadbeef refs/remotes/origin/feat-new-mr + `), + }, + + expectedShellouts: []string{ + "git fetch git@gitlab.com:OWNER/REPO.git refs/merge-requests/123/head:feat-new-mr", + "git config branch.feat-new-mr.remote git@gitlab.com:OWNER/REPO.git", + "git config branch.feat-new-mr.merge refs/merge-requests/123/head", + "git checkout feat-new-mr", + }, + }, { name: "when a valid MR is checked out using MR id and specifying branch", commandArgs: "123 --branch foo",