Skip to content

Commit

Permalink
feat: Introduce .kraftignore
Browse files Browse the repository at this point in the history
Signed-off-by: Md Sahil <[email protected]>
  • Loading branch information
MdSahil-oss committed Jan 22, 2024
1 parent 2bd20dc commit 16e83d8
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 0 deletions.
24 changes: 24 additions & 0 deletions initrd/directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ func (initrd *directory) Build(ctx context.Context) (string, error) {
}
}

if _, err := os.Stat(initrd.opts.output); err == nil {
return "", fmt.Errorf("output already exists")
}

f, err := os.OpenFile(initrd.opts.output, os.O_RDWR|os.O_CREATE, 0o644)
if err != nil {
return "", fmt.Errorf("could not open initramfs file: %w", err)
Expand All @@ -75,6 +79,11 @@ func (initrd *directory) Build(ctx context.Context) (string, error) {
writer := cpio.NewWriter(f)
defer writer.Close()

ignoringItems, err := GetKraftIgnoreItems(ctx, initrd.path)
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)
Expand All @@ -86,6 +95,21 @@ func (initrd *directory) Build(ctx context.Context) (string, error) {
}
internal = "." + filepath.ToSlash(internal)

if len(ignoringItems) > 0 && path != initrd.path {
switch isExistInKraftignoreFile(internal, d, ignoringItems) {
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)
Expand Down
157 changes: 157 additions & 0 deletions initrd/kraftignore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2024, Unikraft GmbH and The KraftKit Authors.
// Licensed under the BSD-3-Clause License (the "License").
// You may not use this file except in compliance with the License.
package initrd

import (
"bufio"
"context"
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"

"kraftkit.sh/log"
)

// kraftignore filename
const KraftignoreFileName = ".kraftignore"

type IgnoringFileType string

const (
Exist = IgnoringFileType("Exist")
NotExist = IgnoringFileType("NotExist")
SkipDir = IgnoringFileType("SkipDir")
)

// GetKraftIgnoreItems returns file and directory names specified in .kraftignore
func GetKraftIgnoreItems(ctx context.Context, dir string) ([]string, error) {
cwd, err := os.Getwd()
if err != nil {
return []string{}, err
}
kraftignorePath := filepath.Join(cwd, KraftignoreFileName)

if _, err := os.Stat(kraftignorePath); errors.Is(err, os.ErrNotExist) {
return []string{}, nil
} else if err != nil {
return []string{}, err
}

kraftignoreFile, err := os.Open(kraftignorePath)
if err != nil {
return []string{}, err
}

defer func() {
kraftIgnoreErr := kraftignoreFile.Close()
if kraftIgnoreErr != nil {
if err != nil {
err = fmt.Errorf("%w: %w", err, kraftIgnoreErr)
} else {
err = kraftIgnoreErr
}
}
}()

kraftignoreScanner := bufio.NewScanner(kraftignoreFile)
kraftignoreScanner.Split(bufio.ScanLines)
var kraftignoreFileLines, ignoringItems []string

for kraftignoreScanner.Scan() {
kraftignoreFileLines = append(kraftignoreFileLines, kraftignoreScanner.Text())
}

for lineNum, line := range kraftignoreFileLines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
items := findLineItems(line)
for _, item := range items {
if item == "" || item == "#" {
continue
}

if hasGlobPatterns(item) {
log.G(ctx).
WithField("file", kraftignorePath).
Warn("contains a glob pattern ", item,
" at line ", lineNum,
" which is not supported by Kraftkit")
continue
}

if _, err := os.Stat(filepath.Join(dir, item)); os.IsNotExist(err) {
log.G(ctx).
WithField("file", kraftignorePath).
Warn("contains ", item,
" at line ", lineNum,
" which does not exist in the provided rootfs directory")
continue
}

ignoringItems = append(ignoringItems, item)
}
}

return ignoringItems, err
}

// isExistInKraftignoreFile checks if the path exist in .kraftignore
func isExistInKraftignoreFile(internal string, pathInfo fs.DirEntry, kraftignoreItems []string) IgnoringFileType {
for _, ignoringItem := range kraftignoreItems {
if internal == ignoringItem {
if pathInfo.IsDir() {
return SkipDir
}
return Exist
}
}
return NotExist
}

// hasGlobPatterns checks if the item contains glob pattern
func hasGlobPatterns(item string) bool {
return strings.ContainsAny(item, "*?![{")
}

// findLineItems finds items in a line of .kraftignore
func findLineItems(line string) []string {
items := strings.Split(line, " ")
for index := 0; index < len(items); index++ {
charToFind := ""
if strings.HasPrefix(items[index], `"`) && !strings.HasSuffix(items[index], `"`) {
charToFind = `"`
} else if strings.HasPrefix(items[index], `'`) && !strings.HasSuffix(items[index], `'`) {
charToFind = `'`
}

if len(charToFind) > 0 {
i := index + 1
for ; i < len(items) && !strings.HasSuffix(items[i], charToFind); i++ {
items[index] += " " + items[i]
items = append(items[:i], items[i+1:]...)
i--
}
items[index] += " " + items[i]
items = append(items[:i], items[i+1:]...)
}
items[index] = strings.Trim(items[index], `"`)
items[index] = strings.Trim(items[index], `'`)
items[index] = strings.TrimSpace(items[index])
items[index] = strings.TrimPrefix(items[index], "../")
if !strings.HasPrefix(items[index], "./") {
if !strings.HasPrefix(items[index], "/") {
items[index] = "/" + items[index]
}
items[index] = "." + items[index]
}
items[index] = strings.TrimSuffix(items[index], string(filepath.Separator))
}
return items
}

0 comments on commit 16e83d8

Please sign in to comment.