Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
run:
timeout: 5m

linters:
enable:
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- unused
- gocritic
- prealloc
- unconvert
- whitespace
- nolintlint
disable:
- exhaustruct
- forcetypeassert
- gochecknoglobals
- gochecknoinits

linters-settings:
errcheck:
check-type-assertions: true
check-blank: false
gocritic:
enabled-tags:
- performance
- diagnostic
- experimental
disabled-checks:
- paramTypeCombine
staticcheck:
checks:
- all
- -SA1019
prealloc:
simple: true
range-loops: true
for-loops: true

issues:
exclude-rules:
- path: _test\.go
linters:
- errcheck
- gocritic
- path: internal/colors/colors.go
text: "WhyNoLint"
linters:
- staticcheck
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: all build build-all clean install test help setup setup-install tidy
.PHONY: all build build-all clean install test help setup setup-install fmt tidy

# Installation parameters
prefix?=/usr/local
Expand Down Expand Up @@ -34,8 +34,13 @@ help:
@echo " make setup-install - Install setup binary to $(BUILD_DIR)"
@echo " make clean - Remove build artifacts"
@echo " make test - Run tests"
@echo " make fmt - Format Go source files"
@echo " make tidy - Tidy go.mod"

fmt:
@gofmt -w .
@echo "Formatted all Go source files"

build:
@echo "Building binaries..."
@mkdir -p $(BUILD_DIR)
Expand Down
9 changes: 8 additions & 1 deletion cmd/arr-feed/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"flag"
"fmt"
"os"
"strings"

