Update golangci-lint to v2.12.2 with canonical config (#62)
check / check (push) Successful in 5s
check / check (push) Successful in 5s
Updates golangci-lint to v2.12.2 everywhere it is pinned and installs the canonical `.golangci.yml`, then remediates every finding the new linter/config surfaces so `make check` is green. ## Version bump - `Dockerfile` lint stage: `golangci/golangci-lint:v2.11.3-alpine` -> `v2.12.2-alpine` (digest-pinned, date comment updated) - `Makefile` `deps` target: `go install` moved from the old v1 module path at `@latest` to the pinned `github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2` - `.golangci.yml` replaced with the canonical config (v2 schema; settings under `linters.settings` so the thresholds actually apply; `default: all` with the standard six disables) - `script/bootstrap` installs golangci-lint via the system package manager and carries no version pin, so it is unchanged - CI (`.gitea/workflows/check.yml`) only runs `script/cibuild`, so it needed no change ## Lint remediation The canonical config surfaced ~3,300 findings across 56k lines. All are fixed, behavior-preserving; incorporates and supersedes the per-package mechanical passes already merged to `main` (refs #61). Highlights: - `err113`: dynamic errors replaced with package sentinels + `%w` wrapping; comparisons via `errors.Is` - `goprintffuncname`: printf-style helpers renamed with an `f` suffix (`ui.Writer` message methods, `cli.ReportErrorf`, `database.Fatalf`) and all call sites updated - `revive` stutter renames: `blob.Handler`, `blob.WithReader`, `blob.ChunkPosition`, `storage.URL`, `storage.Info`; missing doc comments added - `contextcheck`/`noctx`: `context.Context` threaded through `blob.Packer` and the scanner call sites; context-aware `exec`/`sql` variants - `funlen`/`cyclop`/`gocognit`/`dupl`: oversized and duplicated functions split into focused helpers (production and test code) - tests: `t.Parallel()` added where safe (global logger init kept in the serial phase for `-race`), `t.TempDir()`/`t.Helper()` adopted, several suites converted to external test packages - `gosec`: bounded integer conversions, `ReadHeaderTimeout` on the test HTTP server; remaining warnings suppressed per-site with justifications - remaining `nolint` directives are rare, targeted, and each carries a reason (e.g. `nilnil` not-found contract in the repository layer, fx module globals, on-disk snake_case struct tags) - removed the deprecated `log.LogOptions` alias (callers migrated to `log.Options`) `make check` (tests with `-race`, lint, fmt-check) passes. Co-authored-by: sneak <sneak@sneak.berlin> Reviewed-on: #62 Co-authored-by: clawbot <clawbot@noreply.example.org> Co-committed-by: clawbot <clawbot@noreply.example.org>
This commit was merged in pull request #62.
This commit is contained in:
@@ -8,13 +8,56 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"sneak.berlin/go/vaultik/internal/types"
|
||||
)
|
||||
|
||||
// percentScale converts a 0..1 ratio into a percentage.
|
||||
const percentScale = 100
|
||||
|
||||
// progressLogEvery is how many processed items pass between progress
|
||||
// log lines in long-running loops.
|
||||
const progressLogEvery = 100
|
||||
|
||||
// ubytes renders a byte count with humanize.Bytes, clamping negative
|
||||
// values to zero so the int64→uint64 conversion cannot overflow.
|
||||
func ubytes(n int64) string {
|
||||
if n < 0 {
|
||||
n = 0
|
||||
}
|
||||
|
||||
return humanize.Bytes(uint64(n))
|
||||
}
|
||||
|
||||
// Sentinel errors for snapshot ID and duration parsing.
|
||||
var (
|
||||
errMalformedSnapshotID = errors.New(
|
||||
"invalid snapshot ID format: expected hostname_snapshotname_timestamp")
|
||||
errInvalidDuration = errors.New("invalid duration")
|
||||
errUnknownTimeUnit = errors.New("unknown time unit")
|
||||
)
|
||||
|
||||
// Time-unit lengths used by parseDuration.
|
||||
const (
|
||||
day = 24 * time.Hour
|
||||
week = 7 * day
|
||||
month = 30 * day
|
||||
year = 365 * day
|
||||
)
|
||||
|
||||
// Snapshot IDs split on "_" into hostname, optional name parts, and a
|
||||
// trailing timestamp.
|
||||
const (
|
||||
minSnapshotIDParts = 2
|
||||
minSnapshotIDNameParts = 3
|
||||
)
|
||||
|
||||
// SnapshotInfo contains information about a snapshot.
|
||||
// UncompressedSize and NewChunkSize are populated only when the snapshot
|
||||
// is present in the local database; LocallyTracked indicates whether
|
||||
// those values are meaningful.
|
||||
//
|
||||
//nolint:tagliatelle // snake_case is the established output format
|
||||
type SnapshotInfo struct {
|
||||
ID types.SnapshotID `json:"id"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
@@ -44,8 +87,8 @@ func formatBytes(bytes int64) string {
|
||||
// Format: hostname_snapshotname_2026-01-12T14:41:15Z
|
||||
func parseSnapshotTimestamp(snapshotID string) (time.Time, error) {
|
||||
parts := strings.Split(snapshotID, "_")
|
||||
if len(parts) < 2 {
|
||||
return time.Time{}, errors.New("invalid snapshot ID format: expected hostname_snapshotname_timestamp")
|
||||
if len(parts) < minSnapshotIDParts {
|
||||
return time.Time{}, errMalformedSnapshotID
|
||||
}
|
||||
|
||||
// Last part is the RFC3339 timestamp
|
||||
@@ -65,7 +108,7 @@ func parseSnapshotTimestamp(snapshotID string) (time.Time, error) {
|
||||
// Returns the snapshot name, or empty string if the ID is malformed.
|
||||
func parseSnapshotName(snapshotID string) string {
|
||||
parts := strings.Split(snapshotID, "_")
|
||||
if len(parts) < 3 {
|
||||
if len(parts) < minSnapshotIDNameParts {
|
||||
// Format: hostname_timestamp — no snapshot name
|
||||
return ""
|
||||
}
|
||||
@@ -88,7 +131,7 @@ func parseDuration(s string) (time.Duration, error) {
|
||||
|
||||
matches := re.FindAllStringSubmatch(s, -1)
|
||||
if len(matches) == 0 {
|
||||
return 0, fmt.Errorf("invalid duration: %q", s)
|
||||
return 0, fmt.Errorf("%w: %q", errInvalidDuration, s)
|
||||
}
|
||||
|
||||
var total time.Duration
|
||||
@@ -102,15 +145,15 @@ func parseDuration(s string) (time.Duration, error) {
|
||||
unit := strings.ToLower(match[2])
|
||||
switch unit {
|
||||
case "d", "day", "days":
|
||||
total += time.Duration(n) * 24 * time.Hour
|
||||
total += time.Duration(n) * day
|
||||
case "w", "week", "weeks":
|
||||
total += time.Duration(n) * 7 * 24 * time.Hour
|
||||
total += time.Duration(n) * week
|
||||
case "mo", "month", "months":
|
||||
total += time.Duration(n) * 30 * 24 * time.Hour
|
||||
total += time.Duration(n) * month
|
||||
case "y", "year", "years":
|
||||
total += time.Duration(n) * 365 * 24 * time.Hour
|
||||
total += time.Duration(n) * year
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown time unit %q", unit)
|
||||
return 0, fmt.Errorf("%w %q", errUnknownTimeUnit, unit)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user