forked from ksonnet/ksonnet
/
github.go
157 lines (130 loc) · 4.01 KB
/
github.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
155
156
157
// Copyright 2018 The ksonnet authors
//
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package github
import (
"context"
"fmt"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/google/go-github/github"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"golang.org/x/oauth2"
)
var (
// DefaultClient is the default GitHub client.
DefaultClient = &defaultGitHub{
httpClient: defaultHTTPClient(),
urlParse: url.Parse,
}
)
// Repo is a GitHub repo
type Repo struct {
Org string
Repo string
}
func (r Repo) String() string {
return fmt.Sprintf("%v/%v", r.Org, r.Repo)
}
// ContentSpec represents coordinates for fetching contents from a GitHub repo
type ContentSpec struct {
Repo
Path string // Path within repo to fetch
RefSpec string // A git refspec - branch, tag, or commit sha1.
}
func (c ContentSpec) String() string {
return fmt.Sprintf("%v/%v@%v", c.Repo, c.Path, c.RefSpec)
}
// GitHub is an interface for communicating with GitHub.
type GitHub interface {
ValidateURL(u string) error
CommitSHA1(ctx context.Context, repo Repo, refSpec string) (string, error)
Contents(ctx context.Context, repo Repo, path, sha1 string) (*github.RepositoryContent, []*github.RepositoryContent, error)
}
type httpClient interface {
Head(string) (*http.Response, error)
}
func defaultHTTPClient() *http.Client {
return &http.Client{
Timeout: 10 * time.Second,
}
}
type defaultGitHub struct {
httpClient *http.Client
urlParse func(string) (*url.URL, error)
}
var _ GitHub = (*defaultGitHub)(nil)
// NewGitHub constructs a GitHub client
func NewGitHub(httpClient *http.Client) GitHub {
if httpClient == nil {
httpClient = defaultHTTPClient()
}
return &defaultGitHub{
httpClient: httpClient,
urlParse: url.Parse,
}
}
func (dg *defaultGitHub) ValidateURL(urlStr string) error {
u, err := dg.urlParse(urlStr)
if err != nil {
return errors.Wrap(err, "parsing URL")
}
if u.Scheme == "" {
u.Scheme = "https"
}
if !strings.HasPrefix(u.Path, "registry.yaml") {
u.Path = u.Path + "/registry.yaml"
}
resp, err := dg.httpClient.Head(u.String())
if err != nil {
return errors.Wrapf(err, "verifying %q", u.String())
}
if resp.StatusCode != http.StatusOK {
return errors.Errorf("%q actual %d; expected %d", u.String(), resp.StatusCode, http.StatusOK)
}
return nil
}
func (dg *defaultGitHub) CommitSHA1(ctx context.Context, repo Repo, refSpec string) (string, error) {
log := log.WithField("action", "defaultGitHub.CommitSHA1")
if refSpec == "" {
refSpec = "master"
}
log.Debugf("fetching SHA1 for %s@%s", repo, refSpec)
sha, _, err := dg.client().Repositories.GetCommitSHA1(ctx, repo.Org, repo.Repo, refSpec, "")
return sha, err
}
func (dg *defaultGitHub) Contents(ctx context.Context, repo Repo, path, ref string) (*github.RepositoryContent, []*github.RepositoryContent, error) {
log := log.WithField("action", "defaultGitHub.Contents")
log.Debugf("fetching contents for %s/%s@%s", repo, path, ref)
opts := &github.RepositoryContentGetOptions{Ref: ref}
file, dir, _, err := dg.client().Repositories.GetContents(ctx, repo.Org, repo.Repo, path, opts)
return file, dir, err
}
func (dg *defaultGitHub) client() *github.Client {
var httpClient = dg.httpClient
ght := os.Getenv("GITHUB_TOKEN")
if len(ght) > 0 {
// TODO WithTimeout
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, dg.httpClient)
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: ght},
)
httpClient = oauth2.NewClient(ctx, ts)
}
return github.NewClient(httpClient)
}