github.com/nektos/act@v0.2.63-0.20240520024548-8acde99bfa9c/pkg/container/docker_build.go (about) 1 //go:build !(WITHOUT_DOCKER || !(linux || darwin || windows || netbsd)) 2 3 package container 4 5 import ( 6 "context" 7 "io" 8 "os" 9 "path/filepath" 10 11 "github.com/docker/docker/api/types" 12 "github.com/docker/docker/pkg/archive" 13 14 // github.com/docker/docker/builder/dockerignore is deprecated 15 "github.com/moby/buildkit/frontend/dockerfile/dockerignore" 16 "github.com/moby/patternmatcher" 17 18 "github.com/nektos/act/pkg/common" 19 ) 20 21 // NewDockerBuildExecutor function to create a run executor for the container 22 func NewDockerBuildExecutor(input NewDockerBuildExecutorInput) common.Executor { 23 return func(ctx context.Context) error { 24 logger := common.Logger(ctx) 25 if input.Platform != "" { 26 logger.Infof("%sdocker build -t %s --platform %s %s", logPrefix, input.ImageTag, input.Platform, input.ContextDir) 27 } else { 28 logger.Infof("%sdocker build -t %s %s", logPrefix, input.ImageTag, input.ContextDir) 29 } 30 if common.Dryrun(ctx) { 31 return nil 32 } 33 34 cli, err := GetDockerClient(ctx) 35 if err != nil { 36 return err 37 } 38 defer cli.Close() 39 40 logger.Debugf("Building image from '%v'", input.ContextDir) 41 42 tags := []string{input.ImageTag} 43 options := types.ImageBuildOptions{ 44 Tags: tags, 45 Remove: true, 46 Platform: input.Platform, 47 AuthConfigs: LoadDockerAuthConfigs(ctx), 48 Dockerfile: input.Dockerfile, 49 } 50 var buildContext io.ReadCloser 51 if input.BuildContext != nil { 52 buildContext = io.NopCloser(input.BuildContext) 53 } else { 54 buildContext, err = createBuildContext(ctx, input.ContextDir, input.Dockerfile) 55 } 56 if err != nil { 57 return err 58 } 59 60 defer buildContext.Close() 61 62 logger.Debugf("Creating image from context dir '%s' with tag '%s' and platform '%s'", input.ContextDir, input.ImageTag, input.Platform) 63 resp, err := cli.ImageBuild(ctx, buildContext, options) 64 65 err = logDockerResponse(logger, resp.Body, err != nil) 66 if err != nil { 67 return err 68 } 69 return nil 70 } 71 } 72 func createBuildContext(ctx context.Context, contextDir string, relDockerfile string) (io.ReadCloser, error) { 73 common.Logger(ctx).Debugf("Creating archive for build context dir '%s' with relative dockerfile '%s'", contextDir, relDockerfile) 74 75 // And canonicalize dockerfile name to a platform-independent one 76 relDockerfile = archive.CanonicalTarNameForPath(relDockerfile) 77 78 f, err := os.Open(filepath.Join(contextDir, ".dockerignore")) 79 if err != nil && !os.IsNotExist(err) { 80 return nil, err 81 } 82 defer f.Close() 83 84 var excludes []string 85 if err == nil { 86 excludes, err = dockerignore.ReadAll(f) 87 if err != nil { 88 return nil, err 89 } 90 } 91 92 // If .dockerignore mentions .dockerignore or the Dockerfile 93 // then make sure we send both files over to the daemon 94 // because Dockerfile is, obviously, needed no matter what, and 95 // .dockerignore is needed to know if either one needs to be 96 // removed. The daemon will remove them for us, if needed, after it 97 // parses the Dockerfile. Ignore errors here, as they will have been 98 // caught by validateContextDirectory above. 99 var includes = []string{"."} 100 keepThem1, _ := patternmatcher.Matches(".dockerignore", excludes) 101 keepThem2, _ := patternmatcher.Matches(relDockerfile, excludes) 102 if keepThem1 || keepThem2 { 103 includes = append(includes, ".dockerignore", relDockerfile) 104 } 105 106 compression := archive.Uncompressed 107 buildCtx, err := archive.TarWithOptions(contextDir, &archive.TarOptions{ 108 Compression: compression, 109 ExcludePatterns: excludes, 110 IncludeFiles: includes, 111 }) 112 if err != nil { 113 return nil, err 114 } 115 116 return buildCtx, nil 117 }