All checks were successful
check / check (pull_request) Successful in 2m24s
Three defects in the merged listing path, all found in review. 1. The TIMESTAMP column mixed two timezones. Remote-only rows render timestamp.UTC(); local rows come from ListRecent, whose scanner decoded Unix seconds in the host's local zone, unlike the two other snapshot scanners in that file. Both render through the same zone-less format string, so on a non-UTC host the same snapshot showed one time when locally tracked and another when remote-only, in the same column, with nothing to indicate why. Normalized at the point the timestamp enters the domain rather than at the display layer: scanSnapshotRows now decodes in UTC like its siblings, and GetIncompleteByHostname's copy of that loop was folded onto the shared scanner so the three call sites cannot drift apart again. 2. --json silently truncated. The early return skipped reportListDrift, so neither the 1000-row cap nor the unreadable-manifest count reached a machine consumer: past the cap the document was short with no signal at all. Both counts now go to stderr, where the unreachable-destination warning already goes. The document's shape is deliberately unchanged, so existing consumers keep parsing. 3. The --json stderr workaround was half-applied. Two per-snapshot log.Warn calls on the same new path were left unguarded, and the logger writes to stdout at default level, so one corrupt manifest put a log line ahead of the document and broke `| jq`. Both now route through the same JSON-aware writer. They are also collected during the concurrent manifest reads and emitted afterwards in key order, since that writer is not safe for concurrent use. Still a local workaround; the logger itself is issue #82. Also from review, non-blocking: the destination-listing failure is no longer printed twice in table mode, the identifier cell is no longer computed and discarded for locally tracked rows, and the merge sort is stable so that rows sharing the zero-timestamp fallback keep a deterministic order. Tests: each fix has a test that fails without it. The timezone tests pin time.Local to a non-UTC zone and assert Location identity, so they would have caught this on the UTC host where it was missed. The JSON stdout test redirects the process's own stdout to a pipe and rebuilds the logger over it, so it can actually observe a log line landing on stdout ahead of the document rather than asserting on an injected buffer the log never reaches.
115 lines
6.2 KiB
Markdown
115 lines
6.2 KiB
Markdown
# Workflow
|
|
|
|
* branch (from `main`)
|
|
* do the work in Next Step
|
|
* move Next Step to the top of Completed Steps
|
|
* move the top item of Future Steps into Next Step
|
|
* commit (`TODO.md` changes in the same commit as the work)
|
|
* merge to `main` if the branch is not protected, otherwise open a PR
|
|
* push
|
|
|
|
# Status
|
|
|
|
pre-1.0
|
|
|
|
# Next Step
|
|
|
|
Triage the stale remote branches (issue #71): for each, merge the work
|
|
or delete the branch.
|
|
|
|
# Completed Steps
|
|
|
|
- 2026-08-09: Made `snapshot list` list the destination store without
|
|
the private key (issue #64). The listing is now the union of the
|
|
local index and a single streamed listing of the `metadata/` prefix,
|
|
with no `age_secret_key` gate — the manifest is unencrypted, so a
|
|
host holding only the public key can enumerate its own backups and a
|
|
host that lost its local index can still see them. A remote-only
|
|
snapshot's hostname and name are deliberately not recovered (they are
|
|
not recoverable without the private key, and making them so would
|
|
undo the privacy property tracked in issue #81); such rows are
|
|
labelled by an abbreviation of their remote key and carry the real
|
|
timestamp and compressed size from the manifest, with `<remote only>`
|
|
in the two columns that require the local index. Local-only snapshots
|
|
are reported as drift, and the hint now names `vaultik prune`, which
|
|
exists, instead of `vaultik snapshot cleanup`, which does not.
|
|
`reportRemoteDrift` collapsed into the merged view. Every remote
|
|
manifest read in the codebase now goes through
|
|
`downloadManifestByKey`, so issue #81 has one call site to change.
|
|
Review rework: snapshot timestamps now normalize to UTC in
|
|
`scanSnapshotRows`, the one place they enter the domain, so the merged
|
|
TIMESTAMP column cannot show local time for a locally tracked row and
|
|
UTC for a remote-only row on a non-UTC host; `GetIncompleteByHostname`
|
|
was folded onto that same scanner. `--json` now reports the
|
|
unreadable-manifest count and the 1000-row truncation on stderr
|
|
instead of returning a silently short document (the document's shape
|
|
is unchanged). The two per-snapshot `log.Warn` calls on the listing
|
|
path now route through the same JSON-aware writer as the existing
|
|
workaround, so one corrupt manifest can no longer put a log line on
|
|
stdout ahead of the document and break `| jq` — still a local
|
|
workaround pending issue #82. Verified with `script/cibuild` and with
|
|
an uncached `make check` (`0 issues.`, no cached test packages), plus
|
|
end to end against a `file://` destination with no secret key present.
|
|
- 2026-08-09: Closed the gap between `make lint` and CI (issue #78).
|
|
`script/lint` now runs the digest-pinned `golangci-lint` image taken
|
|
from the `Dockerfile` lint stage, which is the single source of truth
|
|
for the linter version; the duplicate pin in the `Makefile` `deps`
|
|
target and the unpinned `golangci-lint` install in `script/bootstrap`
|
|
are gone. A `golangci-lint` on `PATH` is used only when its version is
|
|
exactly the pinned one (which is how the lint stage runs it inside the
|
|
container); anything else goes through Docker, and a missing or
|
|
unreachable Docker daemon is a hard error rather than a silent
|
|
fallback. `make check` is therefore now as trustworthy as
|
|
`script/cibuild`.
|
|
- 2026-08-09: Finished the lint remediation under the canonical
|
|
`.golangci.yml` (issue #61, which also unblocks issue #59). The
|
|
remaining findings were fixed behavior-preservingly: `wsl_v5`
|
|
whitespace, `sqlclosecheck`, and `prealloc`. The `sqlclosecheck` sites
|
|
now close `sql.Rows` in a deferred closure instead of via the
|
|
`CloseRows` helper, which the linter could not see through. Only the
|
|
`revive` package-name findings remain suppressed, with per-site
|
|
`//nolint` directives; the package-rename question behind them is
|
|
tracked in issue #76. Verified with `script/cibuild`, which exits 0 —
|
|
that is the only trustworthy gate, because `script/lint` runs whatever
|
|
`golangci-lint` happens to be on `PATH` rather than the pinned
|
|
v2.12.2 that CI and the `Dockerfile` use, so `make check` can report
|
|
green on findings CI still fails. That tooling gap is tracked in issue
|
|
#78.
|
|
- 2026-08-09: The earlier next step "reconcile the uncommitted
|
|
`ARCHITECTURE.md` edits on `main`" needed no work: the working tree is
|
|
clean and `ARCHITECTURE.md` is committed on `main`.
|
|
- 2026-08-07: Updated golangci-lint to v2.12.2 everywhere it is pinned
|
|
(`Dockerfile` lint stage, `Makefile` deps target), replaced
|
|
`.golangci.yml` with the canonical config (v2 schema, `default: all`),
|
|
and remediated the bulk of the lint findings it surfaced (issue #61):
|
|
behavior-preserving fixes across every package, 2,990 findings down to
|
|
80. `make test` and `make fmt-check` were green at that point but
|
|
`make lint` was still red; the commit message claiming `make check`
|
|
was green was wrong.
|
|
- 2026-08-07: Added the standard `.golangci.yml` and `.editorconfig`
|
|
(issue #59); lint findings under the new config are tracked in issue
|
|
#61. `script/bootstrap` now installs sqlite3 (needed by tests).
|
|
- 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints,
|
|
Makefile shims, README Entrypoints section
|
|
- 2026-07-02: Consolidated CLI verbs, retired overlapping commands; bound
|
|
the local index to its backup destination URL.
|
|
- 2026-06-28: snapshot rm now removes metadata only and prints the prune
|
|
command; restore skips chown when running as non-root.
|
|
- 2026-06-26: Snapshot IDs hashed at the storage boundary; snapshot list
|
|
made resilient to bad remote entries.
|
|
- 2026-06-24: Collapsed snapshot prune into vaultik prune; restore streams
|
|
blobs to disk and restores files in blob-locality order; cron output
|
|
fixes.
|
|
- 2026-06-17: Restore overhaul: ReadAt chunk reads from cached blobs,
|
|
reference-counted blob sweeper, integration tests; new internal/ui
|
|
output layer, banner, and progress lines.
|
|
- 2025-12-18: Added ARCHITECTURE.md and godoc coverage for exported API.
|
|
- 2025-07-26: End-to-end integration tests; manifest format refactor;
|
|
renamed backup to snapshot; afero filesystem abstraction.
|
|
- 2025-07-20: Initial design and implementation: cobra + fx CLI skeleton,
|
|
SQLite index database, UUID blob storage with streaming chunking.
|
|
|
|
# Future Steps
|
|
|
|
- Define remaining scope for a first tagged release and cut v0.1.0.
|