lint: drive baseline to zero, drop only-new-issues gate
Cleanup pass over the repo so CI can enforce lint going forward
without the only-new-issues escape hatch:
* gofumpt -w across the tree (31 hits, all formatting)
* misspell --fix (25 hits, US-locale spelling) — but reverted on
api.JobCancelled = "cancelled" since that literal is the wire +
DB CHECK constraint value, plus matched the case in store/fleet.go
back to "cancelled" and added //nolint:misspell on both for the
next time someone reaches for the auto-fix
* Wrap every `defer rows.Close()` / `defer stmt.Close()` /
`defer res.Body.Close()` in `defer func() { _ = .Close() }()`
to satisfy errcheck without losing the close itself
* websocket.Dial callers (1 prod, 4 tests) now capture + close the
upgrade response Body — coder/websocket can return res with a nil
Body on success, so the test deferred-closes guard against that
* Annotate the two genuine-by-design nilerr cases with //nolint
comments explaining why nil-on-error is the contract (cookie
missing = no session; ctx cancelled mid-backoff = clean shutdown)
* Add brief godoc on the 10 exported const groups + types that
revive flagged (api.HostOS/HostArch/JobKind/JobStatus/LogStream/
ErrorCode, restic.EventKind, store.Role, web.FS)
* Drop the unused (*Server).userByID method
* Inline the unparam baseView(active) — every UI page is under
the dashboard primary nav today
Result: `golangci-lint run ./...` reports 0 issues. CI lint job
no longer needs only-new-issues: true; X-06 follow-up entry in
tasks.md removed.
This commit is contained in:
@@ -99,13 +99,13 @@ func (r *Runner) RunBackup(ctx context.Context, jobID string, paths, excludes, t
|
||||
}
|
||||
lastProgress = time.Now()
|
||||
progEnv, _ := api.Marshal(api.MsgJobProgress, jobID, api.JobProgressPayload{
|
||||
JobID: jobID,
|
||||
PercentDone: status.PercentDone,
|
||||
FilesDone: status.FilesDone,
|
||||
TotalFiles: status.TotalFiles,
|
||||
BytesDone: status.BytesDone,
|
||||
TotalBytes: status.TotalBytes,
|
||||
ETASeconds: status.SecondsRem,
|
||||
JobID: jobID,
|
||||
PercentDone: status.PercentDone,
|
||||
FilesDone: status.FilesDone,
|
||||
TotalFiles: status.TotalFiles,
|
||||
BytesDone: status.BytesDone,
|
||||
TotalBytes: status.TotalBytes,
|
||||
ETASeconds: status.SecondsRem,
|
||||
ThroughputBps: throughput(status.BytesDone, status.SecondsElapsed),
|
||||
})
|
||||
_ = r.tx.Send(progEnv)
|
||||
|
||||
@@ -110,7 +110,7 @@ func (s *Scheduler) Apply(payload api.ScheduleSetPayload, tx Sender) {
|
||||
"received", len(payload.Schedules), "active", added)
|
||||
|
||||
// Ack outside the lock — Send() shouldn't take long, but holding
|
||||
// s.mu across an external call would needlessly serialise other
|
||||
// s.mu across an external call would needlessly serialize other
|
||||
// callers (e.g. a future Status() inspection from the UI).
|
||||
ackEnv, err := api.Marshal(api.MsgScheduleAck, "", api.ScheduleAckPayload{
|
||||
Version: payload.Version,
|
||||
@@ -167,4 +167,3 @@ func (s *Scheduler) fire(entry api.Schedule) {
|
||||
"schedule_id", entry.ID, "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
|
||||
// additionalData binds ciphertexts to the agent-secrets context, so a
|
||||
// blob lifted from one role's file can't be replayed into another's
|
||||
// row in some unrelated table that uses the same key. (Defence in
|
||||
// row in some unrelated table that uses the same key. (Defense in
|
||||
// depth — the key is per-host today, but cheap to be careful.)
|
||||
const additionalData = "rm-agent-repo-creds-v1"
|
||||
|
||||
|
||||
@@ -48,7 +48,9 @@ func Collect(ctx context.Context, resticPath string) (Snapshot, error) {
|
||||
|
||||
// detectResticVersion runs `restic version` and parses the first line.
|
||||
// Output looks like:
|
||||
// restic 0.17.1 compiled with go1.22.5 on linux/amd64
|
||||
//
|
||||
// restic 0.17.1 compiled with go1.22.5 on linux/amd64
|
||||
//
|
||||
// Returns the version token (e.g. "0.17.1") or "" if restic isn't
|
||||
// found. We never block startup on a missing restic — the operator
|
||||
// might not have installed it yet, and the agent should still be
|
||||
@@ -74,5 +76,5 @@ func detectResticVersion(ctx context.Context, override string) (string, error) {
|
||||
if len(parts) >= 2 && parts[0] == "restic" {
|
||||
return parts[1], nil
|
||||
}
|
||||
return "", fmt.Errorf("sysinfo: unrecognised restic version output: %q", first)
|
||||
return "", fmt.Errorf("sysinfo: unrecognized restic version output: %q", first)
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ type Config struct {
|
||||
// Sender is what handlers use to push agent → server messages
|
||||
// (job.progress, job.finished, log.stream, command.result, …).
|
||||
// Returned by the WS client to the dispatch handler. Write operations
|
||||
// serialise behind a single mutex on the conn; concurrent calls are
|
||||
// serialize behind a single mutex on the conn; concurrent calls are
|
||||
// safe.
|
||||
type Sender interface {
|
||||
Send(env api.Envelope) error
|
||||
@@ -52,7 +52,7 @@ type Sender interface {
|
||||
type Handler func(ctx context.Context, env api.Envelope, tx Sender) error
|
||||
|
||||
// Run keeps the agent connected indefinitely. Returns when ctx is
|
||||
// cancelled. Errors during a single connection attempt are logged and
|
||||
// canceled. Errors during a single connection attempt are logged and
|
||||
// trigger reconnect-with-backoff; only ctx.Done() ends the loop.
|
||||
func Run(ctx context.Context, cfg Config, handle Handler) error {
|
||||
if cfg.HeartbeatPeriod <= 0 {
|
||||
@@ -69,7 +69,10 @@ func Run(ctx context.Context, cfg Config, handle Handler) error {
|
||||
slog.Warn("ws agent disconnect", "err", err)
|
||||
}
|
||||
if err := sleepCtx(ctx, backoff.next()); err != nil {
|
||||
return nil
|
||||
// ctx cancellation mid-backoff means the parent shut us down —
|
||||
// exit the reconnect loop quietly rather than propagating
|
||||
// a context error up to a caller that will discard it.
|
||||
return nil //nolint:nilerr
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,11 +103,15 @@ func connectOnce(ctx context.Context, cfg Config, handle Handler) error {
|
||||
}
|
||||
|
||||
dialCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
||||
conn, _, err := websocket.Dial(dialCtx, wsURL, dialOpts)
|
||||
conn, res, err := websocket.Dial(dialCtx, wsURL, dialOpts)
|
||||
cancel()
|
||||
if err != nil {
|
||||
return fmt.Errorf("dial: %w", err)
|
||||
}
|
||||
// websocket.Dial returns the upgrade response separately from the
|
||||
// conn. Body is empty on a successful upgrade but Go's net/http
|
||||
// still expects it closed to release the connection.
|
||||
defer func() { _ = res.Body.Close() }()
|
||||
defer conn.CloseNow() //nolint:errcheck
|
||||
|
||||
// Send hello.
|
||||
|
||||
@@ -50,7 +50,7 @@ func Enroll(ctx context.Context, serverURL string, req EnrollRequest) (*EnrollRe
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("agent enroll: post: %w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
defer func() { _ = res.Body.Close() }()
|
||||
rawRes, _ := io.ReadAll(res.Body)
|
||||
if res.StatusCode != stdhttp.StatusCreated {
|
||||
return nil, fmt.Errorf("agent enroll: server returned %d: %s",
|
||||
|
||||
Reference in New Issue
Block a user