diff --git a/frontend/dockerfile/dockerfile2llb/convert.go b/frontend/dockerfile/dockerfile2llb/convert.go index a7375949e8bf9..862c8ff00ba2d 100644 --- a/frontend/dockerfile/dockerfile2llb/convert.go +++ b/frontend/dockerfile/dockerfile2llb/convert.go @@ -743,17 +743,18 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error { } if err == nil { err = dispatchCopy(d, copyConfig{ - params: c.SourcesAndDest, - source: opt.buildContext, - isAddCommand: true, - cmdToPrint: c, - chown: c.Chown, - chmod: c.Chmod, - link: c.Link, - keepGitDir: c.KeepGitDir, - checksum: checksum, - location: c.Location(), - opt: opt, + params: c.SourcesAndDest, + excludePatterns: c.ExcludePatterns, + source: opt.buildContext, + isAddCommand: true, + cmdToPrint: c, + chown: c.Chown, + chmod: c.Chmod, + link: c.Link, + keepGitDir: c.KeepGitDir, + checksum: checksum, + location: c.Location(), + opt: opt, }) } if err == nil { @@ -795,16 +796,17 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error { l = src.state } err = dispatchCopy(d, copyConfig{ - params: c.SourcesAndDest, - source: l, - isAddCommand: false, - cmdToPrint: c, - chown: c.Chown, - chmod: c.Chmod, - link: c.Link, - parents: c.Parents, - location: c.Location(), - opt: opt, + params: c.SourcesAndDest, + excludePatterns: c.ExcludePatterns, + source: l, + isAddCommand: false, + cmdToPrint: c, + chown: c.Chown, + chmod: c.Chmod, + link: c.Link, + parents: c.Parents, + location: c.Location(), + opt: opt, }) if err == nil { if len(cmd.sources) == 0 { @@ -1111,6 +1113,14 @@ func dispatchWorkdir(d *dispatchState, c *instructions.WorkdirCommand, commit bo return nil } +type excludeOnCopy struct { + patterns []string +} + +func (e *excludeOnCopy) SetCopyOption(i *llb.CopyInfo) { + i.ExcludePatterns = append(i.ExcludePatterns, e.patterns...) +} + func dispatchCopy(d *dispatchState, cfg copyConfig) error { dest, err := pathRelativeToWorkingDir(d.state, cfg.params.DestPath, *d.platform) if err != nil { @@ -1127,6 +1137,8 @@ func dispatchCopy(d *dispatchState, cfg copyConfig) error { copyOpt = append(copyOpt, llb.WithUser(cfg.chown)) } + copyOpt = append(copyOpt, &excludeOnCopy{cfg.excludePatterns}) + var mode *os.FileMode if cfg.chmod != "" { p, err := strconv.ParseUint(cfg.chmod, 8, 32) @@ -1318,18 +1330,19 @@ func dispatchCopy(d *dispatchState, cfg copyConfig) error { } type copyConfig struct { - params instructions.SourcesAndDest - source llb.State - isAddCommand bool - cmdToPrint fmt.Stringer - chown string - chmod string - link bool - keepGitDir bool - checksum digest.Digest - parents bool - location []parser.Range - opt dispatchOpt + params instructions.SourcesAndDest + excludePatterns []string + source llb.State + isAddCommand bool + cmdToPrint fmt.Stringer + chown string + chmod string + link bool + keepGitDir bool + checksum digest.Digest + parents bool + location []parser.Range + opt dispatchOpt } func dispatchMaintainer(d *dispatchState, c *instructions.MaintainerCommand) error { diff --git a/frontend/dockerfile/dockerfile2llb/convert_test.go b/frontend/dockerfile/dockerfile2llb/convert_test.go index bdc58cb2c9281..b70b7c81fadc0 100644 --- a/frontend/dockerfile/dockerfile2llb/convert_test.go +++ b/frontend/dockerfile/dockerfile2llb/convert_test.go @@ -208,3 +208,19 @@ COPY --from=stage1 f2 /sub/ _, _, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{}) assert.EqualError(t, err, "circular dependency detected on stage: stage0") } + +func TestDockerfileCopyExcludePatterns(t *testing.T) { + df := `FROM scratch +COPY --exclude=src/*.go --exclude=tmp/*.txt dir /sub/ +` + _, _, _, err := Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{}) + assert.NoError(t, err) +} + +func TestDockerfileAddExcludePatterns(t *testing.T) { + df := `FROM scratch +ADD --exclude=src/*.go --exclude=tmp/*.txt dir /sub/ +` + _, _, _, err := Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{}) + assert.NoError(t, err) +} diff --git a/frontend/dockerfile/instructions/commands.go b/frontend/dockerfile/instructions/commands.go index 08b99d1fa199a..87252f9f17ddb 100644 --- a/frontend/dockerfile/instructions/commands.go +++ b/frontend/dockerfile/instructions/commands.go @@ -239,11 +239,12 @@ func (s *SourcesAndDest) ExpandRaw(expander SingleWordExpander) error { type AddCommand struct { withNameAndCode SourcesAndDest - Chown string - Chmod string - Link bool - KeepGitDir bool // whether to keep .git dir, only meaningful for git sources - Checksum string + Chown string + Chmod string + Link bool + ExcludePatterns []string + KeepGitDir bool // whether to keep .git dir, only meaningful for git sources + Checksum string } func (c *AddCommand) Expand(expander SingleWordExpander) error { @@ -270,11 +271,12 @@ func (c *AddCommand) Expand(expander SingleWordExpander) error { type CopyCommand struct { withNameAndCode SourcesAndDest - From string - Chown string - Chmod string - Link bool - Parents bool // parents preserves directory structure + From string + Chown string + Chmod string + Link bool + ExcludePatterns []string + Parents bool // parents preserves directory structure } func (c *CopyCommand) Expand(expander SingleWordExpander) error { diff --git a/frontend/dockerfile/instructions/parse.go b/frontend/dockerfile/instructions/parse.go index 0ce7f37d5d4e4..e35355e5f2c10 100644 --- a/frontend/dockerfile/instructions/parse.go +++ b/frontend/dockerfile/instructions/parse.go @@ -284,6 +284,8 @@ func parseAdd(req parseRequest) (*AddCommand, error) { if len(req.args) < 2 { return nil, errNoDestinationArgument("ADD") } + + flExcludes := req.flags.AddStrings("exclude") flChown := req.flags.AddString("chown", "") flChmod := req.flags.AddString("chmod", "") flLink := req.flags.AddBool("link", false) @@ -306,6 +308,7 @@ func parseAdd(req parseRequest) (*AddCommand, error) { Link: flLink.Value == "true", KeepGitDir: flKeepGitDir.Value == "true", Checksum: flChecksum.Value, + ExcludePatterns: flExcludes.StringValues, }, nil } @@ -313,6 +316,8 @@ func parseCopy(req parseRequest) (*CopyCommand, error) { if len(req.args) < 2 { return nil, errNoDestinationArgument("COPY") } + + flExcludes := req.flags.AddStrings("exclude") flChown := req.flags.AddString("chown", "") flFrom := req.flags.AddString("from", "") flChmod := req.flags.AddString("chmod", "") @@ -335,6 +340,7 @@ func parseCopy(req parseRequest) (*CopyCommand, error) { Chmod: flChmod.Value, Link: flLink.Value == "true", Parents: (flParents.Value == "true") && parentsEnabled, // silently ignore if not -labs + ExcludePatterns: flExcludes.StringValues, }, nil }