Compare commits
2
Commits
c49c5e63b9
...
3f9f150736
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f9f150736 | ||
|
|
c4b50e2b55 |
@@ -25,6 +25,11 @@ today), `.dockerignore`, `.gitea/workflows/check.yml` and
|
||||
|
||||
# Completed Steps
|
||||
|
||||
* 2026-10-06: the summary line after `sum`, `check` and `clear` prints
|
||||
the byte unit once (`1.5 KiB`, not `1.5 KiB bytes`)
|
||||
* 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,
|
||||
|
||||
+18
-7
@@ -71,13 +71,13 @@ func (s *Stats) Duration() time.Duration {
|
||||
return time.Since(s.StartTime)
|
||||
}
|
||||
|
||||
func (s *Stats) Print(opts *options, operation string) {
|
||||
func (s *Stats) Print(w io.Writer, opts *options, operation string) {
|
||||
if opts.quiet {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr,
|
||||
"\n%s complete: %d files processed, %d skipped, %d failed, %s bytes in %s\n",
|
||||
_, _ = fmt.Fprintf(w,
|
||||
"\n%s complete: %d files processed, %d skipped, %d failed, %s in %s\n",
|
||||
operation,
|
||||
s.FilesProcessed,
|
||||
s.FilesSkipped,
|
||||
@@ -227,7 +227,7 @@ func runOverPaths(
|
||||
}
|
||||
|
||||
finishBar(bar)
|
||||
stats.Print(opts, op)
|
||||
stats.Print(os.Stderr, opts, op)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -481,7 +481,7 @@ func runCheck(opts *options, args []string, cont bool) error {
|
||||
if perr != nil {
|
||||
if !cont {
|
||||
finishBar(bar)
|
||||
stats.Print(opts, "check")
|
||||
stats.Print(os.Stderr, opts, "check")
|
||||
|
||||
return perr
|
||||
}
|
||||
@@ -491,7 +491,7 @@ func runCheck(opts *options, args []string, cont bool) error {
|
||||
}
|
||||
|
||||
finishBar(bar)
|
||||
stats.Print(opts, "check")
|
||||
stats.Print(os.Stderr, opts, "check")
|
||||
|
||||
return finalErr
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -214,6 +215,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()
|
||||
|
||||
@@ -328,3 +377,27 @@ func TestCheckContinuePastUnreadable(t *testing.T) {
|
||||
t.Fatalf("expected verification error from runCheck, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSummaryPrintsByteUnitOnce(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
bytesProcessed int64
|
||||
want string
|
||||
}{
|
||||
{9, ", 9 B in "},
|
||||
{1536, ", 1.5 KiB in "},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
var out bytes.Buffer
|
||||
|
||||
stats := newTestStats()
|
||||
stats.BytesProcessed = tt.bytesProcessed
|
||||
stats.Print(&out, &options{}, "check")
|
||||
|
||||
if !strings.Contains(out.String(), tt.want) {
|
||||
t.Errorf("summary %q does not contain %q", out.String(), tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user