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  }