mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-08-29 04:06:58 +00:00
more linting fixes
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
# golangci-lint configuration for ATCR
|
||||
# See: https://golangci-lint.run/usage/configuration/
|
||||
version: "2"
|
||||
linters:
|
||||
settings:
|
||||
staticcheck:
|
||||
checks:
|
||||
- "all"
|
||||
- "-SA1019" # Ignore deprecated package warnings for github.com/ipfs/go-ipfs-blockstore
|
||||
# Cannot upgrade to github.com/ipfs/boxo/blockstore due to opentelemetry
|
||||
# dependency conflicts with distribution/distribution
|
||||
errcheck:
|
||||
exclude-functions:
|
||||
- (github.com/distribution/distribution/v3/registry/storage/driver.FileWriter).Cancel
|
||||
- (github.com/distribution/distribution/v3.BlobWriter).Cancel
|
||||
- (*database/sql.Tx).Rollback
|
||||
- (*database/sql.Rows).Close
|
||||
- (*net/http.Server).Shutdown
|
||||
|
||||
exclusions:
|
||||
presets:
|
||||
- std-error-handling
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
@@ -1,3 +1,8 @@
|
||||
// Package appview implements the ATCR AppView component, which serves as the main
|
||||
// OCI Distribution API server. It resolves identities (handle/DID to PDS endpoint),
|
||||
// routes manifests to user's PDS, routes blobs to hold services, validates OAuth tokens,
|
||||
// and issues registry JWTs. This package provides environment-based configuration,
|
||||
// middleware registration, and HTTP server setup for the AppView service.
|
||||
package appview
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Package db provides the database layer for the AppView web UI, including
|
||||
// SQLite schema initialization, migrations, and query functions for OAuth
|
||||
// sessions, device flows, repository metadata, stars, pull counts, and
|
||||
// user profiles.
|
||||
package db
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Package handlers provides HTTP handlers for the AppView web UI, including
|
||||
// home page, repository browsing, search, user authentication, settings,
|
||||
// device management, and API endpoints for the web interface.
|
||||
package handlers
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Package holdhealth provides health checking for hold service endpoints.
|
||||
// It periodically checks hold availability and caches health status with
|
||||
// configurable TTL to avoid excessive health check requests.
|
||||
package holdhealth
|
||||
|
||||
import (
|
||||
|
||||
@@ -131,6 +131,7 @@ func TestGetStatus_CacheHit(t *testing.T) {
|
||||
status := checker.GetStatus(context.Background(), endpoint)
|
||||
if status == nil {
|
||||
t.Fatal("GetStatus returned nil")
|
||||
return
|
||||
}
|
||||
|
||||
if !status.Reachable {
|
||||
@@ -155,6 +156,7 @@ func TestGetStatus_CacheMiss(t *testing.T) {
|
||||
status := checker.GetStatus(context.Background(), server.URL)
|
||||
if status == nil {
|
||||
t.Fatal("GetStatus returned nil")
|
||||
return
|
||||
}
|
||||
|
||||
if !status.Reachable {
|
||||
@@ -191,6 +193,7 @@ func TestSetStatus(t *testing.T) {
|
||||
status := checker.GetCachedStatus(endpoint)
|
||||
if status == nil {
|
||||
t.Fatal("Status not found in cache")
|
||||
return
|
||||
}
|
||||
|
||||
if !status.Reachable {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Package jetstream provides an ATProto Jetstream consumer for real-time updates.
|
||||
// It connects to the Bluesky Jetstream WebSocket, processes repository events,
|
||||
// indexes manifests and tags, and populates the AppView database for the web UI.
|
||||
package jetstream
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Package licenses provides SPDX license validation and parsing for container
|
||||
// image annotations. It embeds the official SPDX license list and provides
|
||||
// functions to look up license identifiers, validate them, and parse
|
||||
// multi-license strings with fuzzy matching support.
|
||||
package licenses
|
||||
|
||||
//go:generate curl -fsSL -o spdx-licenses.json https://spdx.org/licenses/licenses.json
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Package middleware provides HTTP middleware for AppView, including
|
||||
// authentication (session-based for web UI, token-based for registry),
|
||||
// identity resolution (handle/DID to PDS endpoint), and hold discovery
|
||||
// for routing blobs to storage endpoints.
|
||||
package middleware
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Package readme provides README fetching, rendering, and caching functionality
|
||||
// for container repositories. It fetches markdown content from URLs, renders it
|
||||
// to sanitized HTML using GitHub-flavored markdown, and caches the results in
|
||||
// a database with configurable TTL.
|
||||
package readme
|
||||
|
||||
import (
|
||||
|
||||
@@ -451,7 +451,7 @@ func (p *ProxyBlobStore) getPartUploadInfo(ctx context.Context, digest, uploadID
|
||||
return nil, err
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s%s", p.holdURL, atproto.HoldGetPartUploadUrl)
|
||||
url := fmt.Sprintf("%s%s", p.holdURL, atproto.HoldGetPartUploadURL)
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -551,7 +551,7 @@ func TestMultipartEndpoints_CorrectURLs(t *testing.T) {
|
||||
_, err := store.getPartUploadInfo(context.Background(), "sha256:test", "upload-123", 1)
|
||||
return err
|
||||
},
|
||||
expectedPath: atproto.HoldGetPartUploadUrl,
|
||||
expectedPath: atproto.HoldGetPartUploadURL,
|
||||
},
|
||||
{
|
||||
name: "completeMultipartUpload",
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Package storage implements the storage routing layer for AppView.
|
||||
// It routes manifests to ATProto PDS (as io.atcr.manifest records) and
|
||||
// blobs to hold services via XRPC, with hold DID caching for efficient pulls.
|
||||
// All storage operations are proxied - AppView stores nothing locally.
|
||||
package storage
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Package xrpc provides constants for XRPC endpoint paths used throughout ATCR.
|
||||
// Package atproto provides constants for XRPC endpoint paths used throughout ATCR.
|
||||
//
|
||||
// This package serves as a single source of truth for all XRPC endpoint URLs,
|
||||
// preventing typos and making refactoring easier. All endpoint paths follow the
|
||||
@@ -15,11 +15,11 @@ const (
|
||||
// Response: {"uploadId": "..."}
|
||||
HoldInitiateUpload = "/xrpc/io.atcr.hold.initiateUpload"
|
||||
|
||||
// HoldGetPartUploadUrl gets a presigned URL or endpoint info for uploading a specific part.
|
||||
// HoldGetPartUploadURL gets a presigned URL or endpoint info for uploading a specific part.
|
||||
// Method: POST
|
||||
// Request: {"uploadId": "...", "partNumber": 1}
|
||||
// Response: {"url": "...", "method": "PUT", "headers": {...}}
|
||||
HoldGetPartUploadUrl = "/xrpc/io.atcr.hold.getPartUploadUrl"
|
||||
HoldGetPartUploadURL = "/xrpc/io.atcr.hold.getPartUploadUrl"
|
||||
|
||||
// HoldUploadPart handles direct buffered part uploads (alternative to presigned URLs).
|
||||
// Method: PUT
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Profile record key is always "self" per lexicon
|
||||
// ProfileRKey is always "self" per lexicon
|
||||
const ProfileRKey = "self"
|
||||
|
||||
// Global map to track in-flight profile migrations (DID -> true)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Package oauth provides OAuth client and flow implementation for ATCR.
|
||||
// It wraps indigo's OAuth library with ATCR-specific configuration,
|
||||
// including default scopes, client metadata, token refreshing, and
|
||||
// interactive browser-based authentication flows.
|
||||
package oauth
|
||||
|
||||
import (
|
||||
|
||||
+5
-1
@@ -1,7 +1,9 @@
|
||||
// Package auth provides authentication and authorization for ATCR, including
|
||||
// ATProto session validation, hold authorization (captain/crew membership),
|
||||
// scope parsing, and token caching for OAuth and service tokens.
|
||||
package auth
|
||||
|
||||
import (
|
||||
"atcr.io/pkg/atproto"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
@@ -13,6 +15,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"atcr.io/pkg/atproto"
|
||||
|
||||
"github.com/bluesky-social/indigo/atproto/identity"
|
||||
"github.com/bluesky-social/indigo/atproto/syntax"
|
||||
)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Package token provides service token caching and management for AppView.
|
||||
// Service tokens are JWTs issued by a user's PDS to authorize AppView to
|
||||
// act on their behalf when communicating with hold services. Tokens are
|
||||
// cached with automatic expiry parsing and 10-second safety margins.
|
||||
package token
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
// Package hold implements the ATCR hold service, which provides BYOS
|
||||
// (Bring Your Own Storage) functionality. It includes an embedded PDS for
|
||||
// storing captain and crew records, generates presigned URLs for blob storage,
|
||||
// and handles authorization based on crew membership. Configuration is loaded
|
||||
// entirely from environment variables.
|
||||
package hold
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Package oci provides HTTP helpers for OCI registry endpoints in the hold service.
|
||||
// It includes utilities for JSON encoding/decoding of request/response bodies
|
||||
// and standardized error responses for XRPC endpoints.
|
||||
package oci
|
||||
|
||||
import (
|
||||
|
||||
@@ -26,7 +26,7 @@ const (
|
||||
Buffered
|
||||
)
|
||||
|
||||
// CompletedPart represents an uploaded part with its ETag
|
||||
// PartInfo represents an uploaded part with its ETag
|
||||
type PartInfo struct {
|
||||
PartNumber int `json:"part_number"`
|
||||
ETag string `json:"etag"`
|
||||
|
||||
@@ -44,7 +44,7 @@ func (h *XRPCHandler) RegisterHandlers(r chi.Router) {
|
||||
r.Use(h.requireBlobWriteAccess)
|
||||
|
||||
r.Post(atproto.HoldInitiateUpload, h.HandleInitiateUpload)
|
||||
r.Post(atproto.HoldGetPartUploadUrl, h.HandleGetPartUploadUrl)
|
||||
r.Post(atproto.HoldGetPartUploadURL, h.HandleGetPartUploadURL)
|
||||
r.Put(atproto.HoldUploadPart, h.HandleUploadPart)
|
||||
r.Post(atproto.HoldCompleteUpload, h.HandleCompleteUpload)
|
||||
r.Post(atproto.HoldAbortUpload, h.HandleAbortUpload)
|
||||
@@ -80,9 +80,9 @@ func (h *XRPCHandler) HandleInitiateUpload(w http.ResponseWriter, r *http.Reques
|
||||
})
|
||||
}
|
||||
|
||||
// HandleGetPartUploadUrl returns a presigned URL or endpoint info for uploading a part
|
||||
// HandleGetPartUploadURL returns a presigned URL or endpoint info for uploading a part
|
||||
// Replaces the old "action: part" pattern
|
||||
func (h *XRPCHandler) HandleGetPartUploadUrl(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *XRPCHandler) HandleGetPartUploadURL(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
UploadID string `json:"uploadId"`
|
||||
PartNumber int `json:"partNumber"`
|
||||
|
||||
@@ -218,14 +218,14 @@ func TestHandleGetPartUploadUrl_Buffered(t *testing.T) {
|
||||
uploadID := initResp["uploadId"].(string)
|
||||
|
||||
// Now get part upload URL
|
||||
req := makeJSONRequest("POST", atproto.HoldGetPartUploadUrl, map[string]any{
|
||||
req := makeJSONRequest("POST", atproto.HoldGetPartUploadURL, map[string]any{
|
||||
"uploadId": uploadID,
|
||||
"partNumber": 1,
|
||||
})
|
||||
addMockAuth(req)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
handler.HandleGetPartUploadUrl(w, req)
|
||||
handler.HandleGetPartUploadURL(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d: %s", w.Code, w.Body.String())
|
||||
@@ -246,14 +246,14 @@ func TestHandleGetPartUploadUrl_Buffered(t *testing.T) {
|
||||
func TestHandleGetPartUploadUrl_InvalidSession(t *testing.T) {
|
||||
handler, _ := setupTestOCIHandler(t)
|
||||
|
||||
req := makeJSONRequest("POST", atproto.HoldGetPartUploadUrl, map[string]any{
|
||||
req := makeJSONRequest("POST", atproto.HoldGetPartUploadURL, map[string]any{
|
||||
"uploadId": "invalid-upload-id",
|
||||
"partNumber": 1,
|
||||
})
|
||||
addMockAuth(req)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
handler.HandleGetPartUploadUrl(w, req)
|
||||
handler.HandleGetPartUploadURL(w, req)
|
||||
|
||||
if w.Code != http.StatusInternalServerError {
|
||||
t.Errorf("Expected status 500, got %d", w.Code)
|
||||
@@ -274,11 +274,11 @@ func TestHandleGetPartUploadUrl_MissingParams(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req := makeJSONRequest("POST", atproto.HoldGetPartUploadUrl, tt.body)
|
||||
req := makeJSONRequest("POST", atproto.HoldGetPartUploadURL, tt.body)
|
||||
addMockAuth(req)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
handler.HandleGetPartUploadUrl(w, req)
|
||||
handler.HandleGetPartUploadURL(w, req)
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("Expected status 400, got %d", w.Code)
|
||||
|
||||
@@ -1250,7 +1250,7 @@ func (rm *RepoManager) TakeDownRepo(ctx context.Context, uid models.Uid) error {
|
||||
return rm.cs.WipeUserData(ctx, uid)
|
||||
}
|
||||
|
||||
// technically identical to TakeDownRepo, for now
|
||||
// ResetRepo is technically identical to TakeDownRepo, for now
|
||||
func (rm *RepoManager) ResetRepo(ctx context.Context, uid models.Uid) error {
|
||||
unlock := rm.lockUser(ctx, uid)
|
||||
defer unlock()
|
||||
|
||||
@@ -1223,7 +1223,7 @@ func (h *XRPCHandler) HandleRequestCrew(w http.ResponseWriter, r *http.Request)
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// getPresignedURL generates a presigned URL for GET, HEAD, or PUT operations
|
||||
// GetPresignedURL generates a presigned URL for GET, HEAD, or PUT operations
|
||||
// Distinguishes between ATProto blobs (per-DID) and OCI blobs (content-addressed)
|
||||
func (h *XRPCHandler) GetPresignedURL(ctx context.Context, operation string, digest string, did string) (string, error) {
|
||||
var path string
|
||||
|
||||
+8
-4
@@ -1,13 +1,17 @@
|
||||
// Package s3 provides S3 client initialization and presigned URL generation
|
||||
// for hold services. It supports S3, Storj, and Minio storage backends,
|
||||
// with fallback to buffered proxy mode when presigned URLs are unavailable.
|
||||
package s3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type S3Service struct {
|
||||
@@ -16,7 +20,7 @@ type S3Service struct {
|
||||
PathPrefix string // S3 path prefix (if any)
|
||||
}
|
||||
|
||||
// initializes the S3 client for presigned URL generation
|
||||
// NewS3Service initializes the S3 client for presigned URL generation
|
||||
// Returns nil error if S3 client is successfully initialized
|
||||
// Returns error if storage is not S3 or if initialization fails (service will fall back to proxy mode)
|
||||
func NewS3Service(params map[string]any, disablePresigned bool, storageType string) (*S3Service, error) {
|
||||
@@ -85,7 +89,7 @@ func NewS3Service(params map[string]any, disablePresigned bool, storageType stri
|
||||
}, nil
|
||||
}
|
||||
|
||||
// blobPath converts a digest (e.g., "sha256:abc123...") or temp path to a storage path
|
||||
// BlobPath converts a digest (e.g., "sha256:abc123...") or temp path to a storage path
|
||||
// Distribution stores blobs as: /docker/registry/v2/blobs/{algorithm}/{xx}/{hash}/data
|
||||
// where xx is the first 2 characters of the hash for directory sharding
|
||||
// NOTE: Path must start with / for filesystem driver
|
||||
|
||||
Reference in New Issue
Block a user