mirror of https://gitlab.com/gitlab-org/cli.git
254 lines
6.3 KiB
Go
254 lines
6.3 KiB
Go
package view
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"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/internal/glrepo"
|
|
"gitlab.com/gitlab-org/cli/pkg/iostreams"
|
|
"gitlab.com/gitlab-org/cli/pkg/utils"
|
|
)
|
|
|
|
type ViewOptions struct {
|
|
ProjectID string
|
|
APIClient *gitlab.Client
|
|
Web bool
|
|
Branch string
|
|
Browser string
|
|
GlamourStyle string
|
|
|
|
IO *iostreams.IOStreams
|
|
Repo glrepo.Interface
|
|
}
|
|
|
|
func NewCmdView(f *cmdutils.Factory) *cobra.Command {
|
|
opts := ViewOptions{
|
|
IO: f.IO,
|
|
}
|
|
|
|
var projectViewCmd = &cobra.Command{
|
|
Use: "view [repository] [flags]",
|
|
Short: "View a project/repository",
|
|
Long: heredoc.Doc(`Display the description and README of a project or open it in the browser.
|
|
`),
|
|
Args: cobra.MaximumNArgs(1),
|
|
Example: heredoc.Doc(`
|
|
# view project information for the current directory
|
|
$ glab repo view
|
|
|
|
# view project information of specified name
|
|
$ glab repo view my-project
|
|
$ glab repo view user/repo
|
|
$ glab repo view group/namespace/repo
|
|
|
|
# specify repo by full [git] URL
|
|
$ glab repo view git@gitlab.com:user/repo.git
|
|
$ glab repo view https://gitlab.company.org/user/repo
|
|
$ glab repo view https://gitlab.company.org/user/repo.git
|
|
`),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
var err error
|
|
|
|
cfg, err := f.Config()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
opts.Branch = strings.TrimSpace(opts.Branch)
|
|
|
|
if len(args) == 1 {
|
|
opts.ProjectID = args[0]
|
|
}
|
|
|
|
apiClient, err := f.HttpClient()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
opts.APIClient = apiClient
|
|
|
|
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()
|
|
|
|
if opts.Branch == "" {
|
|
opts.Branch, _ = f.Branch()
|
|
}
|
|
}
|
|
|
|
if opts.ProjectID != "" {
|
|
if !strings.Contains(opts.ProjectID, "/") {
|
|
currentUser, err := api.CurrentUser(opts.APIClient)
|
|
if err != nil {
|
|
return cmdutils.WrapError(err, "Failed to retrieve your current user")
|
|
}
|
|
|
|
opts.ProjectID = currentUser.Username + "/" + opts.ProjectID
|
|
}
|
|
|
|
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)
|
|
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)
|
|
},
|
|
}
|
|
|
|
projectViewCmd.Flags().BoolVarP(&opts.Web, "web", "w", false, "Open a project in the browser")
|
|
projectViewCmd.Flags().StringVarP(&opts.Branch, "branch", "b", "", "View a specific branch of the repository")
|
|
|
|
return projectViewCmd
|
|
}
|
|
|
|
func runViewProject(opts *ViewOptions) error {
|
|
project, err := opts.Repo.Project(opts.APIClient)
|
|
if err != nil {
|
|
return cmdutils.WrapError(err, "Failed to retrieve project information")
|
|
}
|
|
|
|
readmeFile, err := getReadmeFile(opts, project)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if opts.Web {
|
|
openURL := generateProjectURL(project, opts.Branch)
|
|
|
|
if opts.IO.IsaTTY {
|
|
fmt.Fprintf(opts.IO.StdOut, "Opening %s in your browser.\n", utils.DisplayURL(openURL))
|
|
}
|
|
|
|
return utils.OpenInBrowser(openURL, opts.Browser)
|
|
} else {
|
|
if opts.IO.IsaTTY {
|
|
if err := opts.IO.StartPager(); err != nil {
|
|
return err
|
|
}
|
|
defer opts.IO.StopPager()
|
|
|
|
printProjectContentTTY(opts, project, readmeFile)
|
|
} else {
|
|
printProjectContentRaw(opts, project, readmeFile)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getReadmeFile(opts *ViewOptions, project *gitlab.Project) (*gitlab.File, error) {
|
|
if project.ReadmeURL == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
readmePath := strings.Replace(project.ReadmeURL, project.WebURL+"/-/blob/", "", 1)
|
|
readmePathComponents := strings.Split(readmePath, "/")
|
|
readmeRef := readmePathComponents[0]
|
|
readmeFileName := readmePathComponents[1]
|
|
|
|
if opts.Branch == "" {
|
|
opts.Branch = readmeRef
|
|
}
|
|
|
|
readmeFile, err := api.GetFile(opts.APIClient, project.PathWithNamespace, readmeFileName, opts.Branch)
|
|
|
|
if err != nil {
|
|
return nil, cmdutils.WrapError(err, fmt.Sprintf("Failed to retrieve README file on the %s branch", opts.Branch))
|
|
}
|
|
|
|
decoded, err := base64.StdEncoding.DecodeString(readmeFile.Content)
|
|
if err != nil {
|
|
return nil, cmdutils.WrapError(err, "Failed to decode README file")
|
|
}
|
|
|
|
readmeFile.Content = string(decoded)
|
|
|
|
return readmeFile, nil
|
|
}
|
|
|
|
func generateProjectURL(project *gitlab.Project, branch string) string {
|
|
if branch != "" && project.DefaultBranch != branch {
|
|
return project.WebURL + "/-/tree/" + branch
|
|
}
|
|
|
|
return project.WebURL
|
|
}
|
|
|
|
func printProjectContentTTY(opts *ViewOptions, project *gitlab.Project, readme *gitlab.File) {
|
|
var description string
|
|
var readmeContent string
|
|
var err error
|
|
|
|
fullName := project.NameWithNamespace
|
|
if project.Description != "" {
|
|
description, err = utils.RenderMarkdownWithoutIndentations(project.Description, opts.GlamourStyle)
|
|
|
|
if err != nil {
|
|
description = project.Description
|
|
}
|
|
} else {
|
|
description = "\n(No description provided)\n\n"
|
|
}
|
|
|
|
if readme != nil {
|
|
readmeContent, err = utils.RenderMarkdown(readme.Content, opts.GlamourStyle)
|
|
|
|
if err != nil {
|
|
readmeContent = readme.Content
|
|
}
|
|
}
|
|
|
|
c := opts.IO.Color()
|
|
// Header
|
|
fmt.Fprint(opts.IO.StdOut, c.Bold(fullName))
|
|
fmt.Fprint(opts.IO.StdOut, c.Gray(description))
|
|
|
|
// Readme
|
|
if readme != nil {
|
|
fmt.Fprint(opts.IO.StdOut, readmeContent)
|
|
} else {
|
|
fmt.Fprintln(opts.IO.StdOut, c.Gray("(This repository does not have a README file)"))
|
|
}
|
|
|
|
fmt.Fprintln(opts.IO.StdOut)
|
|
fmt.Fprintf(opts.IO.StdOut, c.Gray("View this project on GitLab: %s\n"), project.WebURL)
|
|
}
|
|
|
|
func printProjectContentRaw(opts *ViewOptions, project *gitlab.Project, readme *gitlab.File) {
|
|
fullName := project.NameWithNamespace
|
|
description := project.Description
|
|
|
|
fmt.Fprintf(opts.IO.StdOut, "name:\t%s\n", fullName)
|
|
fmt.Fprintf(opts.IO.StdOut, "description:\t%s\n", description)
|
|
|
|
if readme != nil {
|
|
fmt.Fprintln(opts.IO.StdOut, "---")
|
|
fmt.Fprintf(opts.IO.StdOut, readme.Content)
|
|
fmt.Fprintln(opts.IO.StdOut)
|
|
}
|
|
}
|