Unverified Commit 8d45b94c authored by Jay McCure's avatar Jay McCure
Browse files

fix(repo view): consider current host when viewing a repo details

parent 0d31c253
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -189,6 +189,9 @@ the `GITLAB_HOST` environment variable, like this:
- `GITLAB_HOST=gitlab.example.com glab repo clone group/project`
- `GITLAB_HOST=gitlab.example.com glab issue list -R group/project`

When inside a git repository `glab` will use that repository's GitLab host by default. For example `glab issue list`
will list all issues of the current directory's git repository.

### Configure `glab` to use self-signed certificates for self-managed instances

The GitLab CLI can be configured to support self-managed instances using self-signed certificate authorities by making either of the following changes:
+22 −23
Original line number Diff line number Diff line
@@ -40,7 +40,7 @@ func NewCmdView(f *cmdutils.Factory) *cobra.Command {
		`),
		Args: cobra.MaximumNArgs(1),
		Example: heredoc.Doc(`
			# view project information for the current directory
			# view project information for the current directory (must be a git repository)
			$ glab repo view

			# view project information of specified name
@@ -67,27 +67,31 @@ func NewCmdView(f *cmdutils.Factory) *cobra.Command {
				opts.ProjectID = args[0]
			}

			apiClient, err := f.HttpClient()
			if err != nil {
				return err
			}
			opts.APIClient = apiClient

			// No project argument - use current repository
			if opts.ProjectID == "" {
				opts.Repo, err = f.BaseRepo()
				if err != nil {
					return cmdutils.WrapError(err, "`repository` is required when not running in a git repository")
				}
				opts.ProjectID = opts.Repo.FullName()

				// Configure client to have host of current repository
				client, err := api.NewClientWithCfg(opts.Repo.RepoHost(), cfg, false)
				if err != nil {
					return err
				}
				opts.APIClient = client.Lab()

				if opts.Branch == "" {
					opts.Branch, _ = f.Branch()
				}
			}

			if opts.ProjectID != "" {
			} else {
				// If the ProjectID is a single token, use current user's namespace
				if !strings.Contains(opts.ProjectID, "/") {
					currentUser, err := api.CurrentUser(opts.APIClient)
					apiClient, err := f.HttpClient()
					if err != nil {
						return err
					}
					currentUser, err := api.CurrentUser(apiClient)
					if err != nil {
						return cmdutils.WrapError(err, "Failed to retrieve your current user")
					}
@@ -95,27 +99,22 @@ func NewCmdView(f *cmdutils.Factory) *cobra.Command {
					opts.ProjectID = currentUser.Username + "/" + opts.ProjectID
				}

				repo, err := glrepo.FromFullName(opts.ProjectID)
				// Get the repo full name from the ProjectID which can be a full URL or a group/repo format
				opts.Repo, err = glrepo.FromFullName(opts.ProjectID)
				if err != nil {
					return err
				}

				if !glrepo.IsSame(repo, opts.Repo) {
					client, err := api.NewClientWithCfg(repo.RepoHost(), cfg, false)
				client, err := api.NewClientWithCfg(opts.Repo.RepoHost(), cfg, false)
				if err != nil {
					return err
				}
				opts.APIClient = client.Lab()
			}
				opts.Repo = repo
				opts.ProjectID = repo.FullName()
			}

			browser, _ := cfg.Get(opts.Repo.RepoHost(), "browser")
			opts.Browser = browser

			opts.GlamourStyle, _ = cfg.Get(opts.Repo.RepoHost(), "glamour_style")

			return runViewProject(&opts)
		},
	}
