Skip excluded paths before looking at their errors (closes #16)
check / check (push) Successful in 2m36s
check / check (push) Successful in 2m36s
filepath.Walk hands its callback the error for a path it could not read, such as a directory it cannot list. Both callbacks, in walkAndProcess and countFiles, returned that error before they checked the exclusions, so a directory that --exclude or --exclude-dotfiles excluded still failed the run when it could not be listed. Given an error, each callback now checks the exclusions first and skips an excluded path. The new test excludes a directory that cannot be listed and runs sum add and check over its parent. Model: opus-5-5
This commit is contained in:
@@ -25,6 +25,9 @@ today), `.dockerignore`, `.gitea/workflows/check.yml` and
|
||||
|
||||
# Completed Steps
|
||||
|
||||
* 2026-10-06: a path that `--exclude` or `--exclude-dotfiles` excludes
|
||||
is skipped even when it cannot be read, so an excluded directory
|
||||
that cannot be listed no longer fails the run
|
||||
* 2026-10-06: lint and test run as phases of the `Dockerfile`, and the
|
||||
build stage depends on both; `script/lint` and `script/test` each
|
||||
build their phase with `--no-cache`; `script/cibuild` bootstraps,
|
||||
|
||||
+12
-1
@@ -613,6 +613,12 @@ func countFiles(opts *options, root string, cont bool) (int64, error) {
|
||||
root = filepath.Clean(root)
|
||||
|
||||
err := filepath.Walk(root, func(p string, info os.FileInfo, err error) error {
|
||||
// An excluded path is skipped whatever went wrong reading it.
|
||||
rel, _ := filepath.Rel(root, p)
|
||||
if err != nil && shouldExclude(opts, rel) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil && cont {
|
||||
return nil
|
||||
}
|
||||
@@ -627,7 +633,6 @@ func countFiles(opts *options, root string, cont bool) (int64, error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
rel, _ := filepath.Rel(root, p)
|
||||
if shouldExclude(opts, rel) {
|
||||
if info.IsDir() {
|
||||
return filepath.SkipDir
|
||||
@@ -699,6 +704,12 @@ func walkAndProcess(
|
||||
root = filepath.Clean(root)
|
||||
|
||||
return filepath.Walk(root, func(p string, info os.FileInfo, err error) error {
|
||||
// An excluded path is skipped whatever went wrong reading it.
|
||||
rel, _ := filepath.Rel(root, p)
|
||||
if err != nil && shouldExclude(opts, rel) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return unreadable(cont, stats, err)
|
||||
}
|
||||
|
||||
@@ -214,6 +214,54 @@ func TestExcludeDotfilesAndPatterns(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExcludeUnreadableDir(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
opts := &options{excludePatterns: []string{"locked"}}
|
||||
dir := t.TempDir()
|
||||
skipIfNoXattr(t, dir)
|
||||
|
||||
keep := writeFile(t, dir, "keep.txt", "keep")
|
||||
hidden := writeFile(t, dir, "locked/a.txt", "hidden")
|
||||
|
||||
// The excluded directory cannot be listed.
|
||||
sub := filepath.Join(dir, "locked")
|
||||
|
||||
err := os.Chmod(sub, noPerm)
|
||||
if err != nil {
|
||||
t.Fatalf("chmod dir: %v", err)
|
||||
}
|
||||
|
||||
defer func() { _ = os.Chmod(sub, dirPerm) }()
|
||||
|
||||
err = processSumAdd(opts, dir, newTestStats(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("add: %v", err)
|
||||
}
|
||||
|
||||
_, err = xattr.Get(keep, checksumKey)
|
||||
if err != nil {
|
||||
t.Fatalf("expected xattr on keep.txt: %v", err)
|
||||
}
|
||||
|
||||
// Without --quiet, runCheck counts the files for the progress bar
|
||||
// before it checks any, so this also covers the count.
|
||||
err = runCheck(opts, []string{dir}, false)
|
||||
if err != nil {
|
||||
t.Fatalf("check: %v", err)
|
||||
}
|
||||
|
||||
err = os.Chmod(sub, dirPerm)
|
||||
if err != nil {
|
||||
t.Fatalf("chmod dir back: %v", err)
|
||||
}
|
||||
|
||||
_, err = xattr.Get(hidden, checksumKey)
|
||||
if err == nil {
|
||||
t.Fatalf("locked/a.txt should have been excluded")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSkipBrokenSymlink(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user