package docker import ( "errors" "fmt" "io/fs" "os" "path/filepath" "github.com/moby/patternmatcher" "github.com/moby/patternmatcher/ignorefile" ) // readDockerignore returns the patterns of the files to leave out of the // build context in contextDir, read as docker build reads them for the // Dockerfile at dockerfile, a path inside contextDir: from // .dockerignore next to the Dockerfile if there is one, otherwise // from .dockerignore at the root of the context. Without either file there // are no patterns. func readDockerignore(contextDir, dockerfile string) ([]string, error) { if dockerfile == "" { dockerfile = "Dockerfile" // what Docker builds when none is named } name := dockerfile + ".dockerignore" file, err := os.Open(filepath.Join(contextDir, name)) if errors.Is(err, fs.ErrNotExist) { name = ".dockerignore" file, err = os.Open(filepath.Join(contextDir, name)) } if errors.Is(err, fs.ErrNotExist) { return nil, nil } if err != nil { return nil, fmt.Errorf("failed to read %s: %w", name, err) } defer func() { _ = file.Close() }() excludes, err := ignorefile.ReadAll(file) if err != nil { return nil, fmt.Errorf("failed to read %s: %w", name, err) } // Like the docker command line, never leave out .dockerignore or the // Dockerfile: Docker reads the Dockerfile from the context. for _, keep := range []string{".dockerignore", filepath.ToSlash(dockerfile)} { excluded, err := patternmatcher.MatchesOrParentMatches(keep, excludes) if err != nil { return nil, fmt.Errorf("invalid pattern in %s: %w", name, err) } if excluded { excludes = append(excludes, "!"+keep) } } return excludes, nil }