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:
@@ -26,7 +26,7 @@ func (s *Store) AppendAudit(ctx context.Context, e AuditEntry) error {
|
||||
}
|
||||
|
||||
// nullable returns nil for nil/empty *string so SQLite stores NULL.
|
||||
// SQLite's driver treats Go nil as NULL but treats *string("") as ''.
|
||||
// SQLite's driver treats Go nil as NULL but treats *string("") as ”.
|
||||
// We want NULL semantics for "absent."
|
||||
func nullable(p *string) any {
|
||||
if p == nil || *p == "" {
|
||||
|
||||
@@ -172,4 +172,3 @@ func (s *Store) PurgeExpiredEnrollmentTokens(ctx context.Context) (int64, error)
|
||||
n, _ := res.RowsAffected()
|
||||
return n, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ func (s *Store) FleetSummary(ctx context.Context) (FleetSummary, error) {
|
||||
if err != nil {
|
||||
return FleetSummary{}, fmt.Errorf("store: fleet summary jobs: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
defer func() { _ = rows.Close() }()
|
||||
for rows.Next() {
|
||||
var status string
|
||||
var n int
|
||||
@@ -70,7 +70,7 @@ func (s *Store) FleetSummary(ctx context.Context) (FleetSummary, error) {
|
||||
fs.JobsLast24hSucceeded = n
|
||||
case "failed":
|
||||
fs.JobsLast24hFailed = n
|
||||
case "cancelled":
|
||||
case "cancelled": //nolint:misspell // matches the DB CHECK constraint and api.JobCancelled wire value
|
||||
fs.JobsLast24hCancelled = n
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ func (s *Store) ListHosts(ctx context.Context) ([]Host, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("store: list hosts: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
defer func() { _ = rows.Close() }()
|
||||
var out []Host
|
||||
for rows.Next() {
|
||||
h, err := scanHostRow(rows)
|
||||
@@ -150,11 +150,11 @@ func scanHost(row *sql.Row) (*Host, error) {
|
||||
func scanHostRow(s hostScanner) (*Host, error) {
|
||||
var h Host
|
||||
var (
|
||||
lastSeen, lastBackupAt sql.NullString
|
||||
repoID, currentJob, lastBkSt sql.NullString
|
||||
enrolled string
|
||||
tags string
|
||||
bwUp, bwDown sql.NullInt64
|
||||
lastSeen, lastBackupAt sql.NullString
|
||||
repoID, currentJob, lastBkSt sql.NullString
|
||||
enrolled string
|
||||
tags string
|
||||
bwUp, bwDown sql.NullInt64
|
||||
)
|
||||
err := s.Scan(&h.ID, &h.Name, &h.OS, &h.Arch,
|
||||
&h.AgentVersion, &h.ResticVersion, &h.ProtocolVersion,
|
||||
|
||||
+10
-10
@@ -118,7 +118,7 @@ func (s *Store) ListJobLogs(ctx context.Context, jobID string, afterSeq int64, l
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("store: list job logs: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
defer func() { _ = rows.Close() }()
|
||||
var out []JobLogLine
|
||||
for rows.Next() {
|
||||
var l JobLogLine
|
||||
@@ -143,15 +143,15 @@ func (s *Store) GetJob(ctx context.Context, id string) (*Job, error) {
|
||||
started_at, finished_at, exit_code, stats, error, created_at
|
||||
FROM jobs WHERE id = ?`, id)
|
||||
var (
|
||||
j Job
|
||||
schedID sql.NullString
|
||||
actorID sql.NullString
|
||||
startedAt sql.NullString
|
||||
finishedAt sql.NullString
|
||||
exitCode sql.NullInt64
|
||||
stats sql.NullString
|
||||
errMsg sql.NullString
|
||||
createdAt string
|
||||
j Job
|
||||
schedID sql.NullString
|
||||
actorID sql.NullString
|
||||
startedAt sql.NullString
|
||||
finishedAt sql.NullString
|
||||
exitCode sql.NullInt64
|
||||
stats sql.NullString
|
||||
errMsg sql.NullString
|
||||
createdAt string
|
||||
)
|
||||
if err := row.Scan(&j.ID, &j.HostID, &j.Kind, &j.Status, &schedID,
|
||||
&j.ActorKind, &actorID, &startedAt, &finishedAt,
|
||||
|
||||
@@ -31,7 +31,7 @@ func (st *Store) GetRepoMaintenance(ctx context.Context, hostID string) (*HostRe
|
||||
check_cron, check_enabled, check_subset_pct
|
||||
FROM host_repo_maintenance WHERE host_id = ?`, hostID)
|
||||
var (
|
||||
m HostRepoMaintenance
|
||||
m HostRepoMaintenance
|
||||
forgetEnabled, pruneEnabled, checkEnabled int
|
||||
)
|
||||
err := row.Scan(&m.HostID,
|
||||
|
||||
@@ -15,9 +15,9 @@ var migrationsFS embed.FS
|
||||
|
||||
// migration is one ordered SQL file from migrations/.
|
||||
type migration struct {
|
||||
version int // parsed from filename prefix (0001, 0002, …)
|
||||
name string // full filename, for error messages
|
||||
sql string
|
||||
version int // parsed from filename prefix (0001, 0002, …)
|
||||
name string // full filename, for error messages
|
||||
sql string
|
||||
}
|
||||
|
||||
// loadMigrations reads every migrations/*.sql file in lexical order
|
||||
|
||||
@@ -52,7 +52,7 @@ func (st *Store) DuePendingRuns(ctx context.Context, now time.Time, limit int) (
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("store: due pending runs: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
defer func() { _ = rows.Close() }()
|
||||
out := []PendingRun{}
|
||||
for rows.Next() {
|
||||
var p PendingRun
|
||||
|
||||
@@ -144,7 +144,7 @@ func (st *Store) ListSchedulesByHost(ctx context.Context, hostID string) ([]Sche
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("store: list schedules: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
defer func() { _ = rows.Close() }()
|
||||
out := []Schedule{}
|
||||
for rows.Next() {
|
||||
s, err := scanScheduleRow(rows)
|
||||
@@ -247,7 +247,7 @@ func (st *Store) scheduleGroupIDs(ctx context.Context, scheduleID string) ([]str
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("store: read schedule junction: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
defer func() { _ = rows.Close() }()
|
||||
out := []string{}
|
||||
for rows.Next() {
|
||||
var id string
|
||||
@@ -269,7 +269,7 @@ func (st *Store) SchedulesUsingGroup(ctx context.Context, groupID string) ([]str
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("store: schedules using group: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
defer func() { _ = rows.Close() }()
|
||||
out := []string{}
|
||||
for rows.Next() {
|
||||
var id string
|
||||
|
||||
@@ -51,7 +51,7 @@ func (s *Store) ReplaceHostSnapshots(ctx context.Context, hostID string, snaps [
|
||||
if err != nil {
|
||||
return fmt.Errorf("store: prepare snapshot insert: %w", err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
defer func() { _ = stmt.Close() }()
|
||||
|
||||
refreshed := when.UTC().Format(time.RFC3339Nano)
|
||||
for _, snap := range snaps {
|
||||
@@ -92,7 +92,7 @@ func (s *Store) ListSnapshotsByHost(ctx context.Context, hostID string) ([]Snaps
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("store: list snapshots: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
var out []Snapshot
|
||||
for rows.Next() {
|
||||
|
||||
@@ -30,20 +30,20 @@ func TestReplaceHostSnapshotsRoundTrip(t *testing.T) {
|
||||
now := time.Now().UTC().Truncate(time.Second)
|
||||
in := []Snapshot{
|
||||
{
|
||||
ID: "deadbeef" + "00000000000000000000000000000000000000000000000000000000",
|
||||
ShortID: "deadbeef",
|
||||
Time: now.Add(-2 * time.Hour),
|
||||
Hostname: "snap-host",
|
||||
Paths: []string{"/etc", "/home"},
|
||||
Tags: []string{"daily"},
|
||||
ID: "deadbeef" + "00000000000000000000000000000000000000000000000000000000",
|
||||
ShortID: "deadbeef",
|
||||
Time: now.Add(-2 * time.Hour),
|
||||
Hostname: "snap-host",
|
||||
Paths: []string{"/etc", "/home"},
|
||||
Tags: []string{"daily"},
|
||||
SizeBytes: 4096, FileCount: 12,
|
||||
},
|
||||
{
|
||||
ID: "cafef00d" + "00000000000000000000000000000000000000000000000000000000",
|
||||
ShortID: "cafef00d",
|
||||
Time: now.Add(-1 * time.Hour),
|
||||
Hostname: "snap-host",
|
||||
Paths: []string{"/etc"},
|
||||
ID: "cafef00d" + "00000000000000000000000000000000000000000000000000000000",
|
||||
ShortID: "cafef00d",
|
||||
Time: now.Add(-1 * time.Hour),
|
||||
Hostname: "snap-host",
|
||||
Paths: []string{"/etc"},
|
||||
SizeBytes: 8192, FileCount: 24,
|
||||
},
|
||||
}
|
||||
@@ -129,9 +129,11 @@ func TestReplaceHostSnapshotsEmpty(t *testing.T) {
|
||||
|
||||
// First a non-empty replace.
|
||||
if err := s.ReplaceHostSnapshots(ctx, hostID, []Snapshot{
|
||||
{ID: "1111111111111111111111111111111111111111111111111111111111111111",
|
||||
{
|
||||
ID: "1111111111111111111111111111111111111111111111111111111111111111",
|
||||
ShortID: "11111111", Time: time.Now().UTC(), Hostname: "snap-host",
|
||||
Paths: []string{"/x"}},
|
||||
Paths: []string{"/x"},
|
||||
},
|
||||
}, time.Now().UTC()); err != nil {
|
||||
t.Fatalf("replace 1: %v", err)
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ func (st *Store) ListSourceGroupsByHost(ctx context.Context, hostID string) ([]S
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("store: list source groups: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
defer func() { _ = rows.Close() }()
|
||||
out := []SourceGroup{}
|
||||
for rows.Next() {
|
||||
g, err := scanSourceGroupRow(rows)
|
||||
@@ -220,10 +220,10 @@ type sourceGroupScanner interface {
|
||||
|
||||
func scanSourceGroupRow(s sourceGroupScanner) (*SourceGroup, error) {
|
||||
var (
|
||||
out SourceGroup
|
||||
includes, excludes, retention string
|
||||
conflict sql.NullString
|
||||
createdAt, updatedAt string
|
||||
out SourceGroup
|
||||
includes, excludes, retention string
|
||||
conflict sql.NullString
|
||||
createdAt, updatedAt string
|
||||
)
|
||||
err := s.Scan(&out.ID, &out.HostID, &out.Name,
|
||||
&includes, &excludes, &retention,
|
||||
|
||||
@@ -177,7 +177,7 @@ func TestPendingRunQueue(t *testing.T) {
|
||||
|
||||
now := time.Now().UTC()
|
||||
if err := s.EnqueuePendingRun(ctx, &PendingRun{
|
||||
ID: "01HPEND00000000000000001",
|
||||
ID: "01HPEND00000000000000001",
|
||||
ScheduleID: schedID, SourceGroupID: gid, HostID: hostID,
|
||||
NextAttemptAt: now.Add(-time.Second), // already due
|
||||
ScheduledAt: now.Add(-time.Minute),
|
||||
|
||||
@@ -34,10 +34,12 @@ func TestOpenAppliesMigrations(t *testing.T) {
|
||||
}
|
||||
|
||||
// Spot-check a few tables exist with expected columns.
|
||||
tables := []string{"users", "sessions", "hosts", "repos",
|
||||
tables := []string{
|
||||
"users", "sessions", "hosts", "repos",
|
||||
"credentials", "schedules", "jobs", "job_logs",
|
||||
"snapshots", "alerts", "audit_log",
|
||||
"enrollment_tokens", "host_schedule_version"}
|
||||
"enrollment_tokens", "host_schedule_version",
|
||||
}
|
||||
for _, tbl := range tables {
|
||||
row := s.DB().QueryRow(
|
||||
`SELECT name FROM sqlite_master WHERE type='table' AND name = ?`, tbl)
|
||||
|
||||
+15
-14
@@ -20,6 +20,7 @@ type User struct {
|
||||
// Role enumerates the access tiers from spec.md §7.2.
|
||||
type Role string
|
||||
|
||||
// Defined Role values, in descending order of privilege.
|
||||
const (
|
||||
RoleAdmin Role = "admin"
|
||||
RoleOperator Role = "operator"
|
||||
@@ -73,12 +74,12 @@ type Host struct {
|
||||
// only. forget/prune/check are repo-level cadences on
|
||||
// HostRepoMaintenance, not schedule kinds.
|
||||
type Schedule struct {
|
||||
ID string
|
||||
HostID string
|
||||
CronExpr string
|
||||
Enabled bool
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
ID string
|
||||
HostID string
|
||||
CronExpr string
|
||||
Enabled bool
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
// SourceGroupIDs is populated by ListSchedulesByHost (joins
|
||||
// schedule_source_groups) and accepted on Create / Update so the
|
||||
// caller passes the desired junction state in one shape.
|
||||
@@ -160,14 +161,14 @@ type HostRepoMaintenance struct {
|
||||
// PendingRun queues a missed cron tick (agent was offline) for the
|
||||
// server-side retry ticker to dispatch later.
|
||||
type PendingRun struct {
|
||||
ID string
|
||||
ScheduleID string
|
||||
SourceGroupID string
|
||||
HostID string
|
||||
Attempt int
|
||||
NextAttemptAt time.Time
|
||||
ScheduledAt time.Time // original cron tick — forensic / audit
|
||||
LastError string
|
||||
ID string
|
||||
ScheduleID string
|
||||
SourceGroupID string
|
||||
HostID string
|
||||
Attempt int
|
||||
NextAttemptAt time.Time
|
||||
ScheduledAt time.Time // original cron tick — forensic / audit
|
||||
LastError string
|
||||
}
|
||||
|
||||
// EnrollmentToken is the issuer's view of a one-time token.
|
||||
|
||||
Reference in New Issue
Block a user