8
8
"fmt"
9
9
"io"
10
10
"io/ioutil"
11
+ "net/http"
11
12
"os"
12
13
"os/exec"
13
14
"path/filepath"
@@ -37,7 +38,9 @@ type BuildOpts struct {
37
38
LogFile * os.File
38
39
}
39
40
40
- // BuildLocal
41
+ // BuildLocal builds the image via docker
42
+ // If the DOCKER_BUILDKIT environment variable is set, builds will switch to
43
+ // using the docker binary directly (with buildkit enabled)
41
44
func (a * Agent ) BuildLocal (ctx context.Context , opts * BuildOpts ) (err error ) {
42
45
if os .Getenv ("DOCKER_BUILDKIT" ) == "1" {
43
46
return buildLocalWithBuildkit (ctx , * opts )
@@ -178,6 +181,7 @@ func AddDockerfileToBuildContext(dockerfileCtx io.ReadCloser, buildCtx io.ReadCl
178
181
}
179
182
180
183
func buildLocalWithBuildkit (ctx context.Context , opts BuildOpts ) error {
184
+ fmt .Println ("Triggering build via buildkit" )
181
185
if _ , err := exec .LookPath ("docker" ); err != nil {
182
186
return fmt .Errorf ("unable to find docker binary in PATH for buildkit build: %w" , err )
183
187
}
@@ -203,12 +207,35 @@ func buildLocalWithBuildkit(ctx context.Context, opts BuildOpts) error {
203
207
extraDockerArgs = parsedFields
204
208
}
205
209
210
+ cacheFrom := fmt .Sprintf ("%s:%s" , opts .ImageRepo , opts .CurrentTag )
211
+ cacheTo := ""
212
+ if ok , _ := isRunningInGithubActions (); ok && os .Getenv ("BUILDKIT_CACHE_EXPORTER" ) == "gha" {
213
+ fmt .Println ("Github Actions environment detected, switching to the GitHub Actions cache exporter" )
214
+ cacheFrom = "type=gha"
215
+ cacheTo = "type=gha"
216
+
217
+ // CacheMode is set separately to avoid cases where builds may timeout for
218
+ // dockerfiles with many layers.
219
+ // See https://github.com/moby/buildkit/issues/2276 for details.
220
+ cacheMode := os .Getenv ("BUILDKIT_CACHE_MODE" )
221
+ if cacheMode == "min" || cacheMode == "max" {
222
+ fmt .Printf ("Setting GHA cache mode to %s\n " , cacheMode )
223
+ cacheTo = fmt .Sprintf ("type=gha,mode=%s" , cacheMode )
224
+ } else if cacheMode != "" {
225
+ return errors .New ("error while parsing buildkit environment variables: BUILDKIT_CACHE_MODE set to invalid value, valid values: min, max" )
226
+ }
227
+ }
228
+
206
229
commandArgs := []string {
207
230
"build" ,
208
231
"-f" , dockerfileName ,
209
232
"--tag" , fmt .Sprintf ("%s:%s" , opts .ImageRepo , opts .Tag ),
210
- "--cache-from" , fmt . Sprintf ( "%s:%s" , opts . ImageRepo , opts . CurrentTag ) ,
233
+ "--cache-from" , cacheFrom ,
211
234
}
235
+ if cacheTo != "" {
236
+ commandArgs = append (commandArgs , "--cache-to" , cacheTo )
237
+ }
238
+
212
239
for key , val := range opts .Env {
213
240
commandArgs = append (commandArgs , "--build-arg" , fmt .Sprintf ("%s=%s" , key , val ))
214
241
}
@@ -313,3 +340,33 @@ func sliceContainsString(haystack []string, needle string) bool {
313
340
314
341
return false
315
342
}
343
+
344
+ // isRunningInGithubActions detects if the environment is a github actions
345
+ // runner environment by validating certain environment variables and then
346
+ // making a call to the Github api to verify the run itself.
347
+ func isRunningInGithubActions () (bool , error ) {
348
+ for _ , key := range []string {"CI" , "GITHUB_RUN_ID" , "GITHUB_TOKEN" , "GITHUB_REPOSITORY" } {
349
+ if key == "" {
350
+ return false , nil
351
+ }
352
+ }
353
+
354
+ url := fmt .Sprintf ("https://api.github.com/repos/%s/actions/runs/%s" , os .Getenv ("GITHUB_REPOSITORY" ), os .Getenv ("GITHUB_RUN_ID" ))
355
+
356
+ req , err := http .NewRequest ("GET" , url , nil )
357
+ if err == nil {
358
+ return false , err
359
+ }
360
+ req .Header .Set ("Authorization" , fmt .Sprintf ("Bearer %s" , os .Getenv ("GITHUB_TOKEN" )))
361
+
362
+ client := http.Client {
363
+ Timeout : 5 * time .Second ,
364
+ }
365
+ resp , err := client .Do (req )
366
+ if err != nil {
367
+ return false , err
368
+ }
369
+ defer resp .Body .Close () //nolint:errcheck
370
+
371
+ return resp .StatusCode == http .StatusOK , nil
372
+ }
0 commit comments