mirror of
https://github.com/ivuorinen/gibidify.git
synced 2026-01-26 11:34:03 +00:00
163 lines
4.4 KiB
Go
163 lines
4.4 KiB
Go
// Package fileproc provides functions for file processing.
|
|
package fileproc
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/ivuorinen/gibidify/config"
|
|
ignore "github.com/sabhiram/go-gitignore"
|
|
)
|
|
|
|
// Walker defines an interface for scanning directories.
|
|
type Walker interface {
|
|
Walk(root string) ([]string, error)
|
|
}
|
|
|
|
// ProdWalker implements Walker using a custom directory walker that
|
|
// respects .gitignore and .ignore files, configuration-defined ignore directories,
|
|
// and ignores binary and image files by default.
|
|
type ProdWalker struct{}
|
|
|
|
// ignoreRule holds an ignore matcher along with the base directory where it was loaded.
|
|
type ignoreRule struct {
|
|
base string
|
|
gi *ignore.GitIgnore
|
|
}
|
|
|
|
// Walk scans the given root directory recursively and returns a slice of file paths
|
|
// that are not ignored based on .gitignore/.ignore files, the configuration, or the default binary/image filter.
|
|
func (pw ProdWalker) Walk(root string) ([]string, error) {
|
|
absRoot, err := filepath.Abs(root)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return walkDir(absRoot, absRoot, []ignoreRule{})
|
|
}
|
|
|
|
// walkDir recursively walks the directory tree starting at currentDir.
|
|
// It loads any .gitignore and .ignore files found in each directory and
|
|
// appends the corresponding rules to the inherited list. Each file/directory is
|
|
// then checked against the accumulated ignore rules, the configuration's list of ignored directories,
|
|
// and a default filter that ignores binary and image files.
|
|
func walkDir(root string, currentDir string, parentRules []ignoreRule) ([]string, error) {
|
|
var results []string
|
|
|
|
entries, err := os.ReadDir(currentDir)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Start with the parent's ignore rules.
|
|
rules := make([]ignoreRule, len(parentRules))
|
|
copy(rules, parentRules)
|
|
|
|
// Check for .gitignore and .ignore files in the current directory.
|
|
for _, fileName := range []string{".gitignore", ".ignore"} {
|
|
ignorePath := filepath.Join(currentDir, fileName)
|
|
if info, err := os.Stat(ignorePath); err == nil && !info.IsDir() {
|
|
gi, err := ignore.CompileIgnoreFile(ignorePath)
|
|
if err == nil {
|
|
rules = append(rules, ignoreRule{
|
|
base: currentDir,
|
|
gi: gi,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the list of directories to ignore from configuration.
|
|
ignoredDirs := config.GetIgnoredDirectories()
|
|
sizeLimit := config.GetFileSizeLimit() // e.g., 5242880 for 5 MB
|
|
|
|
for _, entry := range entries {
|
|
fullPath := filepath.Join(currentDir, entry.Name())
|
|
|
|
// For directories, check if its name is in the config ignore list.
|
|
if entry.IsDir() {
|
|
for _, d := range ignoredDirs {
|
|
if entry.Name() == d {
|
|
// Skip this directory entirely.
|
|
goto SkipEntry
|
|
}
|
|
}
|
|
} else {
|
|
// Check if file exceeds the configured size limit.
|
|
info, err := entry.Info()
|
|
if err == nil && info.Size() > sizeLimit {
|
|
goto SkipEntry
|
|
}
|
|
|
|
// For files, apply the default filter to ignore binary and image files.
|
|
if isBinaryOrImage(fullPath) {
|
|
goto SkipEntry
|
|
}
|
|
}
|
|
|
|
// Check accumulated ignore rules.
|
|
for _, rule := range rules {
|
|
// Compute the path relative to the base where the ignore rule was defined.
|
|
rel, err := filepath.Rel(rule.base, fullPath)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
// If the rule matches, skip this entry.
|
|
if rule.gi.MatchesPath(rel) {
|
|
goto SkipEntry
|
|
}
|
|
}
|
|
|
|
// If not ignored, then process the entry.
|
|
if entry.IsDir() {
|
|
subFiles, err := walkDir(root, fullPath, rules)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
results = append(results, subFiles...)
|
|
} else {
|
|
results = append(results, fullPath)
|
|
}
|
|
SkipEntry:
|
|
continue
|
|
}
|
|
|
|
return results, nil
|
|
}
|
|
|
|
// isBinaryOrImage checks if a file should be considered binary or an image based on its extension.
|
|
// The check is case-insensitive.
|
|
func isBinaryOrImage(filePath string) bool {
|
|
ext := strings.ToLower(filepath.Ext(filePath))
|
|
// Common image file extensions.
|
|
imageExtensions := map[string]bool{
|
|
".png": true,
|
|
".jpg": true,
|
|
".jpeg": true,
|
|
".gif": true,
|
|
".bmp": true,
|
|
".tiff": true,
|
|
".ico": true,
|
|
".svg": true,
|
|
".webp": true,
|
|
}
|
|
// Common binary file extensions.
|
|
binaryExtensions := map[string]bool{
|
|
".exe": true,
|
|
".dll": true,
|
|
".so": true,
|
|
".bin": true,
|
|
".dat": true,
|
|
".zip": true,
|
|
".tar": true,
|
|
".gz": true,
|
|
".7z": true,
|
|
".rar": true,
|
|
".DS_Store": true,
|
|
}
|
|
if imageExtensions[ext] || binaryExtensions[ext] {
|
|
return true
|
|
}
|
|
return false
|
|
}
|