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  }