mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-20 01:04:15 +00:00
more lint fixes. enable autofix
This commit is contained in:
+9
-2
@@ -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'
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 }}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user