Name the database each command uses on stderr (closes #111)
check / check (push) Canceled after 0s

scan, report and trees now print one line on stderr before anything
else, naming the database and whether its path is the default or comes
from SFDUPES_DATABASE; scan also says when the file is new. report and
trees print it before opening the database, so a missing-database error
follows it. A relative path gets the working directory put in front of
it without cleaning: cleaning drops a ".." with the directory before
it, which names another file when that directory is a symlink. README
"Database" lists when scan writes each file.

Unverified: the line on a terminal has no test of its own.
Rule suppressed: TestDatabasePath carries nolint:paralleltest, as it
changes the working directory.

Model: opus-5-5
This commit is contained in:
2026-10-16 12:34:49 +00:00
committed by sneak
parent 198cefa152
commit df1d5cb8ff
9 changed files with 317 additions and 35 deletions
+23 -4
View File
@@ -11,6 +11,7 @@ import (
"path/filepath"
"slices"
"strconv"
"strings"
"time"
"golang.org/x/sys/unix"
@@ -91,13 +92,31 @@ var errSchemaVersion = errors.New("unsupported database schema version")
var errScanRunning = errors.New("another scan is running")
// databasePath resolves the database location: SFDUPES_DATABASE when
// set and non-empty, the compiled-in default otherwise.
func databasePath() string {
// set and non-empty, the compiled-in default otherwise. It returns the
// path, with the working directory put in front of it when it is
// relative, and where the path came from, "default" or "from
// SFDUPES_DATABASE", for the line each subcommand prints first on
// stderr.
func databasePath() (string, string, error) {
path, source := defaultDatabasePath, "default"
if p := os.Getenv(databaseEnv); p != "" {
return p
path, source = p, "from "+databaseEnv
}
return defaultDatabasePath
if filepath.IsAbs(path) {
return path, source, nil
}
wd, err := os.Getwd()
if err != nil {
return "", "", fmt.Errorf("resolve database path %s: %w", path, err)
}
// Not filepath.Abs or filepath.Join: they clean the path, which
// drops each ".." together with the directory name before it. When
// that directory is a symlink, the cleaned path names another file.
// The working directory ends in "/" only when it is the root.
return strings.TrimSuffix(wd, "/") + "/" + path, source, nil
}
// scanParams are the connection parameters for scan: read-write, with