8
8
"net/http"
9
9
"net/url"
10
10
"os"
11
+ "path"
11
12
"strings"
12
13
"time"
13
14
@@ -24,8 +25,17 @@ const (
24
25
)
25
26
26
27
var client * github.Client
28
+ var cacheDirBase string
29
+ var chartsCacheDir string
27
30
28
31
func init () {
32
+ if env , ok := os .LookupEnv ("HELMGITHUB_DEBUG_LOG" ); ok {
33
+ t , err := os .OpenFile (env , os .O_APPEND | os .O_CREATE | os .O_WRONLY , 0o644 )
34
+ if err != nil {
35
+ log .Panic (err )
36
+ }
37
+ log .SetOutput (t )
38
+ }
29
39
token , err := loadGithubToken ()
30
40
if err != nil {
31
41
log .Panic (err )
@@ -35,18 +45,11 @@ func init() {
35
45
)
36
46
tc := oauth2 .NewClient (context .Background (), ts )
37
47
client = github .NewClient (tc )
48
+ cacheDirBase = getCacheDirBase ()
49
+ chartsCacheDir = getChartCacheDir ()
38
50
}
39
51
40
52
func main () {
41
- if env , ok := os .LookupEnv ("HELMGITHUB_DEBUG_LOG" ); ok {
42
- t , err := os .OpenFile (env , os .O_APPEND | os .O_CREATE | os .O_WRONLY , 0o644 )
43
- if err != nil {
44
- log .Panic (err )
45
- }
46
- defer t .Close ()
47
- log .SetOutput (t )
48
- }
49
-
50
53
ctx , cancel := context .WithTimeout (context .Background (), 30 * time .Second )
51
54
defer cancel ()
52
55
@@ -65,17 +68,62 @@ func main() {
65
68
}
66
69
fmt .Println (string (bytes ))
67
70
} else {
68
- rc , err := fetchArchive ( ctx , uri )
71
+ cacheFile , err := openCacheFile ( uri )
69
72
if err != nil {
70
73
log .Panic (err )
71
74
}
72
- defer rc .Close ()
73
- _ , err = io .Copy (os .Stdout , rc )
74
- if err != nil {
75
- log .Panic (err )
75
+ defer cacheFile .Close ()
76
+ ok := validateDigest (getArchiveDigest (uri ), cacheFile .Name ())
77
+ if ! ok {
78
+ resp , err := fetchArchive (ctx , uri )
79
+ if err != nil {
80
+ _ = os .Remove (cacheFile .Name ())
81
+ log .Panic (err )
82
+ }
83
+ defer resp .Close ()
84
+ _ , err = io .Copy (io .MultiWriter (os .Stdout , cacheFile ), resp )
85
+ if err != nil {
86
+ _ = os .Remove (cacheFile .Name ())
87
+ log .Panic (err )
88
+ }
89
+ } else {
90
+ _ , err := io .Copy (os .Stdout , cacheFile )
91
+ if err != nil {
92
+ log .Panic (err )
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }
98
+
99
+ func getArchiveDigest (uri string ) string {
100
+ _ , r := parseOwnerRepository (uri )
101
+ bytes , err := os .ReadFile (path .Join (cacheDirBase , r + "-index.yaml" ))
102
+ if err != nil {
103
+ return ""
104
+ }
105
+ idx := helm.IndexFile {}
106
+ if err := yaml .Unmarshal (bytes , & idx ); err != nil {
107
+ return ""
108
+ }
109
+ for _ , versions := range idx .Entries {
110
+ for _ , version := range versions {
111
+ for _ , u := range version .URLs {
112
+ if strings .HasSuffix (u , uri ) {
113
+ return version .Digest
114
+ }
76
115
}
77
116
}
78
117
}
118
+ return ""
119
+ }
120
+
121
+ func validateDigest (digest string , fileName string ) bool {
122
+ df , err := helm .DigestFile (fileName )
123
+ if err != nil {
124
+ log .Panic (err )
125
+ }
126
+ return digest == df
79
127
}
80
128
81
129
func loadGithubToken () (string , error ) {
@@ -103,6 +151,29 @@ func getIndexBranch() string {
103
151
return "gh-pages"
104
152
}
105
153
154
+ func getChartCacheDir () string {
155
+ dir := getCacheDirBase ()
156
+ dir = path .Join (dir , "github" , "chart" )
157
+ if err := os .MkdirAll (dir , 0o777 ); err != nil {
158
+ log .Panic (err )
159
+ }
160
+ return dir
161
+ }
162
+
163
+ func getCacheDirBase () string {
164
+ var dir string
165
+ if env , ok := os .LookupEnv ("HELM_REPOSITORY_CACHE" ); ok {
166
+ dir = env
167
+ } else {
168
+ ucd , err := os .UserCacheDir ()
169
+ if err != nil {
170
+ log .Panic (err )
171
+ }
172
+ dir = ucd
173
+ }
174
+ return dir
175
+ }
176
+
106
177
func fetchIndexFile (ctx context.Context , uri string ) (helm.IndexFile , error ) {
107
178
owner , repository := parseOwnerRepository (uri )
108
179
contents , _ , _ , err := client .Repositories .GetContents (ctx , owner , repository , indexFilename , & github.RepositoryContentGetOptions {Ref : getIndexBranch ()})
@@ -121,6 +192,24 @@ func fetchIndexFile(ctx context.Context, uri string) (helm.IndexFile, error) {
121
192
return file , nil
122
193
}
123
194
195
+ func openCacheFile (uri string ) (* os.File , error ) {
196
+ artifactName := parseArtifactName (uri )
197
+ chartPath := path .Join (chartsCacheDir , artifactName + ".tgz" )
198
+ _ , err := os .Stat (chartPath )
199
+ if err != nil {
200
+ create , err := os .Create (chartPath )
201
+ if err != nil {
202
+ return nil , err
203
+ }
204
+ return create , nil
205
+ }
206
+ open , err := os .Open (chartPath )
207
+ if err != nil {
208
+ return nil , err
209
+ }
210
+ return open , nil
211
+ }
212
+
124
213
func fetchArchive (ctx context.Context , uri string ) (io.ReadCloser , error ) {
125
214
owner , repository := parseOwnerRepository (uri )
126
215
tag := parseArtifactName (uri )
0 commit comments