Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
070cf5b598 |
@@ -14,9 +14,9 @@
|
|||||||
# Status
|
# Status
|
||||||
|
|
||||||
- Pre-1.0.
|
- Pre-1.0.
|
||||||
- The tracker is authoritative for the pre-1.0 backlog. The open issues under
|
- The tracker is authoritative for the backlog. The open issues under the
|
||||||
the `1.0.0` milestone are what remains before the tag. This file records
|
`1.0.0` milestone are what remains before the tag. This file records history
|
||||||
history and process, not the queue.
|
and process, not the queue.
|
||||||
|
|
||||||
# Next Step
|
# Next Step
|
||||||
|
|
||||||
@@ -26,12 +26,11 @@
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
Newest first. An entry ends with its date and its issue or branch, where those
|
Newest first. Each entry ends with its date and its issue or branch.
|
||||||
are known.
|
|
||||||
|
|
||||||
- `TODO.md` rewritten from scratch by the same rules as `README.md`: short
|
- `TODO.md` rewritten from scratch by the same rules as `README.md`: short
|
||||||
sentences, lists as bullets, nothing a newcomer reads twice. Every completed
|
sentences, lists as bullets, nothing a newcomer reads twice. Every completed
|
||||||
step is kept, with its date and its issue or branch. The policy-compliance
|
step is kept, with its date and issue, in fewer words. The policy-compliance
|
||||||
checklist of 2026-07-22 is folded into its step (2026-10-26,
|
checklist of 2026-07-22 is folded into its step (2026-10-26,
|
||||||
https://git.eeqj.de/sneak/sfdupes/issues/135)
|
https://git.eeqj.de/sneak/sfdupes/issues/135)
|
||||||
|
|
||||||
@@ -60,24 +59,18 @@ are known.
|
|||||||
- Every file the walk records gets three hashes in the hash phase. `head` and
|
- Every file the walk records gets three hashes in the hash phase. `head` and
|
||||||
`tail` are the SHA-256 of its first and last 64 KiB. `content` is the SHA-256
|
`tail` are the SHA-256 of its first and last 64 KiB. `content` is the SHA-256
|
||||||
of the whole file under 100 MiB, and of 1 MiB at each gigabyte from 100 MiB
|
of the whole file under 100 MiB, and of 1 MiB at each gigabyte from 100 MiB
|
||||||
up. Gone are:
|
up. The 10 MiB and 50 MiB thresholds, the size census, the records without
|
||||||
- the 10 MiB and 50 MiB thresholds;
|
hashes and the content phase are gone. A scan has four phases, and a rescan
|
||||||
- the size census;
|
reads only new and changed files. `info` no longer counts the records with
|
||||||
- the records without hashes;
|
hashes, since every record has them (2026-10-25,
|
||||||
- the content phase.
|
|
||||||
|
|
||||||
A scan has four phases, and a rescan reads only new and changed files.
|
|
||||||
`info` no longer counts the records with `head` and `tail` hashes, nor those
|
|
||||||
with a content hash, since every record has all three (2026-10-25,
|
|
||||||
https://git.eeqj.de/sneak/sfdupes/issues/132)
|
https://git.eeqj.de/sneak/sfdupes/issues/132)
|
||||||
|
|
||||||
- `REPO_POLICIES.md` and the four scripts that pass `CACHEBUST` re-vendored from
|
- `REPO_POLICIES.md` and the four scripts that pass `CACHEBUST` re-vendored from
|
||||||
`sneak/prompts` `next` at `0653579`. Two steps now come before
|
`sneak/prompts` `next` at `0653579`. The module download in the `lint`, `test`
|
||||||
`ARG CACHEBUST`: the module download in the `lint`, `test` and `builder`
|
and `builder` stages of the `Dockerfile`, and the package install in
|
||||||
stages of the `Dockerfile`, and the package install in `builder`. Docker keeps
|
`builder`, now come before `ARG CACHEBUST`. Docker keeps them between builds.
|
||||||
them until the base image, the install line, `go.mod` or `go.sum` changes.
|
|
||||||
Every step that lints, tests or compiles still comes after it.
|
Every step that lints, tests or compiles still comes after it.
|
||||||
`golangci-lint run` also keeps the linter's own cache. README says the same
|
`golangci-lint run` also keeps golangci-lint's cache. README says the same
|
||||||
(2026-10-18, https://git.eeqj.de/sneak/sfdupes/issues/109)
|
(2026-10-18, https://git.eeqj.de/sneak/sfdupes/issues/109)
|
||||||
|
|
||||||
- Release rule set: until 1.0, each tip of `main` that passes the build gets the
|
- Release rule set: until 1.0, each tip of `main` that passes the build gets the
|
||||||
@@ -107,51 +100,44 @@ are known.
|
|||||||
|
|
||||||
- `scan` never records or reads anything on a filesystem that is not persistent:
|
- `scan` never records or reads anything on a filesystem that is not persistent:
|
||||||
`tmpfs`, `proc`, `sysfs` and the other kernel or memory-backed types. It
|
`tmpfs`, `proc`, `sysfs` and the other kernel or memory-backed types. It
|
||||||
matches them by device ID. As the scan starts, it lists the mounted
|
matches them by device ID, listed as the scan starts from
|
||||||
filesystems of those types, from `/proc/self/mountinfo` on Linux and from the
|
`/proc/self/mountinfo` on Linux and from the mount list on macOS. No flag
|
||||||
mount list on macOS. No flag turns that off. Below an operand, the walk does
|
turns that off. Below an operand, the walk does not enter the default excludes
|
||||||
not enter the default excludes either, so `sfdupes scan /` skips the kernel's
|
either, so `sfdupes scan /` skips the kernel's view of the running system and
|
||||||
view of the running system and the temporary directories. On Linux those are
|
the temporary directories. On Linux those are `/proc`, `/sys`, `/dev`, `/run`,
|
||||||
`/proc`, `/sys`, `/dev`, `/run`, `/tmp` and `/var/tmp`. On macOS they are
|
`/tmp` and `/var/tmp`. On macOS they are `/dev`, `/private/tmp`,
|
||||||
`/dev`, `/private/tmp`, `/private/var/tmp`, `/private/var/vm`,
|
`/private/var/tmp`, `/private/var/vm`, `/System/Volumes/VM` and
|
||||||
`/System/Volumes/VM` and `/System/Volumes/Data`. A Windows list exists in the
|
`/System/Volumes/Data`. A Windows list exists in the code but is not used. An
|
||||||
code but is not used. An operand at or inside a default exclude is still
|
operand at or inside a default exclude is still walked. The scan prints a line
|
||||||
walked. The scan prints a line for each path it leaves out, without counting
|
for each path it leaves out, without counting it as skipped, and the records
|
||||||
it as skipped, and the records there count as outside the scanned operands, as
|
there count as outside the scanned operands, as for `-x`.
|
||||||
for `-x`. `--no-default-excludes` turns off the default excludes only, and
|
`--no-default-excludes` turns off the default excludes only, and `scan --help`
|
||||||
`scan --help` lists them (2026-10-17,
|
lists them (2026-10-17, https://git.eeqj.de/sneak/sfdupes/issues/124)
|
||||||
https://git.eeqj.de/sneak/sfdupes/issues/124)
|
|
||||||
|
|
||||||
- The rate a progress display shows covers the items completed over the last 5
|
- The rate a progress display shows covers the items completed over the last 5
|
||||||
seconds, or since the line before when stderr is not a terminal. It falls to 0
|
seconds, or since the line before when stderr is not a terminal. It falls to 0
|
||||||
while nothing completes, and a burst after a wait no longer reads as a steady
|
while nothing completes, and a burst after a wait no longer reads as a steady
|
||||||
rate of over 1,000 files a second. The time remaining still comes from the
|
rate. The time remaining still comes from the phase's average. Once no item
|
||||||
phase's average. Once no item has completed for 10 seconds, the word `stalled`
|
has completed for 10 seconds, the word `stalled` and where the phase waits
|
||||||
and where the phase waits take the rate's place, as `stalled disk/db`. The
|
take the rate's place, as `stalled disk/db`. The places are `disk`, `db`,
|
||||||
places are `disk`, `db`, `out` for stdout and `cpu`, or two of them for a
|
`out` for stdout and `cpu`, or two of them for a phase that may wait on
|
||||||
phase that may wait on either. Each phase gives its word where it is defined.
|
either. A line too wide for the terminal loses its bar first, then its rate or
|
||||||
A line too wide for the terminal loses its bar first, then its rate or those
|
those words, so the phase, progress, elapsed time and time remaining stay
|
||||||
words, so the phase, progress, elapsed time and ETA stay whole. On an
|
whole. The content phase's last partial batch is written under a display of
|
||||||
80-column terminal, at counts like those of README's example, the content
|
|
||||||
phase's checks and reads lose those words at most times in minutes. Every
|
|
||||||
other phase keeps them whenever the elapsed time and the ETA are both in
|
|
||||||
minutes. The content phase's last partial batch is written under a display of
|
|
||||||
its own (2026-10-16, https://git.eeqj.de/sneak/sfdupes/issues/125)
|
its own (2026-10-16, https://git.eeqj.de/sneak/sfdupes/issues/125)
|
||||||
|
|
||||||
- New subcommand `info` prints the database's details on stdout, one per line as
|
- New subcommand `info` prints the database's details on stdout, one per line as
|
||||||
a name, a tab and the value:
|
a name, a tab and the value (2026-10-16,
|
||||||
|
https://git.eeqj.de/sneak/sfdupes/issues/117):
|
||||||
- the path and where it came from;
|
- the path and where it came from;
|
||||||
- the sizes of the database file and of its `-wal` file, when there is one;
|
- the sizes of the database file and of its `-wal` file, when there is one;
|
||||||
- when the database file was last changed;
|
- when the database file was last changed;
|
||||||
- the schema version;
|
- the schema version, the record counts and the sizes of all records added
|
||||||
- the number of records, how many have `head` and `tail` hashes, and how
|
up.
|
||||||
many have a content hash;
|
|
||||||
- the sizes of all records added up.
|
|
||||||
|
|
||||||
It opens the database as `report` does and shows two phases on stderr,
|
It opens the database as `report` does and shows two phases on stderr,
|
||||||
`[1/2] open` and `[2/2] count`. The schema version and the counts come from
|
`[1/2] open` and `[2/2] count`. The schema version and the counts come from
|
||||||
one read-only transaction, the counts from one query (2026-10-16,
|
one read-only transaction, the counts from one query.
|
||||||
https://git.eeqj.de/sneak/sfdupes/issues/117)
|
|
||||||
|
|
||||||
- `report` shows its work on stderr in two phases, `[1/2] open` and
|
- `report` shows its work on stderr in two phases, `[1/2] open` and
|
||||||
`[2/2] read`, with the progress display `scan` and `trees` use. The display
|
`[2/2] read`, with the progress display `scan` and `trees` use. The display
|
||||||
@@ -178,20 +164,19 @@ are known.
|
|||||||
(2026-10-16, https://git.eeqj.de/sneak/sfdupes/issues/113)
|
(2026-10-16, https://git.eeqj.de/sneak/sfdupes/issues/113)
|
||||||
|
|
||||||
- Each `scan` phase's progress display names the phase and its place among the
|
- Each `scan` phase's progress display names the phase and its place among the
|
||||||
five, as `[2/5] walk`. It runs from the moment the phase starts and shows its
|
five, as `[2/5] walk`, from the moment it starts, with its progress, elapsed
|
||||||
progress, elapsed time and ETA. `load` counts the records first. The walk's
|
time and ETA. `load` counts the records first. The walk's total is estimated
|
||||||
total is estimated from the records under the operands. The content phase
|
from the records under the operands. The content phase counts the records it
|
||||||
counts the records it checks from SQLite's grouping alone, then says it is
|
checks from SQLite's grouping alone, then says it is searching the database
|
||||||
searching the database until the first one is returned. On a terminal a phase
|
until the first one is returned. On a terminal a phase with an exact total
|
||||||
with an exact total draws its line followed by the library's bar. Any other
|
draws its line followed by the library's bar, and any other a spinner followed
|
||||||
phase draws a spinner followed by the line. Every display is drawn again
|
by the line. Every display is drawn again several times a second. No line
|
||||||
several times a second. No line reaches the terminal's last column, however
|
reaches the terminal's last column, however narrow, also after the terminal is
|
||||||
narrow, also after the terminal is made narrower during a phase. A line too
|
made narrower during a phase. A line too wide leaves out its rate, then its
|
||||||
wide leaves out its rate, then its bar, keeping the phase, progress, elapsed
|
bar, keeping the phase, progress, elapsed time and ETA whole. It is cut short
|
||||||
time and ETA whole. It is cut short only when those alone do not fit, as below
|
only when those alone do not fit, as below about 70 columns late in a large
|
||||||
about 70 columns late in a large scan. In plain mode the line is printed every
|
scan. In plain mode the line is printed every 5 seconds whether or not an item
|
||||||
5 seconds whether or not an item completed (2026-10-16,
|
completed (2026-10-16, https://git.eeqj.de/sneak/sfdupes/issues/112)
|
||||||
https://git.eeqj.de/sneak/sfdupes/issues/112)
|
|
||||||
|
|
||||||
- `scan`, `report` and `trees` first print a line on stderr naming the database
|
- `scan`, `report` and `trees` first print a line on stderr naming the database
|
||||||
they use, as an absolute path, and whether that path is the default or comes
|
they use, as an absolute path, and whether that path is the default or comes
|
||||||
@@ -200,8 +185,8 @@ are known.
|
|||||||
https://git.eeqj.de/sneak/sfdupes/issues/111)
|
https://git.eeqj.de/sneak/sfdupes/issues/111)
|
||||||
|
|
||||||
- The canonical files and model scripts re-vendored from `sneak/prompts` `next`
|
- The canonical files and model scripts re-vendored from `sneak/prompts` `next`
|
||||||
at `e3f79c4`:
|
at `e3f79c4` (2026-10-15, https://git.eeqj.de/sneak/sfdupes/issues/95):
|
||||||
- `golangci-lint` v2.14.0;
|
- golangci-lint v2.14.0;
|
||||||
- lint and test are phases of the `Dockerfile` that write no image, and
|
- lint and test are phases of the `Dockerfile` that write no image, and
|
||||||
`make test` runs the suite under the race detector. `Dockerfile.lint`,
|
`make test` runs the suite under the race detector. `Dockerfile.lint`,
|
||||||
`script/verify-lint-image-pin` and `make test-race` are gone;
|
`script/verify-lint-image-pin` and `make test-race` are gone;
|
||||||
@@ -209,18 +194,18 @@ are known.
|
|||||||
every stage of the `Dockerfile` declares first, so every `RUN` step runs
|
every stage of the `Dockerfile` declares first, so every `RUN` step runs
|
||||||
on every build. The steps that compile keep Go's build cache, and the
|
on every build. The steps that compile keep Go's build cache, and the
|
||||||
tests run with `-count=1`;
|
tests run with `-count=1`;
|
||||||
- `prettier` runs on the host, from the `node` and `yarn` that
|
- prettier runs on the host, from the node and yarn `script/bootstrap`
|
||||||
`script/bootstrap` installs, so the `prettier` and `markdown` stages are
|
installs, so the `prettier` and `markdown` stages are gone and the build
|
||||||
gone and the build stage installs `git` and `make` itself;
|
stage installs `git` and `make` itself;
|
||||||
- a new push cancels the workflow's older run on the same branch, and a run
|
- a new push cancels the workflow's older run on the same branch, and a run
|
||||||
stops after 20 minutes;
|
stops after 20 minutes;
|
||||||
- `.claude/settings.json` is deleted.
|
- `.claude/settings.json` is deleted.
|
||||||
|
|
||||||
Since https://git.eeqj.de/sneak/sfdupes/issues/109 two steps come before
|
Since https://git.eeqj.de/sneak/sfdupes/issues/109 the module download in
|
||||||
`ARG CACHEBUST`: the module download in `lint`, `test` and the build stage,
|
`lint`, `test` and the build stage, and the install of `git` and `make` in
|
||||||
and the install of `git` and `make` in the build stage. Docker keeps them
|
the build stage, come before `ARG CACHEBUST`. Docker keeps them between
|
||||||
between builds, and every step that lints, tests or compiles still runs on
|
builds, and every step that lints, tests or compiles still runs on every
|
||||||
every build (2026-10-15, https://git.eeqj.de/sneak/sfdupes/issues/95)
|
build.
|
||||||
|
|
||||||
- With `-x`, an operand under another operand is no longer dropped when the
|
- With `-x`, an operand under another operand is no longer dropped when the
|
||||||
outer operand's walk cannot reach it. That is the case when a directory from
|
outer operand's walk cannot reach it. That is the case when a directory from
|
||||||
@@ -273,8 +258,8 @@ are known.
|
|||||||
`script/` pass a new `CACHEBUST` value, so theirs always run (2026-10-04,
|
`script/` pass a new `CACHEBUST` value, so theirs always run (2026-10-04,
|
||||||
https://git.eeqj.de/sneak/sfdupes/issues/39)
|
https://git.eeqj.de/sneak/sfdupes/issues/39)
|
||||||
|
|
||||||
- `make fmt` and `make fmt-check` run `prettier` over all Markdown, and CI
|
- `make fmt` and `make fmt-check` run prettier over all Markdown, and CI checks
|
||||||
checks it. All Markdown was reformatted (2026-10-04,
|
it. All Markdown was reformatted (2026-10-04,
|
||||||
https://git.eeqj.de/sneak/sfdupes/issues/19)
|
https://git.eeqj.de/sneak/sfdupes/issues/19)
|
||||||
|
|
||||||
- `script/lint` writes no image, so a run no longer leaves an untagged one
|
- `script/lint` writes no image, so a run no longer leaves an untagged one
|
||||||
@@ -348,7 +333,7 @@ are known.
|
|||||||
https://git.eeqj.de/sneak/sfdupes/issues/53)
|
https://git.eeqj.de/sneak/sfdupes/issues/53)
|
||||||
|
|
||||||
- Tests cover stdout write failures in `report` and `trees`. README states that
|
- Tests cover stdout write failures in `report` and `trees`. README states that
|
||||||
`| head` ends `sfdupes` by `SIGPIPE` and that `>&-` writes to `/dev/null`
|
`| head` ends sfdupes by `SIGPIPE` and that `>&-` writes to `/dev/null`
|
||||||
(2026-10-03, https://git.eeqj.de/sneak/sfdupes/issues/30)
|
(2026-10-03, https://git.eeqj.de/sneak/sfdupes/issues/30)
|
||||||
|
|
||||||
- `report` and `trees` open the database read-only, and `scan` leaves it out of
|
- `report` and `trees` open the database read-only, and `scan` leaves it out of
|
||||||
@@ -367,16 +352,16 @@ are known.
|
|||||||
`make build` (2026-10-02, branch `next`,
|
`make build` (2026-10-02, branch `next`,
|
||||||
https://git.eeqj.de/sneak/sfdupes/issues/67)
|
https://git.eeqj.de/sneak/sfdupes/issues/67)
|
||||||
|
|
||||||
- The 1 KiB end-window sampling was replaced with the hashes chosen by file
|
- The 1 KiB end-window sampling was replaced with the head/tail plus
|
||||||
size, and README "Duplicate detection" documented every size threshold. A file
|
content-hash ladder, and README "Duplicate detection" documented every rung. A
|
||||||
under 10 MiB was hashed in full, and its `head`, `tail` and `content` all held
|
file under 10 MiB was hashed in full, and its `head`, `tail` and `content` all
|
||||||
that hash. A larger file got only its 64 KiB `head` and `tail` in the hash
|
held that hash. A larger file got only its 64 KiB `head` and `tail` in the
|
||||||
phase. The content phase, after the update phase, read it for `content` only
|
hash phase. The content phase, after the update phase, read it for `content`
|
||||||
when its size, `head` and `tail` matched another record's, from this scan or
|
only when its size, `head` and `tail` matched another record's. `content` was
|
||||||
an earlier one. `content` was the whole file below 50 MiB, and gigabyte-spaced
|
the whole file below 50 MiB, and gigabyte-spaced 1 MiB samples at or above. A
|
||||||
1 MiB samples at or above. A file gone or changed since its record was written
|
file gone or changed since its record was written was never read. `report` and
|
||||||
was never read. `report` and `trees` left out any record without a `content`
|
`trees` left out any record without a `content` hash. The `content` column is
|
||||||
hash. The `content` column is part of the version 1 schema. Superseded by
|
part of the version 1 schema. Superseded by
|
||||||
https://git.eeqj.de/sneak/sfdupes/issues/132 (2026-09-22, branch `next`,
|
https://git.eeqj.de/sneak/sfdupes/issues/132 (2026-09-22, branch `next`,
|
||||||
https://git.eeqj.de/sneak/sfdupes/issues/61)
|
https://git.eeqj.de/sneak/sfdupes/issues/61)
|
||||||
|
|
||||||
@@ -389,11 +374,11 @@ are known.
|
|||||||
`# image:vX.Y.Z, YYYY-MM-DD` comment over a bare `FROM image@sha256:...`,
|
`# image:vX.Y.Z, YYYY-MM-DD` comment over a bare `FROM image@sha256:...`,
|
||||||
without the false `(Debian-based)` note or the tag. Since
|
without the false `(Debian-based)` note or the tag. Since
|
||||||
https://git.eeqj.de/sneak/sfdupes/issues/95 the `Dockerfile`'s `lint` phase
|
https://git.eeqj.de/sneak/sfdupes/issues/95 the `Dockerfile`'s `lint` phase
|
||||||
holds the only `golangci-lint` pin, in that form. `Dockerfile.lint` and
|
holds the only golangci-lint pin, in that form. `Dockerfile.lint` and
|
||||||
`script/verify-lint-image-pin`, which compared the two pins, are gone
|
`script/verify-lint-image-pin`, which compared the two pins, are gone
|
||||||
(2026-08-10, branch `next`, https://git.eeqj.de/sneak/sfdupes/issues/25)
|
(2026-08-10, branch `next`, https://git.eeqj.de/sneak/sfdupes/issues/25)
|
||||||
|
|
||||||
- All linting ran in Docker, via `Dockerfile.lint` and `script/lint`. Per the
|
- All linting runs in Docker, via `Dockerfile.lint` and `script/lint`. Per the
|
||||||
owner ruling the linter is never installed on a host, and
|
owner ruling the linter is never installed on a host, and
|
||||||
`golangci-lint config verify` runs before `golangci-lint run`.
|
`golangci-lint config verify` runs before `golangci-lint run`.
|
||||||
`script/bootstrap` stopped installing or pinning the linter, and
|
`script/bootstrap` stopped installing or pinning the linter, and
|
||||||
@@ -402,7 +387,7 @@ are known.
|
|||||||
`Dockerfile`'s `lint` phase, which `script/lint` builds alone and the build
|
`Dockerfile`'s `lint` phase, which `script/lint` builds alone and the build
|
||||||
stage depends on through `COPY --from=lint /src/go.sum /dev/null`.
|
stage depends on through `COPY --from=lint /src/go.sum /dev/null`.
|
||||||
`Dockerfile.lint` and `script/verify-lint-image-pin` are gone. Nothing inside
|
`Dockerfile.lint` and `script/verify-lint-image-pin` are gone. Nothing inside
|
||||||
an image build may run `docker`, so the phase calls `golangci-lint` directly
|
an image build may run docker, so the phase calls `golangci-lint` directly
|
||||||
(2026-08-10, branch `next`, https://git.eeqj.de/sneak/sfdupes/issues/46)
|
(2026-08-10, branch `next`, https://git.eeqj.de/sneak/sfdupes/issues/46)
|
||||||
|
|
||||||
- The Docker build stage's prerequisites were installed by running
|
- The Docker build stage's prerequisites were installed by running
|
||||||
@@ -429,19 +414,18 @@ are known.
|
|||||||
(2026-08-09, branch `cibuild-cache-bust`,
|
(2026-08-09, branch `cibuild-cache-bust`,
|
||||||
https://git.eeqj.de/sneak/sfdupes/issues/32)
|
https://git.eeqj.de/sneak/sfdupes/issues/32)
|
||||||
|
|
||||||
- `script/bootstrap` checked the installed `golangci-lint` version instead of
|
- `script/bootstrap` checked the installed golangci-lint version instead of only
|
||||||
only its presence. The version lived only in `GOLANGCI_LINT_VERSION`, with the
|
its presence. The version lived only in `GOLANGCI_LINT_VERSION`, with the
|
||||||
`go install` module ref derived from it. Any installed version that was not
|
`go install` module ref derived from it, and any installed version that was
|
||||||
the pin was reinstalled: older, newer, absent or unparseable. `go install`
|
not the pin was reinstalled. `go install` writes into `GOBIN` (or
|
||||||
writes into `GOBIN` (or `GOPATH/bin`) while `make lint` ran the first
|
`GOPATH/bin`) while `make lint` ran the first `golangci-lint` on `PATH`, so
|
||||||
`golangci-lint` on `PATH`, so bootstrap re-read the effective version after
|
bootstrap re-read the effective version after installing. On a mismatch it
|
||||||
installing. On a mismatch it printed both paths and both versions and exited
|
printed both paths and both versions and exited non-zero; it did not reorder
|
||||||
non-zero; it did not reorder `PATH` or delete anyone's binary. The `--version`
|
`PATH` or delete anyone's binary. The `--version` call kept its stderr and was
|
||||||
call kept its stderr and was bounded by `timeout(1)` where that exists. `git`,
|
bounded by `timeout(1)` where that exists. `git`, `make` and `go` kept
|
||||||
`make` and `go` kept presence-only checks. `make check` and `make lint` were
|
presence-only checks. Superseded by
|
||||||
clean at v2.12.2, so v2.10.1 had not been hiding findings on `main`.
|
https://git.eeqj.de/sneak/sfdupes/issues/46: the linter is no longer installed
|
||||||
Superseded by https://git.eeqj.de/sneak/sfdupes/issues/46: the linter is no
|
on a host (2026-08-09, branch `bootstrap-version-check`,
|
||||||
longer installed on a host (2026-08-09, branch `bootstrap-version-check`,
|
|
||||||
https://git.eeqj.de/sneak/sfdupes/issues/24)
|
https://git.eeqj.de/sneak/sfdupes/issues/24)
|
||||||
|
|
||||||
- The hash worker pool unwinds on the error path. The pool is an owned,
|
- The hash worker pool unwinds on the error path. The pool is an owned,
|
||||||
@@ -453,17 +437,13 @@ are known.
|
|||||||
pools and the whole database layer, as the first parameter everywhere. The
|
pools and the whole database layer, as the first parameter everywhere. The
|
||||||
walk pool gets the same treatment, plus a `ctx.Err()` guard after the walk. A
|
walk pool gets the same treatment, plus a `ctx.Err()` guard after the walk. A
|
||||||
cancelled walk yields a partial size census, and every file it never reached
|
cancelled walk yields a partial size census, and every file it never reached
|
||||||
looks vanished to the update phase. That phase's own `BeginTx` also fails on
|
looks vanished to the update phase. The guard is the barrier that still holds
|
||||||
the cancelled context before deleting anything, but the guard is the barrier
|
once an interrupted scan may commit what it has. Tests drive `run(scan)`
|
||||||
that still holds once an interrupted scan may commit what it has. Tests drive
|
against a database whose insert trigger aborts, and assert that the scan fails
|
||||||
`run(scan)` against a database whose insert trigger aborts, and assert that
|
instead of hanging and that the goroutine count returns to its pre-scan
|
||||||
the scan fails instead of hanging and that the goroutine count returns to its
|
baseline. Other tests cancel a scan part-way through the walk and assert that
|
||||||
pre-scan baseline. Other tests cancel a scan part-way through the walk and
|
it stops at the guard with every record intact (2026-08-09, branch
|
||||||
assert that it stops at the guard with every record intact. Direct tests of
|
`hash-pool-cleanup`, https://git.eeqj.de/sneak/sfdupes/issues/6)
|
||||||
`sendEvent`, the walk workers, `dispatchDirs`, `feedHashJobs`, `hashWorker`
|
|
||||||
and `hashPhase` cover the remaining cancellation branches of both pools
|
|
||||||
(2026-08-09, branch `hash-pool-cleanup`,
|
|
||||||
https://git.eeqj.de/sneak/sfdupes/issues/6)
|
|
||||||
|
|
||||||
- The database is closed on every fatal exit path. `fatalf` and its `os.Exit(1)`
|
- The database is closed on every fatal exit path. `fatalf` and its `os.Exit(1)`
|
||||||
are gone, so the deferred `db.Close()`, and with it the SQLite WAL checkpoint,
|
are gone, so the deferred `db.Close()`, and with it the SQLite WAL checkpoint,
|
||||||
@@ -471,14 +451,13 @@ are known.
|
|||||||
`loadRecords` and `resolveRoots` return errors instead. The single exit point
|
`loadRecords` and `resolveRoots` return errors instead. The single exit point
|
||||||
is `run` in `main.go`. It maps a `fatalError` to exit 1 and cobra's own
|
is `run` in `main.go`. It maps a `fatalError` to exit 1 and cobra's own
|
||||||
argument and flag errors to exit 2, so a runtime failure is never reported as
|
argument and flag errors to exit 2, so a runtime failure is never reported as
|
||||||
a usage error or printed with the usage text. `main_test.go` drives the CLI
|
a usage error. `main_test.go` drives the CLI in-process and asserts the exit
|
||||||
in-process and asserts the exit codes from README "Error handling" and the
|
codes from README "Error handling" and the stdout/stderr split. That includes
|
||||||
stdout/stderr split. That includes a fatal error after the database is open
|
a fatal error after the database is open leaving no `-wal` or `-shm` file
|
||||||
leaving no `-wal` or `-shm` file behind, for `scan`, `report` or `trees`
|
behind (2026-08-09, branch `db-close-on-fatal`,
|
||||||
(2026-08-09, branch `db-close-on-fatal`,
|
|
||||||
https://git.eeqj.de/sneak/sfdupes/issues/4)
|
https://git.eeqj.de/sneak/sfdupes/issues/4)
|
||||||
|
|
||||||
- `golangci-lint` updated to v2.12.2 with the canonical config, merged as
|
- golangci-lint updated to v2.12.2 with the canonical config, merged as
|
||||||
`38a01bd`. The pinned linter in the `Dockerfile` lint stage and
|
`38a01bd`. The pinned linter in the `Dockerfile` lint stage and
|
||||||
`script/bootstrap` went from v2.12.1 to v2.12.2, and `.golangci.yml` was
|
`script/bootstrap` went from v2.12.1 to v2.12.2, and `.golangci.yml` was
|
||||||
replaced with the canonical file. The linter settings (`lll`, `funlen`,
|
replaced with the canonical file. The linter settings (`lll`, `funlen`,
|
||||||
@@ -487,13 +466,9 @@ are known.
|
|||||||
`golangci-v2.12.2`, https://git.eeqj.de/sneak/sfdupes/issues/3)
|
`golangci-v2.12.2`, https://git.eeqj.de/sneak/sfdupes/issues/3)
|
||||||
|
|
||||||
- Makefile targets converted to scripts-to-rule-them-all `script/` entrypoints,
|
- Makefile targets converted to scripts-to-rule-them-all `script/` entrypoints,
|
||||||
like the other managed repos. All 12 entrypoints exist:
|
like the other managed repos. All 12 entrypoints exist, and every Makefile
|
||||||
- `bootstrap`, `setup` and `projectname`;
|
target is a thin shim over them (2026-07-26, commit `3abeacf`,
|
||||||
- `test`, `lint`, `fmt`, `fmt-check` and `check`;
|
https://git.eeqj.de/sneak/sfdupes/issues/1)
|
||||||
- `docker`, `cibuild`, `precommit` and `install-precommit`.
|
|
||||||
|
|
||||||
Every Makefile target is a thin shim over them (2026-07-26, commit
|
|
||||||
`3abeacf`, https://git.eeqj.de/sneak/sfdupes/issues/1)
|
|
||||||
|
|
||||||
- The binary is the default Make target: plain `make` builds `sfdupes`, where it
|
- The binary is the default Make target: plain `make` builds `sfdupes`, where it
|
||||||
used to run `check` plus `build`. `make build` remains as an alias
|
used to run `check` plus `build`. `make build` remains as an alias
|
||||||
@@ -508,15 +483,10 @@ are known.
|
|||||||
required (2026-07-24, branch `scan-wide-phases`)
|
required (2026-07-24, branch `scan-wide-phases`)
|
||||||
|
|
||||||
- The stat pass split back out of the walk. Phases are strictly sequential
|
- The stat pass split back out of the walk. Phases are strictly sequential
|
||||||
again, per operand, with parallelism only inside each phase:
|
again, walk, stat, hash and update per operand, with parallelism only inside
|
||||||
- walk;
|
each phase. The walk enumerates paths with per-directory workers, and the stat
|
||||||
- stat;
|
pass lstats them with per-file workers, restoring the exact total and ETA of
|
||||||
- hash;
|
the stat bar (2026-07-24, branch `parallel-phases`)
|
||||||
- update.
|
|
||||||
|
|
||||||
The walk enumerates paths with per-directory workers, and the stat pass
|
|
||||||
lstats them with per-file workers, restoring the exact total and ETA of the
|
|
||||||
stat bar (2026-07-24, branch `parallel-phases`)
|
|
||||||
|
|
||||||
- Each operand is announced on stderr before its passes. With per-operand walk,
|
- Each operand is announced on stderr before its passes. With per-operand walk,
|
||||||
hash and update cycles, a multi-operand run such as `scan /srv/*` showed pass
|
hash and update cycles, a multi-operand run such as `scan /srv/*` showed pass
|
||||||
@@ -549,34 +519,26 @@ are known.
|
|||||||
at filesystem boundaries, which are crossed by default (2026-07-23, branch
|
at filesystem boundaries, which are crossed by default (2026-07-23, branch
|
||||||
`scan-required-paths`)
|
`scan-required-paths`)
|
||||||
|
|
||||||
- The repo was brought into compliance with `REPO_POLICIES.md` (the version of
|
- The repo brought into compliance with `REPO_POLICIES.md` (2026-07-06), the
|
||||||
2026-07-06), the existing repo checklist and the Go styleguide. The audit of
|
existing repo checklist and the Go styleguide, audited 2026-07-22 (2026-07-23,
|
||||||
2026-07-22 found these gaps, and the branch closed them:
|
branch `repo-policy-compliance`). Added or fixed:
|
||||||
- `.gitignore`, with OS, editor, Go and secrets patterns; the compiled
|
- `.gitignore`, with OS, editor, Go and secrets patterns; the compiled
|
||||||
`sfdupes` binary and `files.dat` had been sitting untracked in the tree;
|
`sfdupes` binary and `files.dat` had been sitting untracked in the tree;
|
||||||
- `.editorconfig`;
|
- `.editorconfig`;
|
||||||
- `LICENSE` and the README License section. MIT was assumed from house
|
- `LICENSE` (MIT) and the README License section;
|
||||||
convention, for the owner to confirm;
|
|
||||||
- `REPO_POLICIES.md` in the repo root;
|
- `REPO_POLICIES.md` in the repo root;
|
||||||
- the canonical `.golangci.yml`, with the 150 findings it raised fixed;
|
- the canonical `.golangci.yml`, with the 150 findings it raised fixed;
|
||||||
- the `Makefile` targets `test`, `lint`, `fmt`, `fmt-check`, `docker` and
|
- the `Makefile` targets `test`, `lint`, `fmt`, `fmt-check`, `docker` and
|
||||||
`hooks`, and a `check` target that no longer depends on `build`;
|
`hooks`, and a `check` target that no longer depends on `build`;
|
||||||
- a test suite, which the policy required with a 30-second timeout and the
|
- a test suite covering parsing, grouping, digests, suppression, hashing and
|
||||||
conditional `-v` rerun. It covers parsing, grouping, digests, suppression,
|
the scan pipeline, at 64% coverage;
|
||||||
hashing and the scan pipeline, at 64% coverage;
|
- a multistage `Dockerfile` with hash-pinned images, and `.dockerignore`;
|
||||||
- a multistage `Dockerfile` with hash-pinned images, a fail-fast lint stage
|
|
||||||
and a build stage running `make check`, plus `.dockerignore`;
|
|
||||||
- `.gitea/workflows/check.yml`, running `docker build .` on push with the
|
- `.gitea/workflows/check.yml`, running `docker build .` on push with the
|
||||||
checkout action pinned by commit SHA;
|
checkout action pinned by commit SHA;
|
||||||
- the README sections the policy requires: a Description first line with the
|
- the README sections the policy requires;
|
||||||
name, purpose, category, license and author, then Getting Started,
|
|
||||||
Rationale, TODO, License and Author;
|
|
||||||
- the stale README non-goal "no git repository setup and no CI" removed;
|
- the stale README non-goal "no git repository setup and no CI" removed;
|
||||||
- the pre-commit hook installed.
|
- the pre-commit hook installed.
|
||||||
|
|
||||||
The code was already `gofmt`-clean, so no standalone formatting commit was
|
|
||||||
needed (2026-07-23, branch `repo-policy-compliance`)
|
|
||||||
|
|
||||||
- `git init` with a README-only first commit; the code baseline committed on
|
- `git init` with a README-only first commit; the code baseline committed on
|
||||||
`main` (2026-07-22)
|
`main` (2026-07-22)
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user