diff --git a/initrd/directory.go b/initrd/directory.go index 54c3a187c7..1e87d80e80 100644 --- a/initrd/directory.go +++ b/initrd/directory.go @@ -75,6 +75,11 @@ func (initrd *directory) Build(ctx context.Context) (string, error) { writer := cpio.NewWriter(f) defer writer.Close() + ignoringItems, err := getKraftignoreItems(ctx, initrd) + if err != nil { + return "", err + } + if err := filepath.WalkDir(initrd.path, func(path string, d fs.DirEntry, err error) error { if err != nil { return fmt.Errorf("received error before parsing path: %w", err) @@ -86,6 +91,25 @@ func (initrd *directory) Build(ctx context.Context) (string, error) { return nil // Do not archive empty paths } + if len(ignoringItems) > 0 && path != initrd.path { + str, err := isExistInKraftignoreFile(internal, d, ignoringItems) + if err != nil { + return err + } + switch str { + case "SkipDir": + log.G(ctx). + WithField("directory", internal). + Trace("ignoring from archiving") + return filepath.SkipDir + case "Exist": + log.G(ctx). + WithField("file", internal). + Trace("ignoring from archiving") + return nil + } + } + info, err := d.Info() if err != nil { return fmt.Errorf("could not get directory entry info: %w", err) diff --git a/initrd/kraftignore.go b/initrd/kraftignore.go new file mode 100644 index 0000000000..1443ce070b --- /dev/null +++ b/initrd/kraftignore.go @@ -0,0 +1,96 @@ +package initrd + +import ( + "bufio" + "context" + "errors" + "io/fs" + "os" + "path/filepath" + "strings" + + "kraftkit.sh/log" +) + +// kraftignore filename +const KraftignoreFileName = ".kraftignore" + +func getKraftignoreItems(ctx context.Context, initrd *directory) ([]string, error) { + if initrd.opts.kraftignorePath == "" { + cwd, err := os.Getwd() + if err != nil { + return []string{}, err + } + initrd.opts.kraftignorePath = filepath.Join(cwd, KraftignoreFileName) + } + + if _, err := os.Stat(initrd.opts.kraftignorePath); errors.Is(err, os.ErrNotExist) { + return []string{}, nil + } else if err != nil { + return []string{}, err + } + + kraftignoreFile, err := os.Open(initrd.opts.kraftignorePath) + defer kraftignoreFile.Close() + + if err != nil { + return []string{}, err + } + kraftignoreScanner := bufio.NewScanner(kraftignoreFile) + kraftignoreScanner.Split(bufio.ScanLines) + var kraftignoreFileLines, ignoringItems []string + + for kraftignoreScanner.Scan() { + kraftignoreFileLines = append(kraftignoreFileLines, kraftignoreScanner.Text()) + } + + for _, line := range kraftignoreFileLines { + line = strings.TrimSpace(line) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + items := strings.Split(line, " ") + for _, item := range items { + item = strings.TrimSpace(item) + if item == "" { + continue + } + + if hasGlobPatterns(item) { + log.G(ctx). + WithField("file", initrd.opts.kraftignorePath). + Warn("contains a glob pattern ", item, + " which is not supported by Kraftkit") + continue + } + + if strings.HasPrefix(item, "./") { + item = strings.TrimPrefix(item, ".") + } else if !strings.HasPrefix(item, "/") { + item = "/" + item + } + ignoringItems = append(ignoringItems, item) + } + } + return ignoringItems, nil +} + +func isExistInKraftignoreFile(internal string, pathInfo fs.DirEntry, kraftignoreItems []string) (string, error) { + for _, ignoringItem := range kraftignoreItems { + if internal == ignoringItem { + if pathInfo.IsDir() { + return "SkipDir", nil + } + return "Exist", nil + } + } + return "", nil +} + +func hasGlobPatterns(item string) bool { + if strings.Contains(item, "*") || strings.Contains(item, "?") || + strings.Contains(item, "!") || strings.Contains(item, "[") { + return true + } + return false +} diff --git a/initrd/options.go b/initrd/options.go index 65cbf89dff..c27cb5d508 100644 --- a/initrd/options.go +++ b/initrd/options.go @@ -4,10 +4,15 @@ // You may not use this file except in compliance with the License. package initrd +import ( + "path/filepath" +) + type InitrdOptions struct { - output string - cacheDir string - arch string + output string + cacheDir string + arch string + kraftignorePath string } type InitrdOption func(*InitrdOptions) error @@ -41,3 +46,11 @@ func WithArchitecture(arch string) InitrdOption { return nil } } + +// WithKraftignorePath sets the path for .kraftignore. +func WithKraftignorePath(dir string) InitrdOption { + return func(opts *InitrdOptions) error { + opts.kraftignorePath = filepath.Join(dir, KraftignoreFileName) + return nil + } +}