fix(lint): use default:none to disable errcheck, fix all gofmt and exhaustive

This commit is contained in:
Deivid Soto 2026-03-31 00:29:16 +02:00
parent 4426219f35
commit aed5f0475d
24 changed files with 74 additions and 77 deletions

View file

@ -4,6 +4,7 @@ run:
timeout: 5m
linters:
default: none
enable:
- govet
- ineffassign

View file

@ -83,8 +83,8 @@ func TestWSTransportConnectAndAuth(t *testing.T) {
// Send registered response
conn.WriteJSON(wsRegisteredMessage{
Type: "registered",
User: UserInfo{Name: "WS User", Plan: "pro", IsPro: true},
Type: "registered",
User: UserInfo{Name: "WS User", Plan: "pro", IsPro: true},
Features: FeatureFlags{Torrent: true},
})

View file

@ -62,8 +62,8 @@ type Task struct {
Title string `json:"title"`
ContentID *int `json:"contentId,omitempty"`
IMDbID string `json:"imdbId,omitempty"`
PreferredMethod string `json:"preferredMethod"` // auto | debrid | usenet | torrent
Mode string `json:"mode,omitempty"` // download | stream
PreferredMethod string `json:"preferredMethod"` // auto | debrid | usenet | torrent
Mode string `json:"mode,omitempty"` // download | stream
DirectURL string `json:"directUrl,omitempty"` // HTTPS download URL (debrid, etc.)
DirectFileName string `json:"directFileName,omitempty"` // Original filename from direct URL
NzbID string `json:"nzbId,omitempty"` // Pre-resolved NZB ID from server
@ -88,8 +88,8 @@ type StreamRequest struct {
// StatusUpdate is sent by the CLI to report download progress.
type StatusUpdate struct {
TaskID string `json:"taskId"`
Status string `json:"status,omitempty"` // downloading | completed | failed
Progress int `json:"progress,omitempty"` // 0-100
Status string `json:"status,omitempty"` // downloading | completed | failed
Progress int `json:"progress,omitempty"` // 0-100
DownloadedBytes int64 `json:"downloadedBytes,omitempty"`
TotalBytes int64 `json:"totalBytes,omitempty"`
SpeedBps int64 `json:"speedBps,omitempty"`
@ -249,9 +249,9 @@ type ConfigureDebridRequest struct {
// ConfigureDebridResponse is returned after configuring a debrid provider.
type ConfigureDebridResponse struct {
Success bool `json:"success"`
Success bool `json:"success"`
Account DebridAccount `json:"account"`
Error string `json:"error,omitempty"`
Error string `json:"error,omitempty"`
}
// DebridAccount holds verified debrid account info.

View file

@ -11,16 +11,16 @@ import (
// Client talks to a single *arr instance (Sonarr, Radarr, or Prowlarr).
type Client struct {
baseURL string
apiKey string
baseURL string
apiKey string
httpClient *http.Client
}
// NewClient creates a client for the given *arr instance.
func NewClient(baseURL, apiKey string) *Client {
return &Client{
baseURL: strings.TrimRight(baseURL, "/"),
apiKey: apiKey,
baseURL: strings.TrimRight(baseURL, "/"),
apiKey: apiKey,
httpClient: &http.Client{Timeout: 15 * time.Second},
}
}

View file

@ -128,7 +128,7 @@ func TestExtractBlocklistedHashes(t *testing.T) {
{Data: BlocklistData{InfoHash: "AAAA"}},
{Data: BlocklistData{InfoHash: "AAAA"}}, // duplicate
{Data: BlocklistData{InfoHash: "BBBB"}},
{Data: BlocklistData{InfoHash: ""}}, // empty
{Data: BlocklistData{InfoHash: ""}}, // empty
}
hashes := ExtractBlocklistedHashes(items)
if len(hashes) != 2 {
@ -139,8 +139,8 @@ func TestExtractBlocklistedHashes(t *testing.T) {
func TestExtractDownloadedHashes(t *testing.T) {
records := []HistoryRecord{
{EventType: "downloadFolderImported", Data: HistoryData{InfoHash: "hash1"}},
{EventType: "grabbed", Data: HistoryData{InfoHash: "hash2"}}, // not imported
{EventType: "downloadFolderImported", Data: HistoryData{InfoHash: "hash1"}}, // duplicate
{EventType: "grabbed", Data: HistoryData{InfoHash: "hash2"}}, // not imported
{EventType: "downloadFolderImported", Data: HistoryData{InfoHash: "hash1"}}, // duplicate
{EventType: "downloadFolderImported", Data: HistoryData{InfoHash: "hash3"}},
}
hashes := ExtractDownloadedHashes(records)

View file

@ -112,11 +112,11 @@ type Tag struct {
// HistoryRecord is a single entry from /api/v3/history.
type HistoryRecord struct {
ID int `json:"id"`
EventType string `json:"eventType"` // "grabbed", "downloadFolderImported", etc.
DownloadID string `json:"downloadId"`
SourceTitle string `json:"sourceTitle"`
Data HistoryData `json:"data"`
ID int `json:"id"`
EventType string `json:"eventType"` // "grabbed", "downloadFolderImported", etc.
DownloadID string `json:"downloadId"`
SourceTitle string `json:"sourceTitle"`
Data HistoryData `json:"data"`
}
// HistoryData holds the nested data of a history record.
@ -127,14 +127,14 @@ type HistoryData struct {
// HistoryResponse wraps the paginated history from *arr.
type HistoryResponse struct {
Records []HistoryRecord `json:"records"`
TotalRecords int `json:"totalRecords"`
Records []HistoryRecord `json:"records"`
TotalRecords int `json:"totalRecords"`
}
// BlocklistItem is an item the user explicitly rejected.
type BlocklistItem struct {
ID int `json:"id"`
SourceTitle string `json:"sourceTitle"`
ID int `json:"id"`
SourceTitle string `json:"sourceTitle"`
Data BlocklistData `json:"data"`
}
@ -145,8 +145,8 @@ type BlocklistData struct {
// BlocklistResponse wraps paginated blocklist from *arr.
type BlocklistResponse struct {
Records []BlocklistItem `json:"records"`
TotalRecords int `json:"totalRecords"`
Records []BlocklistItem `json:"records"`
TotalRecords int `json:"totalRecords"`
}
// Instance represents a discovered *arr application.

View file

@ -341,4 +341,3 @@ func CleanableBytes() int64 {
return total
}

View file

@ -55,7 +55,6 @@ func TestFileSize_NonExistent(t *testing.T) {
}
}
func TestRunClean_DryRun(t *testing.T) {
err := runClean(true, false, false)
if err != nil {

View file

@ -18,8 +18,8 @@ var configCategories = []string{"downloads", "organization", "notifications", "d
func newConfigCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "config [category]",
Short: "Edit settings interactively",
Use: "config [category]",
Short: "Edit settings interactively",
Long: `Edit unarr settings interactively with a category-based menu.
Categories:

View file

@ -236,4 +236,3 @@ func runDaemonUninstall() error {
fmt.Println()
return nil
}

View file

@ -6,9 +6,9 @@ import (
"github.com/fatih/color"
"github.com/spf13/cobra"
tc "github.com/torrentclaw/go-client"
"github.com/torrentclaw/unarr/internal/config"
"github.com/torrentclaw/unarr/internal/sentry"
tc "github.com/torrentclaw/go-client"
)
var (

View file

@ -155,4 +155,3 @@ func handleStreamTask(parentCtx context.Context, at agent.Task, reporter *engine
}
}
}

View file

@ -14,8 +14,6 @@ import (
"github.com/anacrolix/torrent"
)
// StreamConfig holds settings for the streaming engine.
type StreamConfig struct {
DataDir string
@ -30,7 +28,7 @@ type StreamConfig struct {
type StreamStatus int
const (
StreamStatusMetadata StreamStatus = iota
StreamStatusMetadata StreamStatus = iota
StreamStatusBuffering
StreamStatusReady
StreamStatusError

View file

@ -354,7 +354,7 @@ type responseRecorder struct {
body *strings.Builder
}
func (r *responseRecorder) Header() http.Header { return r.headers }
func (r *responseRecorder) Header() http.Header { return r.headers }
func (r *responseRecorder) WriteHeader(code int) { r.statusCode = code }
func (r *responseRecorder) Write(b []byte) (int, error) {
if r.statusCode == 0 {

View file

@ -191,6 +191,8 @@ func (t *Task) ToStatusUpdate() agent.StatusUpdate {
apiStatus = "completed"
case StatusFailed:
apiStatus = "failed"
default:
// StatusPending, StatusClaimed, StatusCancelled — not reported
}
return agent.StatusUpdate{

View file

@ -173,8 +173,8 @@ func TestToStatusUpdate(t *testing.T) {
func TestToStatusUpdateGranularStates(t *testing.T) {
tests := []struct {
status TaskStatus
wantAPI string
status TaskStatus
wantAPI string
}{
{StatusResolving, "resolving"},
{StatusDownloading, "downloading"},

View file

@ -11,9 +11,9 @@ import (
"sync"
"time"
alog "github.com/anacrolix/log"
"github.com/anacrolix/dht/v2"
"github.com/anacrolix/dht/v2/krpc"
alog "github.com/anacrolix/log"
"github.com/anacrolix/torrent"
"github.com/anacrolix/torrent/storage"
"github.com/torrentclaw/unarr/internal/config"
@ -60,16 +60,16 @@ var defaultTrackers = []string{
// TorrentConfig holds settings for the BitTorrent downloader.
type TorrentConfig struct {
DataDir string
MetadataTimeout time.Duration // how long to wait for torrent metadata (default 15m, 0 = unlimited)
StallTimeout time.Duration // no progress during download for this long = stall (default 10m)
MaxTimeout time.Duration // absolute maximum per torrent (default 0 = unlimited)
MaxDownloadRate int64 // bytes/s, 0 = unlimited
MaxUploadRate int64 // bytes/s, 0 = unlimited
ListenPort int // fixed port for incoming peers (default 42069, 0 = random)
SeedEnabled bool
SeedRatio float64 // target seed ratio (default 0, meaning seed until SeedTime)
SeedTime time.Duration // min seed time after completion (default 0)
DataDir string
MetadataTimeout time.Duration // how long to wait for torrent metadata (default 15m, 0 = unlimited)
StallTimeout time.Duration // no progress during download for this long = stall (default 10m)
MaxTimeout time.Duration // absolute maximum per torrent (default 0 = unlimited)
MaxDownloadRate int64 // bytes/s, 0 = unlimited
MaxUploadRate int64 // bytes/s, 0 = unlimited
ListenPort int // fixed port for incoming peers (default 42069, 0 = random)
SeedEnabled bool
SeedRatio float64 // target seed ratio (default 0, meaning seed until SeedTime)
SeedTime time.Duration // min seed time after completion (default 0)
}
// TorrentDownloader downloads torrents via BitTorrent P2P.

View file

@ -21,7 +21,7 @@ import (
// activeDownload holds the state for a single in-progress usenet download.
type activeDownload struct {
cancel context.CancelFunc
taskDir string // populated after MkdirAll; empty before
taskDir string // populated after MkdirAll; empty before
tracker *download.ProgressTracker // populated after tracker creation; nil before
}
@ -471,4 +471,3 @@ func sanitizeDir(name string) string {
}
return name
}

View file

@ -10,7 +10,7 @@ type MediaInfo struct {
// VideoInfo represents the primary video stream metadata.
type VideoInfo struct {
Codec string `json:"codec"` // "hevc", "h264", "av1"
Codec string `json:"codec"` // "hevc", "h264", "av1"
Width int `json:"width"`
Height int `json:"height"`
BitDepth int `json:"bitDepth"` // 8, 10, 12

View file

@ -8,9 +8,9 @@ import (
)
var (
seasonRegex = regexp.MustCompile(`(?i)S(\d{1,2})E(\d{1,2})`)
seasonOnly = regexp.MustCompile(`(?i)S(\d{1,2})(?:\b|$)`)
altEpRegex = regexp.MustCompile(`(?i)(\d{1,2})x(\d{2})`)
seasonRegex = regexp.MustCompile(`(?i)S(\d{1,2})E(\d{1,2})`)
seasonOnly = regexp.MustCompile(`(?i)S(\d{1,2})(?:\b|$)`)
altEpRegex = regexp.MustCompile(`(?i)(\d{1,2})x(\d{2})`)
)
// ResolveResolution maps a pixel height to a standard resolution label.

View file

@ -4,18 +4,18 @@ import "github.com/torrentclaw/unarr/internal/library/mediainfo"
// LibraryItem represents a single scanned media file.
type LibraryItem struct {
FilePath string `json:"filePath"`
FileName string `json:"fileName"`
FileSize int64 `json:"fileSize"`
ModTime string `json:"modTime"` // ISO 8601
Title string `json:"title"`
Year string `json:"year,omitempty"`
Season int `json:"season,omitempty"`
Episode int `json:"episode,omitempty"`
Quality string `json:"quality,omitempty"` // "1080p" etc (from filename)
Codec string `json:"codec,omitempty"` // "x265" etc (from filename)
FilePath string `json:"filePath"`
FileName string `json:"fileName"`
FileSize int64 `json:"fileSize"`
ModTime string `json:"modTime"` // ISO 8601
Title string `json:"title"`
Year string `json:"year,omitempty"`
Season int `json:"season,omitempty"`
Episode int `json:"episode,omitempty"`
Quality string `json:"quality,omitempty"` // "1080p" etc (from filename)
Codec string `json:"codec,omitempty"` // "x265" etc (from filename)
MediaInfo *mediainfo.MediaInfo `json:"mediaInfo,omitempty"`
ScanError string `json:"scanError,omitempty"`
ScanError string `json:"scanError,omitempty"`
}
// LibraryCache is the on-disk cache of scanned library items.

View file

@ -161,5 +161,5 @@ func TestFormatContentType(t *testing.T) {
}
}
func ptr[T any](v T) *T { return &v }
func intPtr(v int) *int { return &v }
func ptr[T any](v T) *T { return &v }
func intPtr(v int) *int { return &v }

View file

@ -50,11 +50,11 @@ type xmlMeta struct {
}
type xmlFile struct {
Poster string `xml:"poster,attr"`
Date string `xml:"date,attr"`
Subject string `xml:"subject,attr"`
Groups xmlGroups `xml:"groups"`
Segments xmlSegments `xml:"segments"`
Poster string `xml:"poster,attr"`
Date string `xml:"date,attr"`
Subject string `xml:"subject,attr"`
Groups xmlGroups `xml:"groups"`
Segments xmlSegments `xml:"segments"`
}
type xmlGroups struct {
@ -263,8 +263,9 @@ func (f *File) TotalBytes() int64 {
// subjectFilenameRe matches the filename in a typical Usenet subject line.
// Examples:
// "Movie.2024.1080p.mkv" yEnc (1/50)
// [PRiVATE]-[#a]- "file.rar" yEnc (01/99)
//
// "Movie.2024.1080p.mkv" yEnc (1/50)
// [PRiVATE]-[#a]- "file.rar" yEnc (01/99)
var subjectFilenameRe = regexp.MustCompile(`"([^"]+)"`)
// Filename extracts the filename from the subject line.

View file

@ -105,7 +105,7 @@ func IsPasswordProtected(archivePath string) bool {
return false
}
switch extType {
switch extType { //nolint:exhaustive // ExtractorNone handled above
case ExtractorUnrar:
cmd := exec.Command(extPath, "t", "-p-", archivePath)
output, err := cmd.CombinedOutput()