github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/internal/ghrepo/repo.go (about) 1 package ghrepo 2 3 import ( 4 "fmt" 5 "net/url" 6 "strings" 7 8 "github.com/ungtb10d/cli/v2/internal/ghinstance" 9 ghAuth "github.com/cli/go-gh/pkg/auth" 10 "github.com/cli/go-gh/pkg/repository" 11 ) 12 13 // Interface describes an object that represents a GitHub repository 14 type Interface interface { 15 RepoName() string 16 RepoOwner() string 17 RepoHost() string 18 } 19 20 // New instantiates a GitHub repository from owner and name arguments 21 func New(owner, repo string) Interface { 22 return NewWithHost(owner, repo, ghinstance.Default()) 23 } 24 25 // NewWithHost is like New with an explicit host name 26 func NewWithHost(owner, repo, hostname string) Interface { 27 return &ghRepo{ 28 owner: owner, 29 name: repo, 30 hostname: normalizeHostname(hostname), 31 } 32 } 33 34 // FullName serializes a GitHub repository into an "OWNER/REPO" string 35 func FullName(r Interface) string { 36 return fmt.Sprintf("%s/%s", r.RepoOwner(), r.RepoName()) 37 } 38 39 func defaultHost() string { 40 host, _ := ghAuth.DefaultHost() 41 return host 42 } 43 44 // FromFullName extracts the GitHub repository information from the following 45 // formats: "OWNER/REPO", "HOST/OWNER/REPO", and a full URL. 46 func FromFullName(nwo string) (Interface, error) { 47 return FromFullNameWithHost(nwo, defaultHost()) 48 } 49 50 // FromFullNameWithHost is like FromFullName that defaults to a specific host for values that don't 51 // explicitly include a hostname. 52 func FromFullNameWithHost(nwo, fallbackHost string) (Interface, error) { 53 repo, err := repository.ParseWithHost(nwo, fallbackHost) 54 if err != nil { 55 return nil, err 56 } 57 return NewWithHost(repo.Owner(), repo.Name(), repo.Host()), nil 58 } 59 60 // FromURL extracts the GitHub repository information from a git remote URL 61 func FromURL(u *url.URL) (Interface, error) { 62 if u.Hostname() == "" { 63 return nil, fmt.Errorf("no hostname detected") 64 } 65 66 parts := strings.SplitN(strings.Trim(u.Path, "/"), "/", 3) 67 if len(parts) != 2 { 68 return nil, fmt.Errorf("invalid path: %s", u.Path) 69 } 70 71 return NewWithHost(parts[0], strings.TrimSuffix(parts[1], ".git"), u.Hostname()), nil 72 } 73 74 func normalizeHostname(h string) string { 75 return strings.ToLower(strings.TrimPrefix(h, "www.")) 76 } 77 78 // IsSame compares two GitHub repositories 79 func IsSame(a, b Interface) bool { 80 return strings.EqualFold(a.RepoOwner(), b.RepoOwner()) && 81 strings.EqualFold(a.RepoName(), b.RepoName()) && 82 normalizeHostname(a.RepoHost()) == normalizeHostname(b.RepoHost()) 83 } 84 85 func GenerateRepoURL(repo Interface, p string, args ...interface{}) string { 86 baseURL := fmt.Sprintf("%s%s/%s", ghinstance.HostPrefix(repo.RepoHost()), repo.RepoOwner(), repo.RepoName()) 87 if p != "" { 88 if path := fmt.Sprintf(p, args...); path != "" { 89 return baseURL + "/" + path 90 } 91 } 92 return baseURL 93 } 94 95 // TODO there is a parallel implementation for non-isolated commands 96 func FormatRemoteURL(repo Interface, protocol string) string { 97 if protocol == "ssh" { 98 return fmt.Sprintf("git@%s:%s/%s.git", repo.RepoHost(), repo.RepoOwner(), repo.RepoName()) 99 } 100 101 return fmt.Sprintf("%s%s/%s.git", ghinstance.HostPrefix(repo.RepoHost()), repo.RepoOwner(), repo.RepoName()) 102 } 103 104 type ghRepo struct { 105 owner string 106 name string 107 hostname string 108 } 109 110 func (r ghRepo) RepoOwner() string { 111 return r.owner 112 } 113 114 func (r ghRepo) RepoName() string { 115 return r.name 116 } 117 118 func (r ghRepo) RepoHost() string { 119 return r.hostname 120 }