commit
8c9d66f7da
|
@ -193,6 +193,12 @@ func Local(name string, opts ...LocalOption) State {
|
||||||
if gi.IncludePatterns != "" {
|
if gi.IncludePatterns != "" {
|
||||||
attrs[pb.AttrIncludePatterns] = gi.IncludePatterns
|
attrs[pb.AttrIncludePatterns] = gi.IncludePatterns
|
||||||
}
|
}
|
||||||
|
if gi.ExcludePatterns != "" {
|
||||||
|
attrs[pb.AttrExcludePatterns] = gi.ExcludePatterns
|
||||||
|
}
|
||||||
|
if gi.SharedKeyHint != "" {
|
||||||
|
attrs[pb.AttrSharedKeyHint] = gi.SharedKeyHint
|
||||||
|
}
|
||||||
|
|
||||||
source := NewSource("local://"+name, attrs, gi.Metadata())
|
source := NewSource("local://"+name, attrs, gi.Metadata())
|
||||||
return NewState(source.Output())
|
return NewState(source.Output())
|
||||||
|
@ -216,15 +222,38 @@ func SessionID(id string) LocalOption {
|
||||||
|
|
||||||
func IncludePatterns(p []string) LocalOption {
|
func IncludePatterns(p []string) LocalOption {
|
||||||
return localOptionFunc(func(li *LocalInfo) {
|
return localOptionFunc(func(li *LocalInfo) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
li.IncludePatterns = ""
|
||||||
|
return
|
||||||
|
}
|
||||||
dt, _ := json.Marshal(p) // empty on error
|
dt, _ := json.Marshal(p) // empty on error
|
||||||
li.IncludePatterns = string(dt)
|
li.IncludePatterns = string(dt)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExcludePatterns(p []string) LocalOption {
|
||||||
|
return localOptionFunc(func(li *LocalInfo) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
li.ExcludePatterns = ""
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dt, _ := json.Marshal(p) // empty on error
|
||||||
|
li.ExcludePatterns = string(dt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func SharedKeyHint(h string) LocalOption {
|
||||||
|
return localOptionFunc(func(li *LocalInfo) {
|
||||||
|
li.SharedKeyHint = h
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type LocalInfo struct {
|
type LocalInfo struct {
|
||||||
opMetaWrapper
|
opMetaWrapper
|
||||||
SessionID string
|
SessionID string
|
||||||
IncludePatterns string
|
IncludePatterns string
|
||||||
|
ExcludePatterns string
|
||||||
|
SharedKeyHint string
|
||||||
}
|
}
|
||||||
|
|
||||||
func HTTP(url string, opts ...HTTPOption) State {
|
func HTTP(url string, opts ...HTTPOption) State {
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
package client
|
|
||||||
|
|
||||||
func getSharedKey(dir string) (string, error) {
|
|
||||||
return dir, nil // not implemented
|
|
||||||
}
|
|
|
@ -1,15 +1,18 @@
|
||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/builder/dockerignore"
|
||||||
"github.com/moby/buildkit/client/llb"
|
"github.com/moby/buildkit/client/llb"
|
||||||
"github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb"
|
"github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb"
|
||||||
"github.com/moby/buildkit/frontend/gateway/client"
|
"github.com/moby/buildkit/frontend/gateway/client"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -19,6 +22,7 @@ const (
|
||||||
keyFilename = "filename"
|
keyFilename = "filename"
|
||||||
exporterImageConfig = "containerimage.config"
|
exporterImageConfig = "containerimage.config"
|
||||||
defaultDockerfileName = "Dockerfile"
|
defaultDockerfileName = "Dockerfile"
|
||||||
|
dockerignoreFilename = ".dockerignore"
|
||||||
buildArgPrefix = "build-arg:"
|
buildArgPrefix = "build-arg:"
|
||||||
gitPrefix = "git://"
|
gitPrefix = "git://"
|
||||||
)
|
)
|
||||||
|
@ -37,6 +41,7 @@ func Build(ctx context.Context, c client.Client) error {
|
||||||
src := llb.Local(LocalNameDockerfile,
|
src := llb.Local(LocalNameDockerfile,
|
||||||
llb.IncludePatterns([]string{filename}),
|
llb.IncludePatterns([]string{filename}),
|
||||||
llb.SessionID(c.SessionID()),
|
llb.SessionID(c.SessionID()),
|
||||||
|
llb.SharedKeyHint(defaultDockerfileName),
|
||||||
)
|
)
|
||||||
var buildContext *llb.State
|
var buildContext *llb.State
|
||||||
if strings.HasPrefix(opts[LocalNameContext], gitPrefix) {
|
if strings.HasPrefix(opts[LocalNameContext], gitPrefix) {
|
||||||
|
@ -48,15 +53,52 @@ func Build(ctx context.Context, c client.Client) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ref, err := c.Solve(ctx, def.ToPB(), "", nil, false)
|
eg, ctx2 := errgroup.WithContext(ctx)
|
||||||
|
var dtDockerfile []byte
|
||||||
|
eg.Go(func() error {
|
||||||
|
ref, err := c.Solve(ctx2, def.ToPB(), "", nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
dtDockerfile, err := ref.ReadFile(ctx, filename)
|
dtDockerfile, err = ref.ReadFile(ctx2, filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
var excludes []string
|
||||||
|
eg.Go(func() error {
|
||||||
|
dockerignoreState := buildContext
|
||||||
|
if dockerignoreState == nil {
|
||||||
|
st := llb.Local(LocalNameContext,
|
||||||
|
llb.SessionID(c.SessionID()),
|
||||||
|
llb.IncludePatterns([]string{dockerignoreFilename}),
|
||||||
|
llb.SharedKeyHint(dockerignoreFilename),
|
||||||
|
)
|
||||||
|
dockerignoreState = &st
|
||||||
|
}
|
||||||
|
def, err := dockerignoreState.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ref, err := c.Solve(ctx2, def.ToPB(), "", nil, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dtDockerignore, err := ref.ReadFile(ctx2, dockerignoreFilename)
|
||||||
|
if err == nil {
|
||||||
|
excludes, err = dockerignore.ReadAll(bytes.NewBuffer(dtDockerignore))
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to parse dockerignore")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := eg.Wait(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
st, img, err := dockerfile2llb.Dockerfile2LLB(ctx, dtDockerfile, dockerfile2llb.ConvertOpt{
|
st, img, err := dockerfile2llb.Dockerfile2LLB(ctx, dtDockerfile, dockerfile2llb.ConvertOpt{
|
||||||
Target: opts[keyTarget],
|
Target: opts[keyTarget],
|
||||||
|
@ -64,6 +106,7 @@ func Build(ctx context.Context, c client.Client) error {
|
||||||
BuildArgs: filterBuildArgs(opts),
|
BuildArgs: filterBuildArgs(opts),
|
||||||
SessionID: c.SessionID(),
|
SessionID: c.SessionID(),
|
||||||
BuildContext: buildContext,
|
BuildContext: buildContext,
|
||||||
|
Excludes: excludes,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -38,6 +38,7 @@ type ConvertOpt struct {
|
||||||
BuildArgs map[string]string
|
BuildArgs map[string]string
|
||||||
SessionID string
|
SessionID string
|
||||||
BuildContext *llb.State
|
BuildContext *llb.State
|
||||||
|
Excludes []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State, *Image, error) {
|
func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State, *Image, error) {
|
||||||
|
@ -166,8 +167,11 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
|
||||||
if err := eg.Wait(); err != nil {
|
if err := eg.Wait(); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
buildContext := llb.Local(localNameContext,
|
||||||
buildContext := llb.Local(localNameContext, llb.SessionID(opt.SessionID))
|
llb.SessionID(opt.SessionID),
|
||||||
|
llb.ExcludePatterns(opt.Excludes),
|
||||||
|
llb.SharedKeyHint(localNameContext),
|
||||||
|
)
|
||||||
if opt.BuildContext != nil {
|
if opt.BuildContext != nil {
|
||||||
buildContext = *opt.BuildContext
|
buildContext = *opt.BuildContext
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ func TestIntegration(t *testing.T) {
|
||||||
testExportedHistory,
|
testExportedHistory,
|
||||||
testExposeExpansion,
|
testExposeExpansion,
|
||||||
testUser,
|
testUser,
|
||||||
|
testDockerignore,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,6 +504,78 @@ EXPOSE 5000
|
||||||
require.Equal(t, "5000/tcp", ports[2])
|
require.Equal(t, "5000/tcp", ports[2])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testDockerignore(t *testing.T, sb integration.Sandbox) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
dockerfile := []byte(`
|
||||||
|
FROM scratch
|
||||||
|
COPY . .
|
||||||
|
`)
|
||||||
|
|
||||||
|
dockerignore := []byte(`
|
||||||
|
ba*
|
||||||
|
Dockerfile
|
||||||
|
!bay
|
||||||
|
.dockerignore
|
||||||
|
`)
|
||||||
|
|
||||||
|
dir, err := tmpdir(
|
||||||
|
fstest.CreateFile("Dockerfile", dockerfile, 0600),
|
||||||
|
fstest.CreateFile("foo", []byte(`foo-contents`), 0600),
|
||||||
|
fstest.CreateFile("bar", []byte(`bar-contents`), 0600),
|
||||||
|
fstest.CreateFile("baz", []byte(`baz-contents`), 0600),
|
||||||
|
fstest.CreateFile("bay", []byte(`bay-contents`), 0600),
|
||||||
|
fstest.CreateFile(".dockerignore", dockerignore, 0600),
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
c, err := client.New(sb.Address())
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
destDir, err := ioutil.TempDir("", "buildkit")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer os.RemoveAll(destDir)
|
||||||
|
|
||||||
|
err = c.Solve(context.TODO(), nil, client.SolveOpt{
|
||||||
|
Frontend: "dockerfile.v0",
|
||||||
|
Exporter: client.ExporterLocal,
|
||||||
|
ExporterAttrs: map[string]string{
|
||||||
|
"output": destDir,
|
||||||
|
},
|
||||||
|
LocalDirs: map[string]string{
|
||||||
|
builder.LocalNameDockerfile: dir,
|
||||||
|
builder.LocalNameContext: dir,
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
dt, err := ioutil.ReadFile(filepath.Join(destDir, "foo"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "foo-contents", string(dt))
|
||||||
|
|
||||||
|
_, err = os.Stat(filepath.Join(destDir, ".dockerignore"))
|
||||||
|
require.Error(t, err)
|
||||||
|
require.True(t, os.IsNotExist(err))
|
||||||
|
|
||||||
|
_, err = os.Stat(filepath.Join(destDir, "Dockerfile"))
|
||||||
|
require.Error(t, err)
|
||||||
|
require.True(t, os.IsNotExist(err))
|
||||||
|
|
||||||
|
_, err = os.Stat(filepath.Join(destDir, "bar"))
|
||||||
|
require.Error(t, err)
|
||||||
|
require.True(t, os.IsNotExist(err))
|
||||||
|
|
||||||
|
_, err = os.Stat(filepath.Join(destDir, "baz"))
|
||||||
|
require.Error(t, err)
|
||||||
|
require.True(t, os.IsNotExist(err))
|
||||||
|
|
||||||
|
dt, err = ioutil.ReadFile(filepath.Join(destDir, "bay"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "bay-contents", string(dt))
|
||||||
|
}
|
||||||
|
|
||||||
func testExportedHistory(t *testing.T, sb integration.Sandbox) {
|
func testExportedHistory(t *testing.T, sb integration.Sandbox) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
const (
|
const (
|
||||||
keyOverrideExcludes = "override-excludes"
|
keyOverrideExcludes = "override-excludes"
|
||||||
keyIncludePatterns = "include-patterns"
|
keyIncludePatterns = "include-patterns"
|
||||||
|
keyExcludePatterns = "exclude-patterns"
|
||||||
keyDirName = "dir-name"
|
keyDirName = "dir-name"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -55,7 +56,7 @@ func (sp *fsSyncProvider) TarStream(stream FileSync_TarStreamServer) error {
|
||||||
return sp.handle("tarstream", stream)
|
return sp.handle("tarstream", stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sp *fsSyncProvider) handle(method string, stream grpc.ServerStream) error {
|
func (sp *fsSyncProvider) handle(method string, stream grpc.ServerStream) (retErr error) {
|
||||||
var pr *protocol
|
var pr *protocol
|
||||||
for _, p := range supportedProtocols {
|
for _, p := range supportedProtocols {
|
||||||
if method == p.name && isProtoSupported(p.name) {
|
if method == p.name && isProtoSupported(p.name) {
|
||||||
|
@ -80,8 +81,8 @@ func (sp *fsSyncProvider) handle(method string, stream grpc.ServerStream) error
|
||||||
return errors.Errorf("no access allowed to dir %q", dirName)
|
return errors.Errorf("no access allowed to dir %q", dirName)
|
||||||
}
|
}
|
||||||
|
|
||||||
var excludes []string
|
excludes := opts[keyExcludePatterns]
|
||||||
if len(opts[keyOverrideExcludes]) == 0 || opts[keyOverrideExcludes][0] != "true" {
|
if len(dir.Excludes) != 0 && (len(opts[keyOverrideExcludes]) == 0 || opts[keyOverrideExcludes][0] != "true") {
|
||||||
excludes = dir.Excludes
|
excludes = dir.Excludes
|
||||||
}
|
}
|
||||||
includes := opts[keyIncludePatterns]
|
includes := opts[keyIncludePatterns]
|
||||||
|
@ -140,7 +141,8 @@ var supportedProtocols = []protocol{
|
||||||
type FSSendRequestOpt struct {
|
type FSSendRequestOpt struct {
|
||||||
Name string
|
Name string
|
||||||
IncludePatterns []string
|
IncludePatterns []string
|
||||||
OverrideExcludes bool
|
ExcludePatterns []string
|
||||||
|
OverrideExcludes bool // deprecated: this is used by docker/cli for automatically loading .dockerignore from the directory
|
||||||
DestDir string
|
DestDir string
|
||||||
CacheUpdater CacheUpdater
|
CacheUpdater CacheUpdater
|
||||||
ProgressCb func(int, bool)
|
ProgressCb func(int, bool)
|
||||||
|
@ -175,6 +177,10 @@ func FSSync(ctx context.Context, c session.Caller, opt FSSendRequestOpt) error {
|
||||||
opts[keyIncludePatterns] = opt.IncludePatterns
|
opts[keyIncludePatterns] = opt.IncludePatterns
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opt.ExcludePatterns != nil {
|
||||||
|
opts[keyExcludePatterns] = opt.ExcludePatterns
|
||||||
|
}
|
||||||
|
|
||||||
opts[keyDirName] = []string{opt.Name}
|
opts[keyDirName] = []string{opt.Name}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
|
@ -3,6 +3,8 @@ package pb
|
||||||
const AttrKeepGitDir = "git.keepgitdir"
|
const AttrKeepGitDir = "git.keepgitdir"
|
||||||
const AttrLocalSessionID = "local.session"
|
const AttrLocalSessionID = "local.session"
|
||||||
const AttrIncludePatterns = "local.includepattern"
|
const AttrIncludePatterns = "local.includepattern"
|
||||||
|
const AttrExcludePatterns = "local.excludepatterns"
|
||||||
|
const AttrSharedKeyHint = "local.sharedkeyhint"
|
||||||
const AttrLLBDefinitionFilename = "llbbuild.filename"
|
const AttrLLBDefinitionFilename = "llbbuild.filename"
|
||||||
|
|
||||||
const AttrHTTPChecksum = "http.checksum"
|
const AttrHTTPChecksum = "http.checksum"
|
||||||
|
|
|
@ -80,6 +80,14 @@ func FromLLB(op *pb.Op_Source) (Identifier, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
id.IncludePatterns = patterns
|
id.IncludePatterns = patterns
|
||||||
|
case pb.AttrExcludePatterns:
|
||||||
|
var patterns []string
|
||||||
|
if err := json.Unmarshal([]byte(v), &patterns); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
id.ExcludePatterns = patterns
|
||||||
|
case pb.AttrSharedKeyHint:
|
||||||
|
id.SharedKeyHint = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,6 +150,8 @@ type LocalIdentifier struct {
|
||||||
Name string
|
Name string
|
||||||
SessionID string
|
SessionID string
|
||||||
IncludePatterns []string
|
IncludePatterns []string
|
||||||
|
ExcludePatterns []string
|
||||||
|
SharedKeyHint string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLocalIdentifier(str string) (*LocalIdentifier, error) {
|
func NewLocalIdentifier(str string) (*LocalIdentifier, error) {
|
||||||
|
|
|
@ -79,7 +79,8 @@ func (ls *localSourceHandler) CacheKey(ctx context.Context) (string, error) {
|
||||||
dt, err := json.Marshal(struct {
|
dt, err := json.Marshal(struct {
|
||||||
SessionID string
|
SessionID string
|
||||||
IncludePatterns []string
|
IncludePatterns []string
|
||||||
}{SessionID: sessionID, IncludePatterns: ls.src.IncludePatterns})
|
ExcludePatterns []string
|
||||||
|
}{SessionID: sessionID, IncludePatterns: ls.src.IncludePatterns, ExcludePatterns: ls.src.ExcludePatterns})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -101,7 +102,7 @@ func (ls *localSourceHandler) Snapshot(ctx context.Context) (out cache.Immutable
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sharedKey := keySharedKey + ":" + ls.src.Name + ":" + caller.SharedKey()
|
sharedKey := keySharedKey + ":" + ls.src.Name + ":" + ls.src.SharedKeyHint + ":" + caller.SharedKey() // TODO: replace caller.SharedKey() with source based hint from client(absolute-path+nodeid)
|
||||||
|
|
||||||
var mutable cache.MutableRef
|
var mutable cache.MutableRef
|
||||||
sis, err := ls.md.Search(sharedKey)
|
sis, err := ls.md.Search(sharedKey)
|
||||||
|
@ -157,6 +158,7 @@ func (ls *localSourceHandler) Snapshot(ctx context.Context) (out cache.Immutable
|
||||||
opt := filesync.FSSendRequestOpt{
|
opt := filesync.FSSendRequestOpt{
|
||||||
Name: ls.src.Name,
|
Name: ls.src.Name,
|
||||||
IncludePatterns: ls.src.IncludePatterns,
|
IncludePatterns: ls.src.IncludePatterns,
|
||||||
|
ExcludePatterns: ls.src.ExcludePatterns,
|
||||||
OverrideExcludes: false,
|
OverrideExcludes: false,
|
||||||
DestDir: dest,
|
DestDir: dest,
|
||||||
CacheUpdater: &cacheUpdater{cc},
|
CacheUpdater: &cacheUpdater{cc},
|
||||||
|
|
64
vendor/github.com/docker/docker/builder/dockerignore/dockerignore.go
generated
vendored
Normal file
64
vendor/github.com/docker/docker/builder/dockerignore/dockerignore.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package dockerignore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReadAll reads a .dockerignore file and returns the list of file patterns
|
||||||
|
// to ignore. Note this will trim whitespace from each line as well
|
||||||
|
// as use GO's "clean" func to get the shortest/cleanest path for each.
|
||||||
|
func ReadAll(reader io.Reader) ([]string, error) {
|
||||||
|
if reader == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(reader)
|
||||||
|
var excludes []string
|
||||||
|
currentLine := 0
|
||||||
|
|
||||||
|
utf8bom := []byte{0xEF, 0xBB, 0xBF}
|
||||||
|
for scanner.Scan() {
|
||||||
|
scannedBytes := scanner.Bytes()
|
||||||
|
// We trim UTF8 BOM
|
||||||
|
if currentLine == 0 {
|
||||||
|
scannedBytes = bytes.TrimPrefix(scannedBytes, utf8bom)
|
||||||
|
}
|
||||||
|
pattern := string(scannedBytes)
|
||||||
|
currentLine++
|
||||||
|
// Lines starting with # (comments) are ignored before processing
|
||||||
|
if strings.HasPrefix(pattern, "#") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pattern = strings.TrimSpace(pattern)
|
||||||
|
if pattern == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// normalize absolute paths to paths relative to the context
|
||||||
|
// (taking care of '!' prefix)
|
||||||
|
invert := pattern[0] == '!'
|
||||||
|
if invert {
|
||||||
|
pattern = strings.TrimSpace(pattern[1:])
|
||||||
|
}
|
||||||
|
if len(pattern) > 0 {
|
||||||
|
pattern = filepath.Clean(pattern)
|
||||||
|
pattern = filepath.ToSlash(pattern)
|
||||||
|
if len(pattern) > 1 && pattern[0] == '/' {
|
||||||
|
pattern = pattern[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if invert {
|
||||||
|
pattern = "!" + pattern
|
||||||
|
}
|
||||||
|
|
||||||
|
excludes = append(excludes, pattern)
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("Error reading .dockerignore: %v", err)
|
||||||
|
}
|
||||||
|
return excludes, nil
|
||||||
|
}
|
Loading…
Reference in New Issue