+276 −15
Original line number Diff line number Diff line
@@ -5,6 +5,8 @@ import (
	"os/exec"
	"testing"

	"gitlab.com/gitlab-org/cli/internal/glrepo"

	"github.com/MakeNowJust/heredoc"

	"github.com/stretchr/testify/assert"
@@ -15,7 +17,7 @@ import (
	"gitlab.com/gitlab-org/cli/test"
)

func runCommand(rt http.RoundTripper, isTTY bool, cli string, stub bool) (*test.CmdOut, error, func()) {
func runCommand(rt http.RoundTripper, isTTY bool, cli string, stub bool, repoHost string) (*test.CmdOut, error, func()) {
	ios, _, stdout, stderr := cmdtest.InitIOStreams(isTTY, "")

	factory := cmdtest.InitFactory(ios, rt)
@@ -24,6 +26,14 @@ func runCommand(rt http.RoundTripper, isTTY bool, cli string, stub bool) (*test.
		return "#current-branch", nil
	}

	factory.BaseRepo = func() (glrepo.Interface, error) {
		if repoHost == "" {
			return glrepo.New("OWNER", "REPO"), nil
		} else {
			return glrepo.NewWithHost("OWNER", "REPO", repoHost), nil
		}
	}

	_, _ = factory.HttpClient()

	cmd := NewCmdView(factory)
@@ -55,6 +65,7 @@ func TestProjectView(t *testing.T) {
		httpMocks []httpMock
		isTTY     bool
		stub      bool
		repoHost  string

		expectedOutput string
	}{
@@ -64,7 +75,7 @@ func TestProjectView(t *testing.T) {
			httpMocks: []httpMock{
				{
					http.MethodGet,
					"/api/v4/projects/OWNER/REPO?license=true&statistics=true&with_custom_attributes=true",
					"https://gitlab.com/api/v4/projects/OWNER%2FREPO?license=true&statistics=true&with_custom_attributes=true",
					http.StatusOK,
					`{
							  "id": 37777023,
@@ -82,7 +93,7 @@ func TestProjectView(t *testing.T) {
				},
				{
					http.MethodGet,
					"/api/v4/projects/OWNER/REPO/repository/files/README%2Emd?ref=%23current-branch",
					"https://gitlab.com/api/v4/projects/OWNER%2FREPO/repository/files/README%2Emd?ref=%23current-branch",
					http.StatusOK,
					`{"file_name": "README.md",
							  "file_path": "README.md",
@@ -106,13 +117,13 @@ func TestProjectView(t *testing.T) {
			httpMocks: []httpMock{
				{
					http.MethodGet,
					"/api/v4/user",
					"https://gitlab.com/api/v4/user",
					http.StatusOK,
					`{ "username": "test_user" }`,
				},
				{
					http.MethodGet,
					"/api/v4/projects/test_user/foo?license=true&statistics=true&with_custom_attributes=true",
					"https://gitlab.com/api/v4/projects/test_user%2Ffoo?license=true&statistics=true&with_custom_attributes=true",
					http.StatusOK,
					`{
							  "id": 37777023,
@@ -130,7 +141,7 @@ func TestProjectView(t *testing.T) {
				},
				{
					http.MethodGet,
					"/api/v4/projects/test_user/foo/repository/files/README%2Emd?ref=main",
					"https://gitlab.com/api/v4/projects/test_user%2Ffoo/repository/files/README%2Emd?ref=main",
					http.StatusOK,
					`{"file_name": "README.md",
							  "file_path": "README.md",
@@ -154,7 +165,7 @@ func TestProjectView(t *testing.T) {
			httpMocks: []httpMock{
				{
					http.MethodGet,
					"/api/v4/projects/foo/bar?license=true&statistics=true&with_custom_attributes=true",
					"https://gitlab.com/api/v4/projects/foo%2Fbar?license=true&statistics=true&with_custom_attributes=true",
					http.StatusOK,
					`{
							  "id": 37777023,
@@ -172,7 +183,7 @@ func TestProjectView(t *testing.T) {
				},
				{
					http.MethodGet,
					"/api/v4/projects/foo/bar/repository/files/README%2Emd?ref=main",
					"https://gitlab.com/api/v4/projects/foo%2Fbar/repository/files/README%2Emd?ref=main",
					http.StatusOK,
					`{
							"file_name": "README.md",
@@ -197,7 +208,7 @@ func TestProjectView(t *testing.T) {
			httpMocks: []httpMock{
				{
					http.MethodGet,
					"/api/v4/projects/group/foo/bar?license=true&statistics=true&with_custom_attributes=true",
					"https://gitlab.com/api/v4/projects/group%2Ffoo%2Fbar?license=true&statistics=true&with_custom_attributes=true",
					http.StatusOK,
					`{
							  "id": 37777023,
@@ -215,7 +226,7 @@ func TestProjectView(t *testing.T) {
				},
				{
					http.MethodGet,
					"/api/v4/projects/group/foo/bar/repository/files/README%2Emd?ref=main",
					"https://gitlab.com/api/v4/projects/group%2Ffoo%2Fbar/repository/files/README%2Emd?ref=main",
					http.StatusOK,
					`{
							"file_name": "README.md",
@@ -235,12 +246,99 @@ func TestProjectView(t *testing.T) {
										`),
		},
		{
			name: "view project branch on web",
			name: "view a project details from a project not hosted on the default host",
			cli:  "",
			httpMocks: []httpMock{
				{
					http.MethodGet,
					"https://gitlab.company.org/api/v4/projects/OWNER%2FREPO?license=true&statistics=true&with_custom_attributes=true",
					http.StatusOK,
					`{
							  "id": 37777023,
							  "description": "this is a test description",
							  "name": "bar",
							  "name_with_namespace": "OWNER / REPO",
							  "path": "bar",
							  "path_with_namespace": "OWNER/REPO",
							  "created_at": "2022-07-13T02:04:56.151Z",
							  "default_branch": "main",
							  "http_url_to_repo": "https://gitlab.company.org/OWNER/REPO.git",
							  "web_url": "https://gitlab.company.org/OWNER/REPO",
							  "readme_url": "https://gitlab.company.org/OWNER/REPO/-/blob/main/README.md"
							}`,
				},
				{
					http.MethodGet,
					"https://gitlab.company.org/api/v4/projects/OWNER%2FREPO/repository/files/README%2Emd?ref=%23current-branch",
					http.StatusOK,
					`{
							"file_name": "README.md",
							"file_path": "README.md",
							"encoding": "base64",
							"ref": "main",
							"execute_filemode": false,
							"content": "dGVzdCByZWFkbWUK"
							}`,
				},
			},
			repoHost: "gitlab.company.org",
			expectedOutput: heredoc.Doc(`name:	OWNER / REPO
												description:	this is a test description
												---
												test readme

										`),
		},
		{
			name: "view project details from a git URL",
			cli:  "https://gitlab.company.org/OWNER/REPO.git",
			httpMocks: []httpMock{
				{
					http.MethodGet,
					"https://gitlab.company.org/api/v4/projects/OWNER%2FREPO?license=true&statistics=true&with_custom_attributes=true",
					http.StatusOK,
					`{
							  "id": 37777023,
							  "description": "this is a test description",
							  "name": "bar",
							  "name_with_namespace": "OWNER / REPO",
							  "path": "bar",
							  "path_with_namespace": "OWNER/REPO",
							  "created_at": "2022-07-13T02:04:56.151Z",
							  "default_branch": "main",
							  "http_url_to_repo": "https://gitlab.company.org/OWNER/REPO.git",
							  "web_url": "https://gitlab.company.org/OWNER/REPO",
							  "readme_url": "https://gitlab.company.org/OWNER/REPO/-/blob/main/README.md"
							}`,
				},
				{
					http.MethodGet,
					"https://gitlab.company.org/api/v4/projects/OWNER%2FREPO/repository/files/README%2Emd?ref=main",
					http.StatusOK,
					`{
							"file_name": "README.md",
							"file_path": "README.md",
							"encoding": "base64",
							"ref": "main",
							"execute_filemode": false,
							"content": "dGVzdCByZWFkbWUK"
							}`,
				},
			},
			expectedOutput: heredoc.Doc(`name:	OWNER / REPO
												description:	this is a test description
												---
												test readme

										`),
		},
		{
			name: "view project on web where current branch is different to default branch",
			cli:  "--web",
			httpMocks: []httpMock{
				{
					http.MethodGet,
					"/api/v4/projects/OWNER/REPO?license=true&statistics=true&with_custom_attributes=true",
					"https://gitlab.com/api/v4/projects/OWNER%2FREPO?license=true&statistics=true&with_custom_attributes=true",
					http.StatusOK,
					`{
							  "id": 37777023,
@@ -267,7 +365,7 @@ func TestProjectView(t *testing.T) {
			httpMocks: []httpMock{
				{
					http.MethodGet,
					"/api/v4/projects/OWNER/REPO?license=true&statistics=true&with_custom_attributes=true",
					"https://gitlab.com/api/v4/projects/OWNER%2FREPO?license=true&statistics=true&with_custom_attributes=true",
					http.StatusOK,
					`{
							  "id": 37777023,
@@ -288,12 +386,175 @@ func TestProjectView(t *testing.T) {
			stub:           true,
			expectedOutput: "Opening gitlab.com/OWNER/REPO in your browser.\n",
		},
		{
			name: "view project when passing a https git URL on web",
			cli:  "https://gitlab.company.org/OWNER/REPO.git --web",
			httpMocks: []httpMock{
				{
					http.MethodGet,
					"https://gitlab.company.org/api/v4/projects/OWNER%2FREPO?license=true&statistics=true&with_custom_attributes=true",
					http.StatusOK,
					`{
							  "id": 37777023,
							  "description": "this is a test description",
							  "name": "REPO",
							  "name_with_namespace": "Test User / REPO",
							  "path": "REPO",
							  "path_with_namespace": "OWNER/REPO",
							  "created_at": "2022-07-13T02:04:56.151Z",
							  "default_branch": "#current-branch",
							  "http_url_to_repo": "https://gitlab.company.org/OWNER/REPO.git",
							  "web_url": "https://gitlab.company.org/OWNER/REPO",
							  "readme_url": "https://gitlab.company.org/OWNER/REPO/-/blob/main/README.md"
							}`,
				},
			},
			isTTY:          true,
			stub:           true,
			expectedOutput: "Opening gitlab.company.org/OWNER/REPO in your browser.\n",
		},
		{
			name: "view project when passing a https git URL on web",
			cli:  "https://gitlab.company.org/OWNER/REPO.git --web --branch foobranch",
			httpMocks: []httpMock{
				{
					http.MethodGet,
					"https://gitlab.company.org/api/v4/projects/OWNER%2FREPO?license=true&statistics=true&with_custom_attributes=true",
					http.StatusOK,
					`{
							  "id": 37777023,
							  "description": "this is a test description",
							  "name": "REPO",
							  "name_with_namespace": "Test User / REPO",
							  "path": "REPO",
							  "path_with_namespace": "OWNER/REPO",
							  "created_at": "2022-07-13T02:04:56.151Z",
							  "default_branch": "#current-branch",
							  "http_url_to_repo": "https://gitlab.company.org/OWNER/REPO.git",
							  "web_url": "https://gitlab.company.org/OWNER/REPO",
							  "readme_url": "https://gitlab.company.org/OWNER/REPO/-/blob/main/README.md"
							}`,
				},
			},
			isTTY:          true,
			stub:           true,
			expectedOutput: "Opening gitlab.company.org/OWNER/REPO/-/tree/foobranch in your browser.\n",
		},
		{
			name: "view project when passing a https URL on web",
			cli:  "https://gitlab.company.org/OWNER/REPO --web",
			httpMocks: []httpMock{
				{
					http.MethodGet,
					"https://gitlab.company.org/api/v4/projects/OWNER%2FREPO?license=true&statistics=true&with_custom_attributes=true",
					http.StatusOK,
					`{
							  "id": 37777023,
							  "description": "this is a test description",
							  "name": "REPO",
							  "name_with_namespace": "Test User / REPO",
							  "path": "REPO",
							  "path_with_namespace": "OWNER/REPO",
							  "created_at": "2022-07-13T02:04:56.151Z",
							  "default_branch": "#current-branch",
							  "http_url_to_repo": "https://gitlab.company.org/OWNER/REPO.git",
							  "web_url": "https://gitlab.company.org/OWNER/REPO",
							  "readme_url": "https://gitlab.company.org/OWNER/REPO/-/blob/main/README.md"
							}`,
				},
			},
			isTTY:          true,
			stub:           true,
			expectedOutput: "Opening gitlab.company.org/OWNER/REPO in your browser.\n",
		},
		{
			name: "view project when passing a git URL on web",
			cli:  "git@gitlab.company.org:OWNER/REPO.git --web",
			httpMocks: []httpMock{
				{
					http.MethodGet,
					"https://gitlab.company.org/api/v4/projects/OWNER%2FREPO?license=true&statistics=true&with_custom_attributes=true",
					http.StatusOK,
					`{
							  "id": 37777023,
							  "description": "this is a test description",
							  "name": "REPO",
							  "name_with_namespace": "Test User / REPO",
							  "path": "REPO",
							  "path_with_namespace": "OWNER/REPO",
							  "created_at": "2022-07-13T02:04:56.151Z",
							  "default_branch": "#current-branch",
							  "http_url_to_repo": "https://gitlab.company.org/OWNER/REPO.git",
							  "web_url": "https://gitlab.company.org/OWNER/REPO",
							  "readme_url": "https://gitlab.company.org/OWNER/REPO/-/blob/main/README.md"
							}`,
				},
			},
			isTTY:          true,
			stub:           true,
			expectedOutput: "Opening gitlab.company.org/OWNER/REPO in your browser.\n",
		},
		{
			name: "view a project that isn't on the default host on web",
			cli:  "--web",
			httpMocks: []httpMock{
				{
					http.MethodGet,
					"https://gitlab.company.org/api/v4/projects/OWNER%2FREPO?license=true&statistics=true&with_custom_attributes=true",
					http.StatusOK,
					`{
							  "id": 37777023,
							  "description": "this is a test description",
							  "name": "REPO",
							  "name_with_namespace": "Test User / REPO",
							  "path": "REPO",
							  "path_with_namespace": "OWNER/REPO",
							  "created_at": "2022-07-13T02:04:56.151Z",
							  "default_branch": "#current-branch",
							  "http_url_to_repo": "https://gitlab.company.org/OWNER/REPO.git",
							  "web_url": "https://gitlab.company.org/OWNER/REPO",
							  "readme_url": "https://gitlab.company.org/OWNER/REPO/-/blob/main/README.md"
							}`,
				},
			},
			isTTY:          true,
			stub:           true,
			repoHost:       "gitlab.company.org",
			expectedOutput: "Opening gitlab.company.org/OWNER/REPO in your browser.\n",
		},
		{
			name: "view a specific project branch on the web",
			cli:  "--branch foo --web",
			httpMocks: []httpMock{
				{
					http.MethodGet,
					"https://gitlab.com/api/v4/projects/OWNER%2FREPO?license=true&statistics=true&with_custom_attributes=true",
					http.StatusOK,
					`{
							  "id": 37777023,
							  "description": "this is a test description",
							  "name": "REPO",
							  "name_with_namespace": "Test User / REPO",
							  "path": "REPO",
							  "path_with_namespace": "OWNER/REPO",
							  "created_at": "2022-07-13T02:04:56.151Z",
							  "default_branch": "#current-branch",
							  "http_url_to_repo": "https://gitlab.com/OWNER/REPO.git",
							  "web_url": "https://gitlab.com/OWNER/REPO",
							  "readme_url": "https://gitlab.com/OWNER/REPO/-/blob/main/README.md"
							}`,
				},
			},
			isTTY:          true,
			stub:           true,
			expectedOutput: "Opening gitlab.com/OWNER/REPO/-/tree/foo in your browser.\n",
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			fakeHTTP := &httpmock.Mocker{
				MatchURL: httpmock.PathAndQuerystring,
				MatchURL: httpmock.FullURL,
			}
			defer fakeHTTP.Verify(t)

@@ -301,7 +562,7 @@ func TestProjectView(t *testing.T) {
				fakeHTTP.RegisterResponder(mock.method, mock.path, httpmock.NewStringResponse(mock.status, mock.body))
			}

			output, err, restoreCmd := runCommand(fakeHTTP, tc.isTTY, tc.cli, tc.stub)
			output, err, restoreCmd := runCommand(fakeHTTP, tc.isTTY, tc.cli, tc.stub, tc.repoHost)
			if restoreCmd != nil {
				defer restoreCmd()
			}
+1 −1
Original line number Diff line number Diff line
@@ -24,7 +24,7 @@ glab repo view [repository] [flags]
## Examples

```plaintext
# view project information for the current directory
# view project information for the current directory (must be a git repository)
$ glab repo view

# view project information of specified name
+1 −1
Original line number Diff line number Diff line
@@ -133,7 +133,7 @@ func FromFullName(nwo string) (Interface, error) {
			return NewWithHost(parts[1], repo, normalizeHostname(parts[0])), nil
		}
		// Dots (.) are allowed in group names by GitLab.
		// So we check if if the first part contains a dot.
		// So we check if the first part contains a dot.
		// However, it could be that the user is specifying a hostname but we can't be sure of that
		// So we check in the list of authenticated hosts and see if it matches any
		// if not, we assume it is a group name that contains a dot