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