forked from cloudfoundry/bosh-io-web
-
Notifications
You must be signed in to change notification settings - Fork 0
/
periodic_github_note_importer.go
177 lines (138 loc) · 4.63 KB
/
periodic_github_note_importer.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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package noteimporter
import (
"time"
bosherr "github.com/cloudfoundry/bosh-agent/errors"
boshlog "github.com/cloudfoundry/bosh-agent/logger"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
bhnotesrepo "github.com/cppforlife/bosh-hub/release/notesrepo"
bhrelsrepo "github.com/cppforlife/bosh-hub/release/releasesrepo"
)
type PeriodicGithubNoteImporter struct {
p time.Duration
accessToken string
stopCh <-chan struct{}
releasesRepo bhrelsrepo.ReleasesRepository
logTag string
logger boshlog.Logger
}
func NewPeriodicGithubNoteImporter(
p time.Duration,
accessToken string,
stopCh <-chan struct{},
releasesRepo bhrelsrepo.ReleasesRepository,
logger boshlog.Logger,
) PeriodicGithubNoteImporter {
return PeriodicGithubNoteImporter{
p: p,
accessToken: accessToken,
stopCh: stopCh,
releasesRepo: releasesRepo,
logTag: "PeriodicGithubNoteImporter",
logger: logger,
}
}
func (i PeriodicGithubNoteImporter) Import() error {
i.logger.Info(i.logTag, "Starting importing release notes every '%s'", i.p)
for {
select {
case <-time.After(i.p):
i.logger.Debug(i.logTag, "Looking at the release versions")
err := i.importNotes()
if err != nil {
i.logger.Error(i.logTag, "Failed to import notes: '%s'", err)
}
case <-i.stopCh:
i.logger.Info(i.logTag, "Stopped looking at the release versions")
return nil
}
}
}
func (i PeriodicGithubNoteImporter) importNotes() error {
// todo getting all releases; could be big
sources, err := i.releasesRepo.ListAll()
if err != nil {
return bosherr.WrapError(err, "Listing releases")
}
for _, source := range sources {
ghSource, valid := newGithubSource(source)
if !valid {
continue
}
// todo convert Source to string; argh
relVerRecs, err := i.releasesRepo.FindAll(source.Full)
if err != nil {
return bosherr.WrapError(err, "Listing all versions for release source '%s'", source)
}
err = i.importNotesForRelease(ghSource, relVerRecs)
if err != nil {
return bosherr.WrapError(err, "Importing notes for release source '%s'", source)
}
}
return nil
}
func (i PeriodicGithubNoteImporter) importNotesForRelease(ghSource githubSource, relVerRecs []bhrelsrepo.ReleaseVersionRec) error {
// fast path if there are no release versions
if len(relVerRecs) == 0 {
return nil
}
allGhReleases, err := i.fetchAllReleasesFromGithub(ghSource)
if err != nil {
// Continue onto other release versions
i.logger.Error(i.logTag, "Failed to fetch releases from github for '%v': %s", ghSource, err)
return nil
}
for _, relVerRec := range relVerRecs {
for _, ghRelease := range allGhReleases {
expectedLabel := "v" + relVerRec.VersionRaw
// Either release name or git tag name match
matchesName := ghRelease.Name != nil && *ghRelease.Name == expectedLabel
matchesTagName := ghRelease.TagName != nil && *ghRelease.TagName == expectedLabel
if matchesName || matchesTagName {
noteRec := bhnotesrepo.NoteRec{}
// Always overwrite bosh.io release notes with GH notes;
// covers the case when release notes are removed from GH -> remove from bosh.import
if ghRelease.Body != nil {
noteRec.Content = *ghRelease.Body
}
err = relVerRec.SetNotes(noteRec)
if err != nil {
return bosherr.WrapError(err, "Saving notes for release version '%v'", relVerRec)
}
break
}
}
}
return nil
}
func (i PeriodicGithubNoteImporter) fetchAllReleasesFromGithub(ghSource githubSource) ([]github.RepositoryRelease, error) {
i.logger.Debug(i.logTag, "Fetching github releases for '%v'", ghSource)
conf := &oauth2.Config{}
// Authenticated access allows for 5000 reqs/hour
client := github.NewClient(conf.Client(nil, &oauth2.Token{AccessToken: i.accessToken}))
var allReleases []github.RepositoryRelease
listOpts := &github.ListOptions{PerPage: 30, Page: 0}
for {
releases, resp, err := client.Repositories.ListReleases(ghSource.Owner, ghSource.Repo, listOpts)
if err != nil {
return allReleases, bosherr.WrapError(err, "Listing github releases")
}
// Unauthenticated access can only be used up to 60 reqs/hour
if resp.Rate.Remaining < 50 {
waitD := resp.Rate.Reset.Sub(time.Now())
i.logger.Debug(i.logTag, "Sleeping for '%v' until github rate-limiting resets", waitD)
time.Sleep(waitD)
} else {
i.logger.Debug(i.logTag, "Left with '%d' requests for github for this hour", resp.Rate.Remaining)
}
allReleases = append(allReleases, releases...)
if resp.NextPage == 0 {
break
}
if len(allReleases) > 200 {
i.logger.Debug(i.logTag, "Found '%d' releases on github for '%v'", len(allReleases), ghSource)
}
listOpts.Page = resp.NextPage
}
return allReleases, nil
}