-
-
Notifications
You must be signed in to change notification settings - Fork 103
/
connector.go
154 lines (141 loc) · 5.15 KB
/
connector.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package github
import (
"context"
"errors"
"fmt"
"net/url"
"os"
"github.com/git-town/git-town/v14/src/cli/print"
"github.com/git-town/git-town/v14/src/config/configdomain"
"github.com/git-town/git-town/v14/src/git/gitdomain"
"github.com/git-town/git-town/v14/src/git/giturl"
"github.com/git-town/git-town/v14/src/hosting/hostingdomain"
"github.com/git-town/git-town/v14/src/messages"
"github.com/google/go-github/v58/github"
"golang.org/x/oauth2"
)
// Connector provides standardized connectivity for the given repository (github.com/owner/repo)
// via the GitHub API.
type Connector struct {
hostingdomain.Config
APIToken configdomain.GitHubToken
MainBranch gitdomain.LocalBranchName
client *github.Client
log print.Logger
}
func (self *Connector) DefaultProposalMessage(proposal hostingdomain.Proposal) string {
return fmt.Sprintf("%s (#%d)", proposal.Title, proposal.Number)
}
func (self *Connector) FindProposal(branch, target gitdomain.LocalBranchName) (*hostingdomain.Proposal, error) {
pullRequests, _, err := self.client.PullRequests.List(context.Background(), self.Organization, self.Repository, &github.PullRequestListOptions{
Head: self.Organization + ":" + branch.String(),
Base: target.String(),
State: "open",
})
if err != nil {
return nil, err
}
if len(pullRequests) == 0 {
return nil, nil //nolint:nilnil
}
if len(pullRequests) > 1 {
return nil, fmt.Errorf(messages.ProposalMultipleFound, len(pullRequests), branch, target)
}
proposal := parsePullRequest(pullRequests[0])
return &proposal, nil
}
func (self *Connector) NewProposalURL(branch, parentBranch gitdomain.LocalBranchName) (string, error) {
toCompare := branch.String()
if parentBranch != self.MainBranch {
toCompare = parentBranch.String() + "..." + branch.String()
}
return fmt.Sprintf("%s/compare/%s?expand=1", self.RepositoryURL(), url.PathEscape(toCompare)), nil
}
func (self *Connector) RepositoryURL() string {
return fmt.Sprintf("https://%s/%s/%s", self.HostnameWithStandardPort(), self.Organization, self.Repository)
}
func (self *Connector) SquashMergeProposal(number int, message gitdomain.CommitMessage) (err error) {
if number <= 0 {
return errors.New(messages.ProposalNoNumberGiven)
}
self.log.Start(messages.HostingGithubMergingViaAPI, number)
commitMessageParts := message.Parts()
_, _, err = self.client.PullRequests.Merge(context.Background(), self.Organization, self.Repository, number, commitMessageParts.Text, &github.PullRequestOptions{
MergeMethod: "squash",
CommitTitle: commitMessageParts.Subject,
})
self.log.Success()
return err
}
func (self *Connector) UpdateProposalTarget(number int, target gitdomain.LocalBranchName) error {
self.log.Start(messages.HostingGithubUpdatePRViaAPI, number)
targetName := target.String()
_, _, err := self.client.PullRequests.Edit(context.Background(), self.Organization, self.Repository, number, &github.PullRequest{
Base: &github.PullRequestBranch{
Ref: &(targetName),
},
})
if err != nil {
self.log.Failed(err)
return err
}
self.log.Success()
return nil
}
// getGitHubApiToken returns the GitHub API token to use.
// It first checks the GITHUB_TOKEN environment variable.
// If that is not set, it checks the GITHUB_AUTH_TOKEN environment variable.
// If that is not set, it checks the git config.
func GetAPIToken(gitConfigToken configdomain.GitHubToken) configdomain.GitHubToken {
apiToken := os.ExpandEnv("$GITHUB_TOKEN")
if apiToken != "" {
return configdomain.GitHubToken(apiToken)
}
apiToken = os.ExpandEnv("$GITHUB_AUTH_TOKEN")
if apiToken != "" {
return configdomain.GitHubToken(apiToken)
}
return gitConfigToken
}
// NewConnector provides a fully configured GithubConnector instance
// if the current repo is hosted on GitHub, otherwise nil.
func NewConnector(args NewConnectorArgs) (*Connector, error) {
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: args.APIToken.String()})
httpClient := oauth2.NewClient(context.Background(), tokenSource)
githubClient := github.NewClient(httpClient)
if args.OriginURL.Host != "github.com" {
url := "https://" + args.OriginURL.Host
var err error
githubClient, err = githubClient.WithEnterpriseURLs(url, url)
if err != nil {
return nil, fmt.Errorf(messages.GitHubEnterpriseInitializeError, err)
}
}
return &Connector{
APIToken: args.APIToken,
Config: hostingdomain.Config{
Hostname: args.OriginURL.Host,
Organization: args.OriginURL.Org,
Repository: args.OriginURL.Repo,
},
MainBranch: args.MainBranch,
client: githubClient,
log: args.Log,
}, nil
}
type NewConnectorArgs struct {
APIToken configdomain.GitHubToken
HostingPlatform configdomain.HostingPlatform
Log print.Logger
MainBranch gitdomain.LocalBranchName
OriginURL *giturl.Parts
}
// parsePullRequest extracts standardized proposal data from the given GitHub pull-request.
func parsePullRequest(pullRequest *github.PullRequest) hostingdomain.Proposal {
return hostingdomain.Proposal{
Number: pullRequest.GetNumber(),
Target: gitdomain.NewLocalBranchName(pullRequest.Base.GetRef()),
Title: pullRequest.GetTitle(),
MergeWithAPI: pullRequest.GetMergeableState() == "clean",
}
}