more lint fixes. enable autofix

This commit is contained in:
Evan Jarrett
2026-01-06 23:56:17 -06:00
parent 56dd522218
commit d4b88b5105
12 changed files with 178 additions and 38 deletions
+9 -2
View File
@@ -1,6 +1,10 @@
# golangci-lint configuration for ATCR
# See: https://golangci-lint.run/usage/configuration/
version: "2"
issues:
fix: true
linters:
settings:
staticcheck:
@@ -25,9 +29,12 @@ linters:
linters:
- errcheck
# TODO: fix issues and remove these paths one by one
formatters:
enable:
- gofmt
- goimports
settings:
gofmt:
rewrite-rules:
- pattern: 'interface{}'
replacement: 'any'
+24 -7
View File
@@ -11,6 +11,7 @@ import (
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
)
@@ -385,7 +386,11 @@ func authorizeDevice(serverURL string) (*DeviceConfig, error) {
}
var tokenResult DeviceTokenResponse
json.NewDecoder(tokenResp.Body).Decode(&tokenResult)
if err := json.NewDecoder(tokenResp.Body).Decode(&tokenResult); err != nil {
fmt.Fprintf(os.Stderr, "\nFailed to decode response: %v\n", err)
tokenResp.Body.Close()
continue
}
tokenResp.Body.Close()
if tokenResult.Error == "authorization_pending" {
@@ -767,9 +772,13 @@ func isNewerVersion(newVersion, currentVersion string) bool {
// Compare each part
for i := range min(len(newParts), len(curParts)) {
newNum := 0
if parsed, err := strconv.Atoi(newParts[i]); err == nil {
newNum = parsed
}
curNum := 0
fmt.Sscanf(newParts[i], "%d", &newNum)
fmt.Sscanf(curParts[i], "%d", &curNum)
if parsed, err := strconv.Atoi(curParts[i]); err == nil {
curNum = parsed
}
if newNum > curNum {
return true
@@ -881,7 +890,9 @@ func performUpdate(versionInfo *VersionAPIResponse) error {
// Install new binary
if err := copyFile(binaryPath, currentPath); err != nil {
// Try to restore backup
os.Rename(backupPath, currentPath)
if renameErr := os.Rename(backupPath, currentPath); renameErr != nil {
fmt.Fprintf(os.Stderr, "Warning: failed to restore backup: %v\n", renameErr)
}
return fmt.Errorf("failed to install new binary: %w", err)
}
@@ -889,7 +900,9 @@ func performUpdate(versionInfo *VersionAPIResponse) error {
if err := os.Chmod(currentPath, 0755); err != nil {
// Try to restore backup
os.Remove(currentPath)
os.Rename(backupPath, currentPath)
if renameErr := os.Rename(backupPath, currentPath); renameErr != nil {
fmt.Fprintf(os.Stderr, "Warning: failed to restore backup: %v\n", renameErr)
}
return fmt.Errorf("failed to set permissions: %w", err)
}
@@ -1047,7 +1060,11 @@ func saveUpdateCheckCache(cache *UpdateCheckCache) {
// Ensure directory exists
dir := filepath.Dir(path)
os.MkdirAll(dir, 0700)
if err := os.MkdirAll(dir, 0700); err != nil {
return
}
os.WriteFile(path, data, 0600)
if err := os.WriteFile(path, data, 0600); err != nil {
return // Cache write failed, non-critical
}
}
+15 -13
View File
@@ -2,6 +2,8 @@ package handlers
import (
"context"
"html/template"
"log/slog"
"net/http"
"net/url"
"time"
@@ -12,6 +14,7 @@ import (
// ManifestHealthHandler handles HTMX polling for manifest health status
type ManifestHealthHandler struct {
HealthChecker *holdhealth.Checker
Templates *template.Template
}
func (h *ManifestHealthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -61,18 +64,17 @@ func (h *ManifestHealthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
func (h *ManifestHealthHandler) renderBadge(w http.ResponseWriter, endpoint string, reachable, pending bool) {
w.Header().Set("Content-Type", "text/html")
if pending {
// Still checking - render badge with HTMX retry after 3 seconds
retryURL := "/api/manifest-health?endpoint=" + url.QueryEscape(endpoint)
w.Write([]byte(`<span class="checking-badge"
hx-get="` + retryURL + `"
hx-trigger="load delay:3s"
hx-swap="outerHTML"><i data-lucide="refresh-ccw"></i> Checking...</span>`))
} else if !reachable {
// Unreachable - render offline badge
w.Write([]byte(`<span class="offline-badge"><i data-lucide="triangle-alert"></i> Offline</span>`))
} else {
// Reachable - no badge (empty response)
w.Write([]byte(``))
data := struct {
Pending bool
Reachable bool
RetryURL string
}{
Pending: pending,
Reachable: reachable,
RetryURL: url.QueryEscape(endpoint),
}
if err := h.Templates.ExecuteTemplate(w, "health-badge", data); err != nil {
slog.Warn("Failed to render health badge", "error", err)
}
}
+1 -3
View File
@@ -211,9 +211,7 @@ func (h *UserOGHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// Draw package icon with description-sized text
if err := card.DrawIcon("package", int(layout.TextX), int(textY)-int(ogcard.FontDescription), int(ogcard.FontDescription), ogcard.ColorMuted); err != nil {
slog.Warn("Failed to draw package icon", "error", err)
}
card.DrawIcon("package", int(layout.TextX), int(textY)-int(ogcard.FontDescription), int(ogcard.FontDescription), ogcard.ColorMuted)
card.DrawText(repoText, layout.TextX+42, textY, ogcard.FontDescription, ogcard.ColorMuted, ogcard.AlignLeft, false)
// ATCR branding (bottom right)
+8 -1
View File
@@ -73,6 +73,7 @@ func (h *SettingsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// UpdateDefaultHoldHandler handles updating the default hold
type UpdateDefaultHoldHandler struct {
Refresher *oauth.Refresher
Templates *template.Template
}
func (h *UpdateDefaultHoldHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -105,5 +106,11 @@ func (h *UpdateDefaultHoldHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
}
w.Header().Set("Content-Type", "text/html")
w.Write([]byte(`<div class="success"><i data-lucide="check"></i> Default hold updated successfully!</div>`))
if err := h.Templates.ExecuteTemplate(w, "alert", map[string]string{
"Class": "success",
"Icon": "check",
"Message": "Default hold updated successfully!",
}); err != nil {
slog.Warn("Failed to render alert", "error", err)
}
}
+7 -5
View File
@@ -9,6 +9,7 @@ import (
_ "image/jpeg" // Register JPEG decoder for image.Decode
"image/png"
"io"
"log/slog"
"net/http"
"time"
@@ -118,14 +119,14 @@ func (c *Card) DrawRect(x, y, w, h int, col color.Color) {
draw.Draw(c.img, rect, &image.Uniform{col}, image.Point{}, draw.Over)
}
// DrawText draws text at the specified position
func (c *Card) DrawText(text string, x, y float64, size float64, col color.Color, align int, bold bool) error {
// DrawText draws text at the specified position.
func (c *Card) DrawText(text string, x, y float64, size float64, col color.Color, align int, bold bool) {
f := regularFont
if bold {
f = boldFont
}
if f == nil {
return nil // No font loaded
return // No font loaded
}
ctx := freetype.NewContext()
@@ -152,8 +153,9 @@ func (c *Card) DrawText(text string, x, y float64, size float64, col color.Color
}
pt := freetype.Pt(int(x), int(y))
_, err := ctx.DrawString(text, pt)
return err
if _, err := ctx.DrawString(text, pt); err != nil {
slog.Warn("Failed to draw text", "text", text, "error", err)
}
}
// MeasureText returns the width of text in pixels
+7 -6
View File
@@ -6,6 +6,7 @@ import (
"image"
"image/color"
"image/draw"
"log/slog"
"strings"
"github.com/srwiley/oksvg"
@@ -28,11 +29,12 @@ var iconPaths = map[string]string{
"package": `<path d="M16.5 9.4l-9-5.19M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/><path d="M3.27 6.96L12 12.01l8.73-5.05M12 22.08V12" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>`,
}
// DrawIcon draws a Lucide icon at the specified position with the given size and color
func (c *Card) DrawIcon(name string, x, y, size int, col color.Color) error {
// DrawIcon draws a Lucide icon at the specified position with the given size and color.
func (c *Card) DrawIcon(name string, x, y, size int, col color.Color) {
path, ok := iconPaths[name]
if !ok {
return fmt.Errorf("unknown icon: %s", name)
slog.Warn("Unknown icon", "name", name)
return
}
// Build full SVG with color
@@ -45,7 +47,8 @@ func (c *Card) DrawIcon(name string, x, y, size int, col color.Color) error {
// Parse SVG
icon, err := oksvg.ReadIconStream(bytes.NewReader([]byte(svg)))
if err != nil {
return fmt.Errorf("failed to parse icon SVG: %w", err)
slog.Warn("Failed to parse icon SVG", "name", name, "error", err)
return
}
// Create target image for the icon
@@ -63,6 +66,4 @@ func (c *Card) DrawIcon(name string, x, y, size int, col color.Color) error {
// Draw icon onto card
rect := image.Rect(x, y, x+size, y+size)
draw.Draw(c.img, rect, iconImg, image.Point{}, draw.Over)
return nil
}
+2
View File
@@ -146,6 +146,7 @@ func RegisterUIRoutes(router chi.Router, deps UIDependencies) {
// Manifest health check API endpoint (HTMX polling)
router.Get("/api/manifest-health", (&uihandlers.ManifestHealthHandler{
HealthChecker: deps.HealthChecker,
Templates: deps.Templates,
}).ServeHTTP)
router.Get("/u/{handle}", middleware.OptionalAuth(deps.SessionStore, deps.Database)(
@@ -196,6 +197,7 @@ func RegisterUIRoutes(router chi.Router, deps UIDependencies) {
r.Post("/api/profile/default-hold", (&uihandlers.UpdateDefaultHoldHandler{
Refresher: deps.Refresher,
Templates: deps.Templates,
}).ServeHTTP)
r.Delete("/api/images/{repository}/tags/{tag}", (&uihandlers.DeleteTagHandler{
@@ -0,0 +1,3 @@
{{ define "alert" }}
<div class="{{ .Class }}"><i data-lucide="{{ .Icon }}"></i> {{ .Message }}</div>
{{ end }}
@@ -0,0 +1,10 @@
{{ define "health-badge" }}
{{ if .Pending }}
<span class="checking-badge"
hx-get="/api/manifest-health?endpoint={{ .RetryURL }}"
hx-trigger="load delay:3s"
hx-swap="outerHTML"><i data-lucide="refresh-ccw"></i> Checking...</span>
{{ else if not .Reachable }}
<span class="offline-badge"><i data-lucide="triangle-alert"></i> Offline</span>
{{ end }}
{{ end }}
+91
View File
@@ -551,6 +551,8 @@ func TestTemplates(t *testing.T) {
"install.html",
"manifest-modal",
"push-list.html",
"health-badge",
"alert",
}
for _, name := range expectedTemplates {
@@ -683,6 +685,95 @@ func TestTemplateExecution_WithFuncMap(t *testing.T) {
}
}
func TestTemplateExecution_HealthBadge(t *testing.T) {
tmpl, err := Templates()
if err != nil {
t.Fatalf("Templates() error = %v", err)
}
tests := []struct {
name string
data map[string]any
expectInOutput string
expectMissing string
}{
{
name: "pending state",
data: map[string]any{
"Pending": true,
"Reachable": false,
"RetryURL": "http%3A%2F%2Fexample.com",
},
expectInOutput: "checking-badge",
expectMissing: "offline-badge",
},
{
name: "offline state",
data: map[string]any{
"Pending": false,
"Reachable": false,
"RetryURL": "",
},
expectInOutput: "offline-badge",
expectMissing: "checking-badge",
},
{
name: "online state - empty output",
data: map[string]any{
"Pending": false,
"Reachable": true,
"RetryURL": "",
},
expectMissing: "badge",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
buf := new(bytes.Buffer)
err := tmpl.ExecuteTemplate(buf, "health-badge", tt.data)
if err != nil {
t.Fatalf("Failed to execute template: %v", err)
}
output := buf.String()
if tt.expectInOutput != "" && !strings.Contains(output, tt.expectInOutput) {
t.Errorf("Template output %q does not contain expected %q", output, tt.expectInOutput)
}
if tt.expectMissing != "" && strings.Contains(output, tt.expectMissing) {
t.Errorf("Template output %q should not contain %q", output, tt.expectMissing)
}
})
}
}
func TestTemplateExecution_Alert(t *testing.T) {
tmpl, err := Templates()
if err != nil {
t.Fatalf("Templates() error = %v", err)
}
data := map[string]string{
"Class": "success",
"Icon": "check",
"Message": "Operation completed!",
}
buf := new(bytes.Buffer)
err = tmpl.ExecuteTemplate(buf, "alert", data)
if err != nil {
t.Fatalf("Failed to execute template: %v", err)
}
output := buf.String()
expectedParts := []string{"success", "check", "Operation completed!"}
for _, expected := range expectedParts {
if !strings.Contains(output, expected) {
t.Errorf("Template output %q does not contain expected %q", output, expected)
}
}
}
func TestStaticHandler(t *testing.T) {
handler := StaticHandler()
if handler == nil {
+1 -1
View File
@@ -87,7 +87,7 @@ func (ui *AdminUI) newPageData(r *http.Request, title, activePage string) PageDa
}
// renderTemplate renders a template with the given data
func (ui *AdminUI) renderTemplate(w http.ResponseWriter, name string, data interface{}) {
func (ui *AdminUI) renderTemplate(w http.ResponseWriter, name string, data any) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if err := ui.templates.ExecuteTemplate(w, name, data); err != nil {