diff --git a/.drone.yml b/.drone.yml deleted file mode 100644 index f8b7eaa..0000000 --- a/.drone.yml +++ /dev/null @@ -1,23 +0,0 @@ -kind: pipeline -name: test-docker-build - -steps: -- name: test-docker-build - image: plugins/docker - network_mode: bridge - settings: - repo: sneak/mfer - build_args_from_env: [ DRONE_COMMIT_SHA ] - dry_run: true - custom_dns: [ 116.202.204.30 ] - tags: - - ${DRONE_COMMIT_SHA:0:7} - - ${DRONE_BRANCH} - - latest -- name: notify - image: plugins/slack - settings: - webhook: - from_secret: SLACK_WEBHOOK_URL - when: - event: pull_request diff --git a/.gitignore b/.gitignore index adc169b..a19733f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ *.tmp *.dockerimage /vendor + +# Stale files +.drone.yml diff --git a/mfer/checker.go b/mfer/checker.go index 0abf0b4..a213697 100644 --- a/mfer/checker.go +++ b/mfer/checker.go @@ -272,12 +272,14 @@ func (c *Checker) checkFile(entry *MFFilePath, checkedBytes *FileSize) Result { // FindExtraFiles walks the filesystem and reports files not in the manifest. // Results are sent to the results channel. The channel is closed when done. +// Hidden files/directories (starting with .) are skipped, as they are excluded +// from manifests by default. The manifest file itself is also skipped. func (c *Checker) FindExtraFiles(ctx context.Context, results chan<- Result) error { if results != nil { defer close(results) } - return afero.Walk(c.fs, string(c.basePath), func(path string, info os.FileInfo, err error) error { + return afero.Walk(c.fs, string(c.basePath), func(walkPath string, info os.FileInfo, err error) error { if err != nil { return err } @@ -288,16 +290,31 @@ func (c *Checker) FindExtraFiles(ctx context.Context, results chan<- Result) err default: } + // Get relative path + rel, err := filepath.Rel(string(c.basePath), walkPath) + if err != nil { + return err + } + + // Skip hidden files and directories (dotfiles) + if IsHiddenPath(filepath.ToSlash(rel)) { + if info.IsDir() { + return filepath.SkipDir + } + return nil + } + // Skip directories if info.IsDir() { return nil } - // Get relative path - rel, err := filepath.Rel(string(c.basePath), path) - if err != nil { - return err + // Skip manifest files + base := filepath.Base(rel) + if base == "index.mf" || base == ".index.mf" { + return nil } + relPath := RelFilePath(rel) // Check if path is in manifest diff --git a/mfer/checker_test.go b/mfer/checker_test.go index 44a9ac0..5ba283e 100644 --- a/mfer/checker_test.go +++ b/mfer/checker_test.go @@ -305,6 +305,44 @@ func TestFindExtraFiles(t *testing.T) { assert.Equal(t, "not in manifest", extras[0].Message) } +func TestFindExtraFilesSkipsManifestAndDotfiles(t *testing.T) { + fs := afero.NewMemMapFs() + manifestFiles := map[string][]byte{ + "file1.txt": []byte("in manifest"), + } + createTestManifest(t, fs, "/data/.index.mf", manifestFiles) + createFilesOnDisk(t, fs, "/data", map[string][]byte{ + "file1.txt": []byte("in manifest"), + }) + // Create dotfile and manifest that should be skipped + require.NoError(t, afero.WriteFile(fs, "/data/.hidden", []byte("hidden"), 0o644)) + require.NoError(t, afero.WriteFile(fs, "/data/.config/settings", []byte("cfg"), 0o644)) + // Create a real extra file + require.NoError(t, fs.MkdirAll("/data", 0o755)) + require.NoError(t, afero.WriteFile(fs, "/data/extra.txt", []byte("extra"), 0o644)) + + chk, err := NewChecker("/data/.index.mf", "/data", fs) + require.NoError(t, err) + + results := make(chan Result, 10) + err = chk.FindExtraFiles(context.Background(), results) + require.NoError(t, err) + + var extras []Result + for r := range results { + extras = append(extras, r) + } + + // Should only report extra.txt, not .hidden, .config/settings, or .index.mf + for _, e := range extras { + t.Logf("extra: %s", e.Path) + } + assert.Len(t, extras, 1) + if len(extras) > 0 { + assert.Equal(t, RelFilePath("extra.txt"), extras[0].Path) + } +} + func TestFindExtraFilesContextCancellation(t *testing.T) { fs := afero.NewMemMapFs() files := map[string][]byte{"file.txt": []byte("data")}