"github.com/calmcacil/CalmsToolkit/internal/colors"
"github.com/calmcacil/CalmsToolkit/internal/config"
"github.com/calmcacil/CalmsToolkit/internal/feed"
)
Expand Down Expand Up @@ -38,7 +40,12 @@ func main() {
cfg.Timeout = *timeout
cfg.NoColor = *noColor || *json
cfg.Theme = *theme
cfg.JSON = *json
if cfg.Theme != "" && !colors.ValidateTheme(cfg.Theme) {
fmt.Fprintf(os.Stderr, "Warning: unknown theme %q, falling back to default (valid: %s)\n",
cfg.Theme, strings.Join(colors.ValidThemes(), ", "))
cfg.Theme = "default"
}
cfg.JSONOutput = *json
cfg.Watch = *watch
cfg.ShowGrabbed = *showGrabbed
cfg.ShowImported = *showImported
Expand Down
17 changes: 17 additions & 0 deletions cmd/arr-feed/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

import (
"testing"

"github.com/calmcacil/CalmsToolkit/internal/config"
)

func TestConfigDefaults(t *testing.T) {
cfg := config.DefaultToolkitConfig()
if cfg == nil {
t.Fatal("DefaultToolkitConfig() returned nil")
}
if cfg.ArrFeed.MaxEvents != 50 {
t.Errorf("MaxEvents = %d, want %d", cfg.ArrFeed.MaxEvents, 50)
}
}
2 changes: 1 addition & 1 deletion cmd/calmstoolkit-setup/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func main() {

path := ConfigPath()
if path == "" {
fmt.Println("ERROR: Cannot determine home directory")
fmt.Fprintln(os.Stderr, "ERROR: Cannot determine home directory")
os.Exit(1)
}

Expand Down
21 changes: 21 additions & 0 deletions cmd/calmstoolkit-setup/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import (
"os"
"testing"

"github.com/calmcacil/CalmsToolkit/internal/config"
)

func TestConfigPath(t *testing.T) {
origHome := os.Getenv("HOME")
defer os.Setenv("HOME", origHome)

dir := t.TempDir()
os.Setenv("HOME", dir)

path := config.ConfigPath()
if path == "" {
t.Fatal("ConfigPath() returned empty")
}
}
6 changes: 6 additions & 0 deletions cmd/media-calendar/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"

"github.com/calmcacil/CalmsToolkit/internal/calendar"
"github.com/calmcacil/CalmsToolkit/internal/colors"
"github.com/calmcacil/CalmsToolkit/internal/config"
)

Expand Down Expand Up @@ -37,6 +38,11 @@ func main() {
cfg.Timeout = *timeout
cfg.NoColor = *noColor || *jsonOutput
cfg.Theme = *theme
if cfg.Theme != "" && !colors.ValidateTheme(cfg.Theme) {
fmt.Fprintf(os.Stderr, "Warning: unknown theme %q, falling back to default (valid: %s)\n",
cfg.Theme, strings.Join(colors.ValidThemes(), ", "))
cfg.Theme = "default"
}
cfg.JSONOutput = *jsonOutput
cfg.WatchMode = *watchMode
cfg.WatchSeconds = *watchSeconds
Expand Down
17 changes: 17 additions & 0 deletions cmd/media-calendar/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

import (
"testing"

"github.com/calmcacil/CalmsToolkit/internal/config"
)

func TestConfigDefaults(t *testing.T) {
cfg := config.DefaultToolkitConfig()
if cfg == nil {
t.Fatal("DefaultToolkitConfig() returned nil")
}
if cfg.MediaCalendar.Days != 1 {
t.Errorf("Days = %d, want %d", cfg.MediaCalendar.Days, 1)
}
}
8 changes: 7 additions & 1 deletion cmd/media-requests/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"
"time"

"github.com/calmcacil/CalmsToolkit/internal/colors"
"github.com/calmcacil/CalmsToolkit/internal/config"
"github.com/calmcacil/CalmsToolkit/internal/requests"
)
Expand All @@ -19,7 +20,7 @@ func main() {
cfg := requests.BuildToolConfig(tk)

url := flag.String("url", cfg.ServerURL, "Overseerr/Jellyseerr server URL")
token := flag.String("token", cfg.APIKey, "API key/token")
token := flag.String("token", config.TokenFromEnv("OVERSEERR_API_KEY", cfg.APIKey), "API key/token")
timeout := flag.Duration("timeout", cfg.Timeout, "Connection timeout")
theme := flag.String("theme", cfg.Theme, "Color theme (default, catppuccin-mocha, catppuccin-latte)")
noColor := flag.Bool("no-color", cfg.NoColor, "Disable colored output")
Expand All @@ -33,6 +34,11 @@ func main() {
cfg.Timeout = *timeout
cfg.NoColor = *noColor || *jsonOutput
cfg.Theme = *theme
if cfg.Theme != "" && !colors.ValidateTheme(cfg.Theme) {
fmt.Fprintf(os.Stderr, "Warning: unknown theme %q, falling back to default (valid: %s)\n",
cfg.Theme, strings.Join(colors.ValidThemes(), ", "))
cfg.Theme = "default"
}
cfg.Verbose = *verbose
cfg.JSONOutput = *jsonOutput
cfg.Quiet = *quiet
Expand Down
21 changes: 21 additions & 0 deletions cmd/media-requests/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import (
"os"
"testing"

"github.com/calmcacil/CalmsToolkit/internal/config"
)

func TestConfigPath(t *testing.T) {
origHome := os.Getenv("HOME")
defer os.Setenv("HOME", origHome)

dir := t.TempDir()
os.Setenv("HOME", dir)

path := config.ConfigPath()
if path == "" {
t.Fatal("ConfigPath() returned empty")
}
}
10 changes: 8 additions & 2 deletions cmd/media-streams/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"strings"

"github.com/calmcacil/CalmsToolkit/internal/colors"
"github.com/calmcacil/CalmsToolkit/internal/config"
"github.com/calmcacil/CalmsToolkit/internal/streams"
)
Expand All @@ -19,9 +20,9 @@ func main() {

server := flag.String("server", cfg.ServerType, "Server type: plex, jellyfin, or both")
plexURL := flag.String("plex-url", cfg.PlexURL, "Plex server URL")
plexToken := flag.String("plex-token", cfg.PlexToken, "Plex authentication token")
plexToken := flag.String("plex-token", config.TokenFromEnv("PLEX_TOKEN", cfg.PlexToken), "Plex authentication token")
jellyfinURL := flag.String("jellyfin-url", cfg.JellyfinURL, "Jellyfin server URL")
jellyfinToken := flag.String("jellyfin-token", cfg.JellyfinToken, "Jellyfin API token")
jellyfinToken := flag.String("jellyfin-token", config.TokenFromEnv("JELLYFIN_TOKEN", cfg.JellyfinToken), "Jellyfin API token")
timeout := flag.Duration("timeout", cfg.Timeout, "Connection timeout")
theme := flag.String("theme", cfg.Theme, "Color theme (default, catppuccin-mocha, catppuccin-latte)")
noColor := flag.Bool("no-color", cfg.NoColor, "Disable colored output")
Expand All @@ -40,6 +41,11 @@ func main() {
cfg.Timeout = *timeout
cfg.NoColor = *noColor || *jsonOutput
cfg.Theme = *theme
if cfg.Theme != "" && !colors.ValidateTheme(cfg.Theme) {
fmt.Fprintf(os.Stderr, "Warning: unknown theme %q, falling back to default (valid: %s)\n",
cfg.Theme, strings.Join(colors.ValidThemes(), ", "))
cfg.Theme = "default"
}
cfg.JSONOutput = *jsonOutput
cfg.WatchMode = *watchMode
cfg.WatchSeconds = *watchSeconds
Expand Down
17 changes: 17 additions & 0 deletions cmd/media-streams/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

import (
"testing"

"github.com/calmcacil/CalmsToolkit/internal/config"
)

func TestConfigDefaults(t *testing.T) {
cfg := config.DefaultToolkitConfig()
if cfg == nil {
t.Fatal("DefaultToolkitConfig() returned nil")
}
if cfg.MediaStreams.ServerType != "both" {
t.Errorf("ServerType = %q, want %q", cfg.MediaStreams.ServerType, "both")
}
}
14 changes: 7 additions & 7 deletions internal/calendar/calendar.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

"github.com/calmcacil/CalmsToolkit/internal/colors"
"github.com/calmcacil/CalmsToolkit/internal/config"
httpclient "github.com/calmcacil/CalmsToolkit/internal/http"
"github.com/calmcacil/CalmsToolkit/internal/httputil"
)

// SonarrEpisode represents a Sonarr calendar episode.
Expand Down Expand Up @@ -239,7 +239,7 @@ func Run(cfg ToolConfig) {
func aggregateCalendar(ctx context.Context, cfg ToolConfig) ([]CalendarItem, []QueueIssue, error) {
start, end := calculateDateRange(cfg.Days, cfg.DaysPast)

client := httpclient.NewTransportClient(cfg.Timeout)
client := httputil.NewTransportClient(cfg.Timeout)

items := make([]CalendarItem, 0)
queueIssues := make([]QueueIssue, 0)
Expand Down Expand Up @@ -295,7 +295,7 @@ func aggregateCalendar(ctx context.Context, cfg ToolConfig) ([]CalendarItem, []Q
return items, queueIssues, nil
}

func fetchSonarrInstance(ctx context.Context, client *httpclient.Client, inst config.ArrInstance, start, end time.Time, debug bool, mu, qMu *sync.Mutex, items *[]CalendarItem, queueIssues *[]QueueIssue, seen map[string]bool) error {
func fetchSonarrInstance(ctx context.Context, client *httputil.Client, inst config.ArrInstance, start, end time.Time, debug bool, mu, qMu *sync.Mutex, items *[]CalendarItem, queueIssues *[]QueueIssue, seen map[string]bool) error {
episodes, err := fetchSonarrCalendar(ctx, client, inst, start, end, debug)
if err != nil {
return fmt.Errorf("Sonarr %s: %w", inst.Name, err)
Expand Down Expand Up @@ -356,7 +356,7 @@ func fetchSonarrInstance(ctx context.Context, client *httpclient.Client, inst co
return nil
}

func fetchRadarrInstance(ctx context.Context, client *httpclient.Client, inst config.ArrInstance, start, end time.Time, debug bool, mu, qMu *sync.Mutex, items *[]CalendarItem, queueIssues *[]QueueIssue, seen map[string]bool) error {
func fetchRadarrInstance(ctx context.Context, client *httputil.Client, inst config.ArrInstance, start, end time.Time, debug bool, mu, qMu *sync.Mutex, items *[]CalendarItem, queueIssues *[]QueueIssue, seen map[string]bool) error {
movies, err := fetchRadarrCalendar(ctx, client, inst, start, end, debug)
if err != nil {
return fmt.Errorf("Radarr %s: %w", inst.Name, err)
Expand Down Expand Up @@ -428,7 +428,7 @@ func fetchRadarrInstance(ctx context.Context, client *httpclient.Client, inst co
return nil
}

func fetchSonarrCalendar(ctx context.Context, client *httpclient.Client, inst config.ArrInstance, start, end time.Time, debug bool) ([]SonarrEpisode, error) {
func fetchSonarrCalendar(ctx context.Context, client *httputil.Client, inst config.ArrInstance, start, end time.Time, debug bool) ([]SonarrEpisode, error) {
apiURL := fmt.Sprintf("%s/api/v3/calendar?start=%s&end=%s&includeSeries=true",
inst.URL, start.Format("2006-01-02"), end.Format("2006-01-02"))

Expand All @@ -444,7 +444,7 @@ func fetchSonarrCalendar(ctx context.Context, client *httpclient.Client, inst co
return episodes, nil
}

func fetchRadarrCalendar(ctx context.Context, client *httpclient.Client, inst config.ArrInstance, start, end time.Time, debug bool) ([]RadarrMovie, error) {
func fetchRadarrCalendar(ctx context.Context, client *httputil.Client, inst config.ArrInstance, start, end time.Time, debug bool) ([]RadarrMovie, error) {
apiURL := fmt.Sprintf("%s/api/v3/calendar?start=%s&end=%s",
inst.URL, start.Format("2006-01-02"), end.Format("2006-01-02"))

Expand All @@ -460,7 +460,7 @@ func fetchRadarrCalendar(ctx context.Context, client *httpclient.Client, inst co
return movies, nil
}

func fetchQueue(ctx context.Context, client *httpclient.Client, inst config.ArrInstance, debug bool) (*QueueResponse, error) {
func fetchQueue(ctx context.Context, client *httputil.Client, inst config.ArrInstance, debug bool) (*QueueResponse, error) {
apiURL := fmt.Sprintf("%s/api/v3/queue?pageSize=100", inst.URL)

if debug {
Expand Down
8 changes: 4 additions & 4 deletions internal/calendar/calendar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

"github.com/calmcacil/CalmsToolkit/internal/colors"
"github.com/calmcacil/CalmsToolkit/internal/config"
httpclient "github.com/calmcacil/CalmsToolkit/internal/http"
"github.com/calmcacil/CalmsToolkit/internal/httputil"
)

func TestFetchSonarrCalendar(t *testing.T) {
Expand Down Expand Up @@ -40,7 +40,7 @@ func TestFetchSonarrCalendar(t *testing.T) {
}))
defer server.Close()

client := httpclient.NewClient(5 * time.Second)
client := httputil.NewClient(5 * time.Second)
inst := config.ArrInstance{Name: "Test", URL: server.URL, APIKey: "test-token"}
start := time.Now()
end := start.Add(7 * 24 * time.Hour)
Expand Down Expand Up @@ -91,7 +91,7 @@ func TestFetchRadarrCalendar(t *testing.T) {
}))
defer server.Close()

client := httpclient.NewClient(5 * time.Second)
client := httputil.NewClient(5 * time.Second)
inst := config.ArrInstance{Name: "Test", URL: server.URL, APIKey: "test-token"}
start := time.Now()
end := start.Add(7 * 24 * time.Hour)
Expand Down Expand Up @@ -152,7 +152,7 @@ func TestFetchQueue(t *testing.T) {
}))
defer server.Close()

client := httpclient.NewClient(5 * time.Second)
client := httputil.NewClient(5 * time.Second)
inst := config.ArrInstance{Name: "Test", URL: server.URL, APIKey: "test-token"}
ctx := context.Background()

Expand Down
Loading
Loading