github.com/suzuken/ghq@v0.7.5-0.20160607064937-214ded0f64ec/remote_repository.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"net/url"
     6  	"regexp"
     7  	"strings"
     8  
     9  	"github.com/motemen/ghq/utils"
    10  )
    11  
    12  // A RemoteRepository represents a remote repository.
    13  type RemoteRepository interface {
    14  	// The repository URL.
    15  	URL() *url.URL
    16  	// Checks if the URL is valid.
    17  	IsValid() bool
    18  	// The VCS backend that hosts the repository.
    19  	VCS() *VCSBackend
    20  }
    21  
    22  // A GitHubRepository represents a GitHub repository. Impliments RemoteRepository.
    23  type GitHubRepository struct {
    24  	url *url.URL
    25  }
    26  
    27  func (repo *GitHubRepository) URL() *url.URL {
    28  	return repo.url
    29  }
    30  
    31  func (repo *GitHubRepository) IsValid() bool {
    32  	if strings.HasPrefix(repo.url.Path, "/blog/") {
    33  		return false
    34  	}
    35  
    36  	// must be /{user}/{project}/?
    37  	pathComponents := strings.Split(strings.TrimRight(repo.url.Path, "/"), "/")
    38  	if len(pathComponents) != 3 {
    39  		return false
    40  	}
    41  
    42  	return true
    43  }
    44  
    45  func (repo *GitHubRepository) VCS() *VCSBackend {
    46  	return GitBackend
    47  }
    48  
    49  // A GitHubGistRepository represents a GitHub Gist repository.
    50  type GitHubGistRepository struct {
    51  	url *url.URL
    52  }
    53  
    54  func (repo *GitHubGistRepository) URL() *url.URL {
    55  	return repo.url
    56  }
    57  
    58  func (repo *GitHubGistRepository) IsValid() bool {
    59  	return true
    60  }
    61  
    62  func (repo *GitHubGistRepository) VCS() *VCSBackend {
    63  	return GitBackend
    64  }
    65  
    66  type GoogleCodeRepository struct {
    67  	url *url.URL
    68  }
    69  
    70  func (repo *GoogleCodeRepository) URL() *url.URL {
    71  	return repo.url
    72  }
    73  
    74  var validGoogleCodePathPattern = regexp.MustCompile(`^/p/[^/]+/?$`)
    75  
    76  func (repo *GoogleCodeRepository) IsValid() bool {
    77  	return validGoogleCodePathPattern.MatchString(repo.url.Path)
    78  }
    79  
    80  func (repo *GoogleCodeRepository) VCS() *VCSBackend {
    81  	if utils.RunSilently("hg", "identify", repo.url.String()) == nil {
    82  		return MercurialBackend
    83  	} else if utils.RunSilently("git", "ls-remote", repo.url.String()) == nil {
    84  		return GitBackend
    85  	} else {
    86  		return nil
    87  	}
    88  }
    89  
    90  type DarksHubRepository struct {
    91  	url *url.URL
    92  }
    93  
    94  func (repo *DarksHubRepository) URL() *url.URL {
    95  	return repo.url
    96  }
    97  
    98  func (repo *DarksHubRepository) IsValid() bool {
    99  	return strings.Count(repo.url.Path, "/") == 2
   100  }
   101  
   102  func (repo *DarksHubRepository) VCS() *VCSBackend {
   103  	return DarcsBackend
   104  }
   105  
   106  type BluemixRepository struct {
   107  	url *url.URL
   108  }
   109  
   110  func (repo *BluemixRepository) URL() *url.URL {
   111  	return repo.url
   112  }
   113  
   114  var validBluemixPathPattern = regexp.MustCompile(`^/git/[^/]+/[^/]+$`)
   115  
   116  func (repo *BluemixRepository) IsValid() bool {
   117  	return validBluemixPathPattern.MatchString(repo.url.Path)
   118  }
   119  
   120  func (repo *BluemixRepository) VCS() *VCSBackend {
   121  	return GitBackend
   122  }
   123  
   124  type OtherRepository struct {
   125  	url *url.URL
   126  }
   127  
   128  func (repo *OtherRepository) URL() *url.URL {
   129  	return repo.url
   130  }
   131  
   132  func (repo *OtherRepository) IsValid() bool {
   133  	return true
   134  }
   135  
   136  func (repo *OtherRepository) VCS() *VCSBackend {
   137  	if GitHasFeatureConfigURLMatch() {
   138  		// Respect 'ghq.url.https://ghe.example.com/.vcs' config variable
   139  		// (in gitconfig:)
   140  		//     [ghq "https://ghe.example.com/"]
   141  		//     vcs = github
   142  		vcs, err := GitConfig("--get-urlmatch", "ghq.vcs", repo.URL().String())
   143  		if err != nil {
   144  			utils.Log("error", err.Error())
   145  		}
   146  
   147  		if vcs == "git" || vcs == "github" {
   148  			return GitBackend
   149  		}
   150  
   151  		if vcs == "svn" || vcs == "subversion" {
   152  			return SubversionBackend
   153  		}
   154  
   155  		if vcs == "git-svn" {
   156  			return GitsvnBackend
   157  		}
   158  
   159  		if vcs == "hg" || vcs == "mercurial" {
   160  			return MercurialBackend
   161  		}
   162  
   163  		if vcs == "darcs" {
   164  			return DarcsBackend
   165  		}
   166  	} else {
   167  		utils.Log("warning", "This version of Git does not support `config --get-urlmatch`; per-URL settings are not available")
   168  	}
   169  
   170  	// Detect VCS backend automatically
   171  	if utils.RunSilently("hg", "identify", repo.url.String()) == nil {
   172  		return MercurialBackend
   173  	} else if utils.RunSilently("git", "ls-remote", repo.url.String()) == nil {
   174  		return GitBackend
   175  	} else if utils.RunSilently("svn", "info", repo.url.String()) == nil {
   176  		return SubversionBackend
   177  	} else {
   178  		return nil
   179  	}
   180  }
   181  
   182  func NewRemoteRepository(url *url.URL) (RemoteRepository, error) {
   183  	if url.Host == "github.com" {
   184  		return &GitHubRepository{url}, nil
   185  	}
   186  
   187  	if url.Host == "gist.github.com" {
   188  		return &GitHubGistRepository{url}, nil
   189  	}
   190  
   191  	if url.Host == "code.google.com" {
   192  		return &GoogleCodeRepository{url}, nil
   193  	}
   194  
   195  	if url.Host == "hub.darcs.net" {
   196  		return &DarksHubRepository{url}, nil
   197  	}
   198  
   199  	if url.Host == "hub.jazz.net" {
   200  		return &BluemixRepository{url}, nil
   201  	}
   202  
   203  	gheHosts, err := GitConfigAll("ghq.ghe.host")
   204  
   205  	if err != nil {
   206  		return nil, fmt.Errorf("failed to retrieve GH:E hostname from .gitconfig: %s", err)
   207  	}
   208  
   209  	for _, host := range gheHosts {
   210  		if url.Host == host {
   211  			return &GitHubRepository{url}, nil
   212  		}
   213  	}
   214  
   215  	return &OtherRepository{url}, nil
   216  }