github.com/pengwynn/gh@v1.0.1-0.20140118055701-14327ca3942e/github/client.go (about)

     1  package github
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/jingweno/go-octokit/octokit"
     6  	"net/url"
     7  	"os"
     8  )
     9  
    10  const (
    11  	GitHubHost    string = "github.com"
    12  	GitHubApiHost string = "api.github.com"
    13  	OAuthAppURL   string = "http://owenou.com/gh"
    14  )
    15  
    16  type ClientError struct {
    17  	error
    18  }
    19  
    20  func (e *ClientError) Error() string {
    21  	return e.error.Error()
    22  }
    23  
    24  func (e *ClientError) Is2FAError() bool {
    25  	re, ok := e.error.(*octokit.ResponseError)
    26  	return ok && re.Type == octokit.ErrorOneTimePasswordRequired
    27  }
    28  
    29  type Client struct {
    30  	Credentials *Credentials
    31  }
    32  
    33  func (client *Client) PullRequest(project *Project, id string) (pr *octokit.PullRequest, err error) {
    34  	url, err := octokit.PullRequestsURL.Expand(octokit.M{"owner": project.Owner, "repo": project.Name, "number": id})
    35  	if err != nil {
    36  		return
    37  	}
    38  
    39  	pr, result := client.octokit().PullRequests(client.requestURL(url)).One()
    40  	if result.HasError() {
    41  		err = fmt.Errorf("Error getting pull request: %s", result.Err)
    42  	}
    43  
    44  	return
    45  }
    46  
    47  func (client *Client) CreatePullRequest(project *Project, base, head, title, body string) (pr *octokit.PullRequest, err error) {
    48  	url, err := octokit.PullRequestsURL.Expand(octokit.M{"owner": project.Owner, "repo": project.Name})
    49  	if err != nil {
    50  		return
    51  	}
    52  
    53  	params := octokit.PullRequestParams{Base: base, Head: head, Title: title, Body: body}
    54  	pr, result := client.octokit().PullRequests(client.requestURL(url)).Create(params)
    55  	if result.HasError() {
    56  		err = fmt.Errorf("Error creating pull request: %s", result.Err)
    57  	}
    58  
    59  	return
    60  }
    61  
    62  func (client *Client) CreatePullRequestForIssue(project *Project, base, head, issue string) (pr *octokit.PullRequest, err error) {
    63  	url, err := octokit.PullRequestsURL.Expand(octokit.M{"owner": project.Owner, "repo": project.Name})
    64  	if err != nil {
    65  		return
    66  	}
    67  
    68  	params := octokit.PullRequestForIssueParams{Base: base, Head: head, Issue: issue}
    69  	pr, result := client.octokit().PullRequests(client.requestURL(url)).Create(params)
    70  	if result.HasError() {
    71  		err = fmt.Errorf("Error creating pull request: %s", result.Err)
    72  	}
    73  
    74  	return
    75  }
    76  
    77  func (client *Client) Repository(project *Project) (repo *octokit.Repository, err error) {
    78  	url, err := octokit.RepositoryURL.Expand(octokit.M{"owner": project.Owner, "repo": project.Name})
    79  	if err != nil {
    80  		return
    81  	}
    82  
    83  	repo, result := client.octokit().Repositories(client.requestURL(url)).One()
    84  	if result.HasError() {
    85  		err = fmt.Errorf("Error getting repository: %s", result.Err)
    86  	}
    87  
    88  	return
    89  }
    90  
    91  func (client *Client) IsRepositoryExist(project *Project) bool {
    92  	repo, err := client.Repository(project)
    93  
    94  	return err == nil && repo != nil
    95  }
    96  
    97  func (client *Client) CreateRepository(project *Project, description, homepage string, isPrivate bool) (repo *octokit.Repository, err error) {
    98  	var repoURL octokit.Hyperlink
    99  	if project.Owner != client.Credentials.User {
   100  		repoURL = octokit.OrgRepositoriesURL
   101  	} else {
   102  		repoURL = octokit.UserRepositoriesURL
   103  	}
   104  
   105  	url, err := repoURL.Expand(octokit.M{"org": project.Owner})
   106  	if err != nil {
   107  		return
   108  	}
   109  
   110  	params := octokit.Repository{
   111  		Name:        project.Name,
   112  		Description: description,
   113  		Homepage:    homepage,
   114  		Private:     isPrivate,
   115  	}
   116  	repo, result := client.octokit().Repositories(client.requestURL(url)).Create(params)
   117  	if result.HasError() {
   118  		if result.Response == nil || result.Response.StatusCode == 500 {
   119  			err = fmt.Errorf("Error creating repository: Internal Server Error (HTTP 500)")
   120  		} else {
   121  			err = fmt.Errorf("Error creating repository: %v", result.Err)
   122  		}
   123  	}
   124  
   125  	return
   126  }
   127  
   128  func (client *Client) Releases(project *Project) (releases []octokit.Release, err error) {
   129  	url, err := octokit.ReleasesURL.Expand(octokit.M{"owner": project.Owner, "repo": project.Name})
   130  	if err != nil {
   131  		return
   132  	}
   133  
   134  	releases, result := client.octokit().Releases(client.requestURL(url)).All()
   135  	if result.HasError() {
   136  		err = fmt.Errorf("Error getting release: %s", result.Err)
   137  	}
   138  
   139  	return
   140  }
   141  
   142  func (client *Client) CreateRelease(project *Project, params octokit.ReleaseParams) (release *octokit.Release, err error) {
   143  	url, err := octokit.ReleasesURL.Expand(octokit.M{"owner": project.Owner, "repo": project.Name})
   144  	if err != nil {
   145  		return
   146  	}
   147  
   148  	release, result := client.octokit().Releases(client.requestURL(url)).Create(params)
   149  	if result.HasError() {
   150  		err = fmt.Errorf("Error creating release: %s", result.Err)
   151  	}
   152  
   153  	return
   154  }
   155  
   156  func (client *Client) UploadReleaseAsset(uploadUrl *url.URL, asset *os.File, contentType string) (err error) {
   157  	c := client.octokit()
   158  	result := c.Uploads(uploadUrl).UploadAsset(asset, contentType)
   159  	if result.HasError() {
   160  		err = fmt.Errorf("Error uploading asset: %s", result.Err)
   161  	}
   162  	return
   163  }
   164  
   165  func (client *Client) CIStatus(project *Project, sha string) (status *octokit.Status, err error) {
   166  	url, err := octokit.StatusesURL.Expand(octokit.M{"owner": project.Owner, "repo": project.Name, "ref": sha})
   167  	if err != nil {
   168  		return
   169  	}
   170  
   171  	statuses, result := client.octokit().Statuses(client.requestURL(url)).All()
   172  	if result.HasError() {
   173  		err = fmt.Errorf("Error getting CI status: %s", result.Err)
   174  		return
   175  	}
   176  
   177  	if len(statuses) > 0 {
   178  		status = &statuses[0]
   179  	}
   180  
   181  	return
   182  }
   183  
   184  func (client *Client) ForkRepository(project *Project) (repo *octokit.Repository, err error) {
   185  	url, err := octokit.ForksURL.Expand(octokit.M{"owner": project.Owner, "repo": project.Name})
   186  	if err != nil {
   187  		return
   188  	}
   189  
   190  	repo, result := client.octokit().Repositories(client.requestURL(url)).Create(nil)
   191  	if result.HasError() {
   192  		err = fmt.Errorf("Error forking repository: %s", result.Err)
   193  	}
   194  
   195  	return
   196  }
   197  
   198  func (client *Client) Issues(project *Project) (issues []octokit.Issue, err error) {
   199  	var result *octokit.Result
   200  
   201  	err = client.issuesService(project, func(service *octokit.IssuesService) error {
   202  		issues, result = service.All()
   203  		return resultError(result)
   204  	})
   205  
   206  	return
   207  }
   208  
   209  func (client *Client) CreateIssue(project *Project, title, body string, labels []string) (issue *octokit.Issue, err error) {
   210  	params := octokit.IssueParams{
   211  		Title:  title,
   212  		Body:   body,
   213  		Labels: labels,
   214  	}
   215  
   216  	var result *octokit.Result
   217  
   218  	err = client.issuesService(project, func(service *octokit.IssuesService) error {
   219  		issue, result = service.Create(params)
   220  		return resultError(result)
   221  	})
   222  
   223  	return
   224  }
   225  
   226  func (client *Client) GhLatestTagName() (tagName string, err error) {
   227  	url, err := octokit.ReleasesURL.Expand(octokit.M{"owner": "jingweno", "repo": "gh"})
   228  	if err != nil {
   229  		return
   230  	}
   231  
   232  	c := octokit.NewClientWith(client.apiEndpoint(), nil, nil)
   233  	releases, result := c.Releases(client.requestURL(url)).All()
   234  	if result.HasError() {
   235  		err = fmt.Errorf("Error getting gh release: %s", result.Err)
   236  		return
   237  	}
   238  
   239  	if len(releases) == 0 {
   240  		err = fmt.Errorf("No gh release is available")
   241  		return
   242  	}
   243  
   244  	tagName = releases[0].TagName
   245  
   246  	return
   247  }
   248  
   249  func (client *Client) FindOrCreateToken(user, password, twoFactorCode string) (token string, err error) {
   250  	url, e := octokit.AuthorizationsURL.Expand(nil)
   251  	if e != nil {
   252  		err = &ClientError{e}
   253  		return
   254  	}
   255  
   256  	basicAuth := octokit.BasicAuth{Login: user, Password: password, OneTimePassword: twoFactorCode}
   257  	c := octokit.NewClientWith(client.apiEndpoint(), nil, basicAuth)
   258  	authsService := c.Authorizations(client.requestURL(url))
   259  
   260  	auths, result := authsService.All()
   261  	if result.HasError() {
   262  		err = &ClientError{result.Err}
   263  		return
   264  	}
   265  
   266  	for _, auth := range auths {
   267  		if auth.App.URL == OAuthAppURL {
   268  			token = auth.Token
   269  			break
   270  		}
   271  	}
   272  
   273  	if token == "" {
   274  		authParam := octokit.AuthorizationParams{}
   275  		authParam.Scopes = append(authParam.Scopes, "repo")
   276  		authParam.Note = "gh"
   277  		authParam.NoteURL = OAuthAppURL
   278  
   279  		auth, result := authsService.Create(authParam)
   280  		if result.HasError() {
   281  			err = &ClientError{result.Err}
   282  			return
   283  		}
   284  
   285  		token = auth.Token
   286  	}
   287  
   288  	return
   289  }
   290  
   291  func (client *Client) octokit() (c *octokit.Client) {
   292  	tokenAuth := octokit.TokenAuth{AccessToken: client.Credentials.AccessToken}
   293  	c = octokit.NewClientWith(client.apiEndpoint(), nil, tokenAuth)
   294  
   295  	return
   296  }
   297  
   298  func (client *Client) requestURL(u *url.URL) (uu *url.URL) {
   299  	uu = u
   300  	if client.Credentials != nil && client.Credentials.Host != GitHubHost {
   301  		uu, _ = url.Parse(fmt.Sprintf("/api/v3/%s", u.Path))
   302  	}
   303  
   304  	return
   305  }
   306  
   307  func (client *Client) apiEndpoint() string {
   308  	host := os.Getenv("GH_API_HOST")
   309  	if host == "" && client.Credentials != nil {
   310  		host = client.Credentials.Host
   311  	}
   312  
   313  	if host == GitHubHost {
   314  		host = GitHubApiHost
   315  	}
   316  
   317  	return absolute(host)
   318  }
   319  
   320  func absolute(endpoint string) string {
   321  	u, _ := url.Parse(endpoint)
   322  	if u.Scheme == "" {
   323  		u.Scheme = "https"
   324  	}
   325  
   326  	return u.String()
   327  }
   328  
   329  func NewClient(host string) *Client {
   330  	c := CurrentConfigs().PromptFor(host)
   331  	return &Client{Credentials: c}
   332  }
   333  
   334  func (client *Client) issuesService(project *Project, fn func(service *octokit.IssuesService) error) (err error) {
   335  	url, err := octokit.RepoIssuesURL.Expand(octokit.M{"owner": project.Owner, "repo": project.Name})
   336  	if err != nil {
   337  		return
   338  	}
   339  
   340  	service := client.octokit().Issues(client.requestURL(url))
   341  	return fn(service)
   342  }
   343  
   344  func resultError(result *octokit.Result) (err error) {
   345  	if result != nil && result.HasError() {
   346  		err = result.Err
   347  	}
   348  	return
   349  }