@@ -19,11 +19,13 @@ import (
19
19
"fmt"
20
20
"io"
21
21
"io/ioutil"
22
+ "math/rand"
22
23
"net/http"
23
24
"net/url"
24
25
"os"
25
26
"path/filepath"
26
27
"strings"
28
+ "time"
27
29
28
30
"github.com/pkg/errors"
29
31
"helm.sh/helm/v3/pkg/chart/loader"
@@ -41,13 +43,27 @@ import (
41
43
type GitHub interface {
42
44
CreateRelease (ctx context.Context , input * github.Release ) error
43
45
GetRelease (ctx context.Context , tag string ) (* github.Release , error )
46
+ CreatePullRequest (owner string , repo string , message string , head string , base string ) (string , error )
44
47
}
45
48
46
49
type HttpClient interface {
47
50
Get (url string ) (* http.Response , error )
48
51
}
49
52
50
- type DefaultHttpClient struct {
53
+ type Git interface {
54
+ AddWorktree (workingDir string , committish string ) (string , error )
55
+ RemoveWorktree (workingDir string , path string ) error
56
+ Add (workingDir string , args ... string ) error
57
+ Commit (workingDir string , message string ) error
58
+ Push (workingDir string , args ... string ) error
59
+ }
60
+
61
+ type DefaultHttpClient struct {}
62
+
63
+ var letters = []rune ("abcdefghijklmnopqrstuvwxyz0123456789" )
64
+
65
+ func init () {
66
+ rand .Seed (time .Now ().UnixNano ())
51
67
}
52
68
53
69
func (c * DefaultHttpClient ) Get (url string ) (resp * http.Response , err error ) {
@@ -58,17 +74,19 @@ type Releaser struct {
58
74
config * config.Options
59
75
github GitHub
60
76
httpClient HttpClient
77
+ git Git
61
78
}
62
79
63
- func NewReleaser (config * config.Options , github GitHub ) * Releaser {
80
+ func NewReleaser (config * config.Options , github GitHub , git Git ) * Releaser {
64
81
return & Releaser {
65
82
config : config ,
66
83
github : github ,
67
84
httpClient : & DefaultHttpClient {},
85
+ git : git ,
68
86
}
69
87
}
70
88
71
- // UpdateIndexFile index.yaml file for a give git repo
89
+ // UpdateIndexFile updates the index.yaml file for a given Git repo
72
90
func (r * Releaser ) UpdateIndexFile () (bool , error ) {
73
91
// if path doesn't end with index.yaml we can try and fix it
74
92
if filepath .Base (r .config .IndexPath ) != "index.yaml" {
@@ -103,13 +121,13 @@ func (r *Releaser) UpdateIndexFile() (bool, error) {
103
121
return false , err
104
122
}
105
123
106
- fmt .Printf ("====> Using existing index at %s\n " , r .config .IndexPath )
124
+ fmt .Printf ("Using existing index at %s\n " , r .config .IndexPath )
107
125
indexFile , err = repo .LoadIndexFile (r .config .IndexPath )
108
126
if err != nil {
109
127
return false , err
110
128
}
111
129
} else {
112
- fmt .Printf ("====> UpdateIndexFile new index at %s\n " , r .config .IndexPath )
130
+ fmt .Printf ("UpdateIndexFile new index at %s\n " , r .config .IndexPath )
113
131
indexFile = repo .NewIndexFile ()
114
132
}
115
133
@@ -133,7 +151,7 @@ func (r *Releaser) UpdateIndexFile() (bool, error) {
133
151
baseName := strings .TrimSuffix (name , filepath .Ext (name ))
134
152
tagParts := r .splitPackageNameAndVersion (baseName )
135
153
packageName , packageVersion := tagParts [0 ], tagParts [1 ]
136
- fmt .Printf ("====> Found %s-%s.tgz\n " , packageName , packageVersion )
154
+ fmt .Printf ("Found %s-%s.tgz\n " , packageName , packageVersion )
137
155
if _ , err := indexFile .Get (packageName , packageVersion ); err != nil {
138
156
if err := r .addToIndexFile (indexFile , downloadUrl .String ()); err != nil {
139
157
return false , err
@@ -144,15 +162,62 @@ func (r *Releaser) UpdateIndexFile() (bool, error) {
144
162
}
145
163
}
146
164
147
- if update {
148
- fmt .Printf ("--> Updating index %s\n " , r .config .IndexPath )
149
- indexFile .SortEntries ()
150
- return true , indexFile .WriteFile (r .config .IndexPath , 0644 )
151
- } else {
152
- fmt .Printf ("--> Index %s did not change\n " , r .config .IndexPath )
165
+ if ! update {
166
+ fmt .Printf ("Index %s did not change\n " , r .config .IndexPath )
167
+ return false , nil
168
+ }
169
+
170
+ fmt .Printf ("Updating index %s\n " , r .config .IndexPath )
171
+ indexFile .SortEntries ()
172
+
173
+ if err := indexFile .WriteFile (r .config .IndexPath , 0644 ); err != nil {
174
+ return false , err
175
+ }
176
+
177
+ if ! r .config .Push && ! r .config .PR {
178
+ return true , nil
179
+ }
180
+
181
+ worktree , err := r .git .AddWorktree ("" , "origin/" + r .config .PagesBranch )
182
+ if err != nil {
183
+ return false , err
184
+ }
185
+ defer r .git .RemoveWorktree ("" , worktree )
186
+
187
+ indexYamlPath := filepath .Join (worktree , "index.yaml" )
188
+ if err := copyFile (r .config .IndexPath , indexYamlPath ); err != nil {
189
+ return false , err
190
+ }
191
+ if err := r .git .Add (worktree , indexYamlPath ); err != nil {
192
+ return false , err
193
+ }
194
+ if err := r .git .Commit (worktree , "Update index.yaml" ); err != nil {
195
+ return false , err
196
+ }
197
+
198
+ pushURL := fmt .Sprintf ("https://x-access-token:%s@github.com/%s/%s" , r .config .Token , r .config .Owner , r .config .GitRepo )
199
+
200
+ if r .config .Push {
201
+ fmt .Printf ("Pushing to branch %q\n " , r .config .PagesBranch )
202
+ if err := r .git .Push (worktree , pushURL , "HEAD:refs/heads/" + r .config .PagesBranch ); err != nil {
203
+ return false , err
204
+ }
205
+ } else if r .config .PR {
206
+ branch := fmt .Sprintf ("chart-releaser-%s" , randomString (16 ))
207
+
208
+ fmt .Printf ("Pushing to branch %q\n " , branch )
209
+ if err := r .git .Push (worktree , pushURL , "HEAD:refs/heads/" + branch ); err != nil {
210
+ return false , err
211
+ }
212
+ fmt .Printf ("Creating pull request against branch %q\n " , r .config .PagesBranch )
213
+ prURL , err := r .github .CreatePullRequest (r .config .Owner , r .config .GitRepo , "Update index.yaml" , branch , r .config .PagesBranch )
214
+ if err != nil {
215
+ return false , err
216
+ }
217
+ fmt .Println ("Pull request created:" , prURL )
153
218
}
154
219
155
- return false , nil
220
+ return true , nil
156
221
}
157
222
158
223
func (r * Releaser ) splitPackageNameAndVersion (pkg string ) []string {
@@ -164,13 +229,13 @@ func (r *Releaser) addToIndexFile(indexFile *repo.IndexFile, url string) error {
164
229
arch := filepath .Join (r .config .PackagePath , filepath .Base (url ))
165
230
166
231
// extract chart metadata
167
- fmt .Printf ("====> Extracting chart metadata from %s\n " , arch )
232
+ fmt .Printf ("Extracting chart metadata from %s\n " , arch )
168
233
c , err := loader .LoadFile (arch )
169
234
if err != nil {
170
235
return errors .Wrapf (err , "%s is not a helm chart package" , arch )
171
236
}
172
237
// calculate hash
173
- fmt .Printf ("====> Calculating Hash for %s\n " , arch )
238
+ fmt .Printf ("Calculating Hash for %s\n " , arch )
174
239
hash , err := provenance .DigestFile (arch )
175
240
if err != nil {
176
241
return err
@@ -187,7 +252,7 @@ func (r *Releaser) addToIndexFile(indexFile *repo.IndexFile, url string) error {
187
252
return nil
188
253
}
189
254
190
- // CreateReleases finds and uploads helm chart packages to github
255
+ // CreateReleases finds and uploads Helm chart packages to GitHub
191
256
func (r * Releaser ) CreateReleases () error {
192
257
packages , err := r .getListOfPackages (r .config .PackagePath )
193
258
if err != nil {
@@ -230,3 +295,28 @@ func (r *Releaser) CreateReleases() error {
230
295
func (r * Releaser ) getListOfPackages (dir string ) ([]string , error ) {
231
296
return filepath .Glob (filepath .Join (dir , "*.tgz" ))
232
297
}
298
+
299
+ func copyFile (srcFile string , dstFile string ) error {
300
+ source , err := os .Open (srcFile )
301
+ if err != nil {
302
+ return err
303
+ }
304
+ defer source .Close ()
305
+
306
+ destination , err := os .Create (dstFile )
307
+ if err != nil {
308
+ return err
309
+ }
310
+ defer destination .Close ()
311
+
312
+ _ , err = io .Copy (destination , source )
313
+ return err
314
+ }
315
+
316
+ func randomString (n int ) string {
317
+ b := make ([]rune , n )
318
+ for i := range b {
319
+ b [i ] = letters [rand .Intn (len (letters ))]
320
+ }
321
+ return string (b )
322
+ }
0 commit comments