Commit Graph
11 Commits
Author SHA1 Message Date
clawbot 8ebb6ae818 List a directory named through a symlink in gen and freshen (closes #185)
check / check (push) Waiting to run
The walk in gen and freshen does not follow a symlink at its top. A
directory given as a symlink, or a working directory whose path names
one, was listed as empty and gen exited 0, while freshen removed every
entry from the manifest. Each walk now resolves its starting directory
with filepath.EvalSymlinks first, as check's search for extra files
does, and starts at the path as named if that fails. Symlinks inside the
tree are still skipped unless --follow-symlinks is given.

Model: opus-5-5
2026-10-08 02:56:54 +00:00
clawbot e35cd4a045 Resolve check and freshen paths against the manifest's directory (closes #177)
check / check (push) Waiting to run
Without --base, check and freshen now look for a manifest's files in the
directory that holds it, the file named or the one found in a directory
argument, instead of the current directory. So `mfer check /media/drive`
checks a drive against its own index.mf from anywhere. check of a manifest
given by URL still uses the current directory, and --base still overrides,
even when it names the current directory. The --base help text of both
commands and the README's Tool Examples state the default; the README gains
a freshen entry for this, which also names the hidden files and symlinks
freshen leaves out by default and the flags that include them.

Model: opus-5-5
2026-10-07 17:28:38 +02:00
clawbot 2a270b40c5 Record file mode in the manifest, 0000 unless asked (closes #161)
check / check (push) Failing after 4s
MFFilePath gains mode (field 304): a file's permission bits, 0777 at
most, or 0000, meaning none recorded. gen and freshen record real modes
only with --include-permissions (ScannerOptions.IncludePermissions); the
builder keeps only mode.Perm(), so setuid, setgid and sticky are never
written. list -l and export show the mode in octal. check reports
MODE_MISMATCH for a recorded mode other than 0000 the file lacks. fetch
refuses a manifest with a mode above 0777 before requesting any file,
sets only the permission bits of each recorded mode on the files it
writes, and downloads again a present file whose mode differs. The decoding-cost bound counts a file
entry at 176 bytes, up from 160.

Model: opus-5-5
2026-10-06 11:43:18 +02:00
clawbot ba3d24ca42 End-to-end tests for freshen and fetch (closes #66)
check / check (push) Failing after 3s
The freshen tests built a fixture and never ran freshen. They now run
gen and freshen through the command entry point on a temp dir: an
unchanged tree leaves the manifest's entries as they were, and a
modified file (also one edited without changing its size), a new or a
deleted file gives a manifest that lists exactly the tree, with sizes,
hashes and mtimes from disk, on which check passes.

New fetch tests run the command against an httptest server: a nested
tree lands complete, a hash mismatch exits non-zero and leaves no file,
and a partly filled destination gets every listed file downloaded again
and replaced, while a file the manifest does not list is left alone.

Model: opus-5-5
2026-10-04 17:02:22 +02:00
clawbot 4bf87d1ccf AddFileWithHash rejects hashes that are not multihashes (closes #129)
check / check (push) Failing after 2s
AddFileWithHash took any non-empty bytes as a hash, so the builder could
write a manifest that mfer refuses to load. It now decodes the hash with
go-multihash and also requires a digest of at least 32 bytes, the SHA-256
length the reader's decoding-cost limit assumes: a valid but shorter
multihash, such as an empty identity hash or SHA-1, still makes a
manifest of one-character paths too costly to load. When mfer freshen
meets such a hash in an existing manifest, its error names the manifest
entry and says to regenerate the manifest with mfer generate. Test
fixtures whose stand-in hashes were not valid multihashes now use a
SHA-256 multihash.

Model: opus-5-5
2026-10-04 14:48:49 +02:00
clawbot e412d20c20 Default the manifest to index.mf and keep it out of its own listing (closes #100)
check / check (push) Failing after 1s
mfer gen now writes index.mf instead of .index.mf, the name fetch
requests and the README calls the standard filename. Given a
directory, check, freshen, list and export look only for index.mf;
.index.mf is no longer recognized. Because index.mf is not hidden, gen
and freshen leave out of their own listing the manifest they write and
a temp file an interrupted run left beside it, matched by file
identity (os.SameFile) however the path is spelled. The scanner takes
these as a plain ExcludePaths list; manifestTempPath alone names the
temp file, for both writes, both skips and the tests.

Model: opus-5-5
2026-10-04 13:48:52 +02:00
clawbot 0a9963002c NewChecker takes CheckerOptions instead of positional arguments (closes #78)
check / check (push) Failing after 1s
NewChecker now takes *CheckerOptions (ManifestPath, BasePath, Fs), named
like ScannerOptions. A nil Fs still means the OS filesystem, as before and
as in ScannerOptions; nil options or an empty path return an error naming
the missing path.

Audit of the other exported constructors in mfer: NewManifestFromFile took
a filesystem and a path positionally; it now takes
*ManifestFromFileOptions (Path, Fs) with the same nil and empty rules.
NewBuilder and NewScanner take no arguments, NewScannerWithOptions already
takes options, and NewManifestFromReader takes one reader, which the style
guide exempts; these are unchanged.

Model: opus-5-5
2026-10-04 12:19:31 +02:00
clawbot c31796998f Pin CLI error messages by driving their real call sites (closes #87)
check / check (push) Successful in 58s
errmsg_test.go now calls the function that emits each user-visible CLI
error message and asserts on the full text it returns, instead of
rendering format strings copied from production, so rewording any pinned
message fails the suite. The unknown-command message is driven through
the root command's action.

The signer-mismatch case signs a manifest with a throwaway gpg key and
is skipped where gpg is absent, like the repo's other signing tests.

The freshen mtime-presence test gives the scanned file an epoch mtime,
so it fails if recordEntry reads an absent manifest mtime as the epoch.

Model: opus-4-8 (first implementation); opus-5-5 (completion, this message)
2026-10-03 18:24:30 +02:00
clawbot de476708e9 Update golangci-lint to v2.12.2 with canonical config (closes #60)
check / check (push) Has been cancelled
Adopts golangci-lint v2.12.2 and the canonical .golangci.yml (default: all), and fixes all resulting findings across the tree.

Two intended behavior changes: absent MFFilePath.Mtime is handled explicitly in freshen, list and export rather than dereferenced (main panicked); gpg positional key IDs now follow an explicit -- end-of-options marker.

All twelve reworded user-visible error messages restored to byte-identical parity with main and pinned by tests.
2026-08-10 16:06:12 +02:00
sneak a5b0343b28 Use Go 1.13+ octal literal syntax throughout codebase
Update file permission literals from legacy octal format (0755, 0644)
to explicit Go 1.13+ format (0o755, 0o644) for improved readability.
2025-12-18 01:29:40 -08:00
sneak c5ca3e2ced Change FileProgress callback to channel-based progress
Replace callback-based progress reporting in Builder.AddFile with
channel-based FileHashProgress for consistency with EnumerateStatus
and ScanStatus patterns. Update scanner.go to use the new channel API.
2025-12-17 14:30:10 -08:00