@@ -26,18 +26,20 @@ type PushOpts struct {
26
26
Password * dagger.Secret
27
27
}
28
28
29
- func (p PushOpts ) getChartFqdn ( name string ) string {
29
+ func (p PushOpts ) getProtocol ( ) string {
30
30
if p .Oci {
31
- return fmt .Sprintf ("oci://%s/%s/%s" , p .Registry , p .Repository , name )
31
+ return "oci"
32
+ } else {
33
+ return "https"
32
34
}
33
- return fmt .Sprintf ("%s/%s/%s" , p .Registry , p .Repository , name )
34
35
}
35
36
36
37
func (p PushOpts ) getRepoFqdn () string {
37
- if p .Oci {
38
- return fmt .Sprintf ("oci://%s/%s" , p .Registry , p .Repository )
39
- }
40
- return fmt .Sprintf ("%s/%s" , p .Registry , p .Repository )
38
+ return fmt .Sprintf ("%s://%s/%s" , p .getProtocol (), p .Registry , p .Repository )
39
+ }
40
+
41
+ func (p PushOpts ) getChartFqdn (name string ) string {
42
+ return fmt .Sprintf ("%s/%s" , p .getRepoFqdn (), name )
41
43
}
42
44
43
45
// Get and display the version of the Helm Chart located inside the given directory.
@@ -58,7 +60,7 @@ func (h *Helm) Version(
58
60
return strings .TrimSpace (version ), nil
59
61
}
60
62
61
- // Packages and pushes a Helm chart to a specified OCI-compatible registry with authentication.
63
+ // Packages and pushes a Helm chart to a specified OCI-compatible (by default) registry with authentication.
62
64
//
63
65
// Returns true if the chart was successfully pushed, or false if the chart already exists, with error handling for push failures.
64
66
//
@@ -70,6 +72,19 @@ func (h *Helm) Version(
70
72
// --username $REGISTRY_HELM_USER \
71
73
// --password env:REGISTRY_HELM_PASSWORD \
72
74
// --directory ./examples/testdata/mychart/
75
+ //
76
+ // Example usage for pushing to a legacy (non-OCI) Helm repository assuming the repo name is 'helm'. If your target URL
77
+ // requires a vendor-specific path prefix (for example, JFrog Artifactory usually requires 'artifactory' before the repo
78
+ // name) then add it before the repository name. If you want to put the chart in a subpath in
79
+ // the repository, then append that to the end of the repository name.
80
+ //
81
+ // dagger call package-push \
82
+ // --registry registry.puzzle.ch \
83
+ // --repository vendor-specific-prefix/helm/optional/subpath/in/repository \
84
+ // --username $REGISTRY_HELM_USER \
85
+ // --password env:REGISTRY_HELM_PASSWORD \
86
+ // --directory ./examples/testdata/mychart/ \
87
+ // --use-non-oci-helm-repo=true
73
88
func (h * Helm ) PackagePush (
74
89
// method call context
75
90
ctx context.Context ,
@@ -83,11 +98,15 @@ func (h *Helm) PackagePush(
83
98
username string ,
84
99
// registry login password
85
100
password * dagger.Secret ,
101
+ // use a non-OCI (legacy) Helm repository
102
+ // +optional
103
+ // +default=false
104
+ useNonOciHelmRepo bool , // Dev note: We are forced to use default=false due to https://github.com/dagger/dagger/issues/8810
86
105
) (bool , error ) {
87
106
opts := PushOpts {
88
107
Registry : registry ,
89
108
Repository : repository ,
90
- Oci : true ,
109
+ Oci : ! useNonOciHelmRepo ,
91
110
Username : username ,
92
111
Password : password ,
93
112
}
@@ -110,38 +129,47 @@ func (h *Helm) PackagePush(
110
129
}
111
130
112
131
name = strings .TrimSpace (name )
132
+ pkgFile := fmt .Sprintf ("%s-%s.tgz" , name , version )
113
133
114
- c , err = c .
115
- WithEnvVariable ("REGISTRY_URL" , opts .Registry ).
116
- WithEnvVariable ("REGISTRY_USERNAME" , opts .Username ).
117
- WithSecretVariable ("REGISTRY_PASSWORD" , opts .Password ).
118
- WithExec ([]string {"sh" , "-c" , `echo ${REGISTRY_PASSWORD} | helm registry login ${REGISTRY_URL} --username ${REGISTRY_USERNAME} --password-stdin` }).
119
- Sync (ctx )
134
+ chartExists , err := h .doesChartExistOnRepo (ctx , c , & opts , name , version )
120
135
if err != nil {
121
136
return false , err
122
137
}
123
138
124
- //TODO: Refactor with return
125
- c , err = c .WithExec ([]string {"sh" , "-c" , fmt .Sprintf ("helm show chart %s --version %s; echo -n $? > /ec" , opts .getChartFqdn (name ), version )}).Sync (ctx )
126
- if err != nil {
127
- return false , err
139
+ if chartExists {
140
+ return false , nil
128
141
}
129
142
130
- exc , err := c .File ("/ec" ).Contents (ctx )
143
+ c , err = c .WithExec ([]string {"helm" , "dependency" , "update" , "." }).
144
+ WithExec ([]string {"helm" , "package" , "." }).
145
+ WithExec ([]string {"sh" , "-c" , "ls" }).
146
+ Sync (ctx )
147
+
131
148
if err != nil {
132
149
return false , err
133
150
}
134
151
135
- if exc == "0" {
136
- //Chart exists
137
- return false , nil
152
+ if useNonOciHelmRepo {
153
+ curlCmd := []string {
154
+ `curl --variable %REGISTRY_USERNAME` ,
155
+ `--variable %REGISTRY_PASSWORD` ,
156
+ `--expand-user "{{REGISTRY_USERNAME}}:{{REGISTRY_PASSWORD}}"` ,
157
+ `-T` ,
158
+ pkgFile ,
159
+ opts .getRepoFqdn () + "/" ,
160
+ }
161
+
162
+ c , err = c .
163
+ WithEnvVariable ("REGISTRY_USERNAME" , opts .Username ).
164
+ WithSecretVariable ("REGISTRY_PASSWORD" , opts .Password ).
165
+ WithExec ([]string {"sh" , "-c" , strings .Join (curlCmd , " " )}).
166
+ Sync (ctx )
167
+ } else {
168
+ c , err = c .
169
+ WithExec ([]string {"helm" , "push" , pkgFile , opts .getRepoFqdn ()}).
170
+ Sync (ctx )
138
171
}
139
172
140
- _ , err = c .WithExec ([]string {"helm" , "dependency" , "update" , "." }).
141
- WithExec ([]string {"helm" , "package" , "." }).
142
- WithExec ([]string {"sh" , "-c" , "ls" }).
143
- WithExec ([]string {"helm" , "push" , fmt .Sprintf ("%s-%s.tgz" , name , version ), opts .getRepoFqdn ()}).
144
- Sync (ctx )
145
173
if err != nil {
146
174
return false , err
147
175
}
@@ -203,6 +231,78 @@ func (h *Helm) Lint(
203
231
return out , nil
204
232
}
205
233
234
+ func (h * Helm ) doesChartExistOnRepo (
235
+ ctx context.Context ,
236
+ c * dagger.Container ,
237
+ opts * PushOpts ,
238
+ name string ,
239
+ version string ,
240
+ ) (bool , error ) {
241
+ if opts .Oci {
242
+ c , err := c .
243
+ WithEnvVariable ("REGISTRY_URL" , opts .Registry ).
244
+ WithEnvVariable ("REGISTRY_USERNAME" , opts .Username ).
245
+ WithSecretVariable ("REGISTRY_PASSWORD" , opts .Password ).
246
+ WithExec ([]string {"sh" , "-c" , `echo ${REGISTRY_PASSWORD} | helm registry login ${REGISTRY_URL} --username ${REGISTRY_USERNAME} --password-stdin` }).
247
+ Sync (ctx )
248
+ if err != nil {
249
+ return false , err
250
+ }
251
+
252
+ //TODO: Refactor with return
253
+ c , err = c .WithExec ([]string {"sh" , "-c" , fmt .Sprintf ("helm show chart %s --version %s; echo -n $? > /ec" , opts .getChartFqdn (name ), version )}).Sync (ctx )
254
+ if err != nil {
255
+ return false , err
256
+ }
257
+
258
+ exc , err := c .File ("/ec" ).Contents (ctx )
259
+ if err != nil {
260
+ return false , err
261
+ }
262
+
263
+ if exc == "0" {
264
+ //Chart exists
265
+ return true , nil
266
+ }
267
+
268
+ return false , nil
269
+ }
270
+ // else non-OCI
271
+ pkgFile := fmt .Sprintf ("%s-%s.tgz" , name , version )
272
+ // Do a GET of the chart but with response headers only so we do not download the chart
273
+ curlCmd := []string {
274
+ `curl --variable %REGISTRY_USERNAME` ,
275
+ `--variable %REGISTRY_PASSWORD` ,
276
+ `--expand-user "{{REGISTRY_USERNAME}}:{{REGISTRY_PASSWORD}}"` ,
277
+ opts .getChartFqdn (pkgFile ),
278
+ `--output /dev/null` ,
279
+ `--silent -Iw '%{http_code}'` ,
280
+ }
281
+
282
+ httpCode , err := c .
283
+ WithEnvVariable ("REGISTRY_USERNAME" , opts .Username ).
284
+ WithSecretVariable ("REGISTRY_PASSWORD" , opts .Password ).
285
+ WithExec ([]string {"sh" , "-c" , strings .Join (curlCmd , " " )}).
286
+ Stdout (ctx )
287
+
288
+ if err != nil {
289
+ return false , err
290
+ }
291
+
292
+ httpCode = strings .TrimSpace (httpCode )
293
+
294
+ if httpCode == "200" {
295
+ // Chart exists
296
+ return true , nil
297
+ }
298
+
299
+ if httpCode == "404" {
300
+ return false , nil
301
+ }
302
+
303
+ return false , fmt .Errorf ("Server returned error code %s checking for chart existence on server." , httpCode )
304
+ }
305
+
206
306
func (h * Helm ) hasMissingDependencies (
207
307
// method call context
208
308
ctx context.Context ,
0 commit comments