mirror of
https://github.com/x-motemen/ghq.git
synced 2026-09-10 07:26:27 -04:00
211 lines
4.5 KiB
Go
211 lines
4.5 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/motemen/ghq/cmdutil"
|
|
"github.com/motemen/ghq/logger"
|
|
)
|
|
|
|
// A RemoteRepository represents a remote repository.
|
|
type RemoteRepository interface {
|
|
// The repository URL.
|
|
URL() *url.URL
|
|
// Checks if the URL is valid.
|
|
IsValid() bool
|
|
// The VCS backend that hosts the repository.
|
|
VCS() (*VCSBackend, *url.URL)
|
|
}
|
|
|
|
// A GitHubRepository represents a GitHub repository. Impliments RemoteRepository.
|
|
type GitHubRepository struct {
|
|
url *url.URL
|
|
}
|
|
|
|
func (repo *GitHubRepository) URL() *url.URL {
|
|
return repo.url
|
|
}
|
|
|
|
func (repo *GitHubRepository) IsValid() bool {
|
|
if strings.HasPrefix(repo.url.Path, "/blog/") {
|
|
return false
|
|
}
|
|
|
|
// must be /{user}/{project}/?
|
|
pathComponents := strings.Split(strings.TrimRight(repo.url.Path, "/"), "/")
|
|
if len(pathComponents) != 3 {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (repo *GitHubRepository) VCS() (*VCSBackend, *url.URL) {
|
|
return GitBackend, repo.URL()
|
|
}
|
|
|
|
// A GitHubGistRepository represents a GitHub Gist repository.
|
|
type GitHubGistRepository struct {
|
|
url *url.URL
|
|
}
|
|
|
|
func (repo *GitHubGistRepository) URL() *url.URL {
|
|
return repo.url
|
|
}
|
|
|
|
func (repo *GitHubGistRepository) IsValid() bool {
|
|
return true
|
|
}
|
|
|
|
func (repo *GitHubGistRepository) VCS() (*VCSBackend, *url.URL) {
|
|
return GitBackend, repo.URL()
|
|
}
|
|
|
|
type GoogleCodeRepository struct {
|
|
url *url.URL
|
|
}
|
|
|
|
func (repo *GoogleCodeRepository) URL() *url.URL {
|
|
return repo.url
|
|
}
|
|
|
|
var validGoogleCodePathPattern = regexp.MustCompile(`^/p/[^/]+/?$`)
|
|
|
|
func (repo *GoogleCodeRepository) IsValid() bool {
|
|
return validGoogleCodePathPattern.MatchString(repo.url.Path)
|
|
}
|
|
|
|
func (repo *GoogleCodeRepository) VCS() (*VCSBackend, *url.URL) {
|
|
if cmdutil.RunSilently("hg", "identify", repo.url.String()) == nil {
|
|
return MercurialBackend, repo.URL()
|
|
} else if cmdutil.RunSilently("git", "ls-remote", repo.url.String()) == nil {
|
|
return GitBackend, repo.URL()
|
|
} else {
|
|
return nil, nil
|
|
}
|
|
}
|
|
|
|
type DarksHubRepository struct {
|
|
url *url.URL
|
|
}
|
|
|
|
func (repo *DarksHubRepository) URL() *url.URL {
|
|
return repo.url
|
|
}
|
|
|
|
func (repo *DarksHubRepository) IsValid() bool {
|
|
return strings.Count(repo.url.Path, "/") == 2
|
|
}
|
|
|
|
func (repo *DarksHubRepository) VCS() (*VCSBackend, *url.URL) {
|
|
return DarcsBackend, repo.URL()
|
|
}
|
|
|
|
type BluemixRepository struct {
|
|
url *url.URL
|
|
}
|
|
|
|
func (repo *BluemixRepository) URL() *url.URL {
|
|
return repo.url
|
|
}
|
|
|
|
var validBluemixPathPattern = regexp.MustCompile(`^/git/[^/]+/[^/]+$`)
|
|
|
|
func (repo *BluemixRepository) IsValid() bool {
|
|
return validBluemixPathPattern.MatchString(repo.url.Path)
|
|
}
|
|
|
|
func (repo *BluemixRepository) VCS() (*VCSBackend, *url.URL) {
|
|
return GitBackend, repo.URL()
|
|
}
|
|
|
|
type OtherRepository struct {
|
|
url *url.URL
|
|
}
|
|
|
|
func (repo *OtherRepository) URL() *url.URL {
|
|
return repo.url
|
|
}
|
|
|
|
func (repo *OtherRepository) IsValid() bool {
|
|
return true
|
|
}
|
|
|
|
func (repo *OtherRepository) VCS() (*VCSBackend, *url.URL) {
|
|
if err := gitHasFeatureConfigURLMatch(); err != nil {
|
|
logger.Log("warning", err.Error())
|
|
} else {
|
|
// Respect 'ghq.url.https://ghe.example.com/.vcs' config variable
|
|
// (in gitconfig:)
|
|
// [ghq "https://ghe.example.com/"]
|
|
// vcs = github
|
|
vcs, err := GitConfig("--get-urlmatch", "ghq.vcs", repo.URL().String())
|
|
if err != nil {
|
|
logger.Log("error", err.Error())
|
|
}
|
|
if backend, ok := vcsRegistry[vcs]; ok {
|
|
return backend, repo.URL()
|
|
}
|
|
}
|
|
|
|
// Detect VCS backend automatically
|
|
if cmdutil.RunSilently("git", "ls-remote", repo.url.String()) == nil {
|
|
return GitBackend, repo.URL()
|
|
}
|
|
|
|
vcs, repoURL, err := detectGoImport(repo.url)
|
|
if err == nil {
|
|
// vcs == "mod" (modproxy) not supported yet
|
|
return vcsRegistry[vcs], repoURL
|
|
}
|
|
|
|
if cmdutil.RunSilently("hg", "identify", repo.url.String()) == nil {
|
|
return MercurialBackend, repo.URL()
|
|
}
|
|
|
|
if cmdutil.RunSilently("svn", "info", repo.url.String()) == nil {
|
|
return SubversionBackend, repo.URL()
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func NewRemoteRepository(url *url.URL) (RemoteRepository, error) {
|
|
if url.Host == "github.com" {
|
|
return &GitHubRepository{url}, nil
|
|
}
|
|
|
|
if url.Host == "gist.github.com" {
|
|
return &GitHubGistRepository{url}, nil
|
|
}
|
|
|
|
if url.Host == "code.google.com" {
|
|
return &GoogleCodeRepository{url}, nil
|
|
}
|
|
|
|
if url.Host == "hub.darcs.net" {
|
|
return &DarksHubRepository{url}, nil
|
|
}
|
|
|
|
if url.Host == "hub.jazz.net" {
|
|
return &BluemixRepository{url}, nil
|
|
}
|
|
|
|
gheHosts, err := GitConfigAll("ghq.ghe.host")
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve GH:E hostname from .gitconfig: %s", err)
|
|
}
|
|
|
|
for _, host := range gheHosts {
|
|
if url.Host == host {
|
|
return &GitHubRepository{url}, nil
|
|
}
|
|
}
|
|
|
|
return &OtherRepository{url}, nil
|
|
}
|