1
0
Fork 1
mirror of https://github.com/git-pkgs/proxy.git synced 2026-06-02 00:38:16 -04:00

Compare commits

...

1 commit

Author SHA1 Message Date
Andrew Nesbitt
5e88758852
Add .golangci.yml and clear gocognit/goconst findings
Bake the extended linter set into a project config so plain
golangci-lint run matches what we check locally, with goconst tuned
to ignore tests and bare lowercase words to drop ~200 ecosystem-name
and test-literal false positives.

Clear the remaining real findings: extract GradleBuildCacheConfig.Validate
from Config.Validate, pull the eviction sort comparator into
sortOldestFirst (zero time.Time already sorts first via Before so the
switch was redundant), add headerAcceptEncoding and SQL column-type
constants, and drop a dead empty-key recheck in the gradle handler.
2026-05-04 11:33:35 +01:00
10 changed files with 97 additions and 62 deletions

28
.golangci.yml Normal file
View file

@ -0,0 +1,28 @@
version: "2"
linters:
enable:
- gocritic
- gocognit
- gocyclo
- maintidx
- dupl
- mnd
- unparam
- ireturn
- goconst
- errcheck
settings:
goconst:
min-len: 4
min-occurrences: 5
ignore-tests: true
ignore-string-values:
- "^[a-z]+$"
exclusions:
rules:
- path: _test\.go
linters:
- goconst
- dupl
- mnd

View file

@ -294,10 +294,10 @@ func Default() *Config {
Gradle: GradleConfig{ Gradle: GradleConfig{
BuildCache: GradleBuildCacheConfig{ BuildCache: GradleBuildCacheConfig{
ReadOnly: false, ReadOnly: false,
MaxUploadSize: "100MB", MaxUploadSize: defaultGradleMaxUploadSizeStr,
MaxAge: "168h", MaxAge: "168h",
MaxSize: "", MaxSize: "",
SweepInterval: "10m", SweepInterval: defaultGradleSweepIntervalStr,
}, },
}, },
} }
@ -481,51 +481,59 @@ func (c *Config) Validate() error {
} }
} }
// Validate Gradle build cache upload size (always required and must be > 0). if err := c.Gradle.BuildCache.Validate(); err != nil {
if c.Gradle.BuildCache.MaxUploadSize == "" { return err
c.Gradle.BuildCache.MaxUploadSize = "100MB"
} }
uploadSize, err := ParseSize(c.Gradle.BuildCache.MaxUploadSize)
return nil
}
// Validate checks Gradle build cache settings, applying the default upload
// size if unset.
func (g *GradleBuildCacheConfig) Validate() error {
if g.MaxUploadSize == "" {
g.MaxUploadSize = defaultGradleMaxUploadSizeStr
}
uploadSize, err := ParseSize(g.MaxUploadSize)
if err != nil { if err != nil {
return fmt.Errorf("invalid gradle.build_cache.max_upload_size: %w", err) return fmt.Errorf("invalid gradle.build_cache.max_upload_size: %w", err)
} }
if uploadSize <= 0 { if uploadSize <= 0 {
return fmt.Errorf("invalid gradle.build_cache.max_upload_size %q: must be > 0", c.Gradle.BuildCache.MaxUploadSize) return fmt.Errorf("invalid gradle.build_cache.max_upload_size %q: must be > 0", g.MaxUploadSize)
} }
// Validate Gradle max age if specified. if g.MaxAge != "" && g.MaxAge != "0" {
if c.Gradle.BuildCache.MaxAge != "" && c.Gradle.BuildCache.MaxAge != "0" { if _, err := time.ParseDuration(g.MaxAge); err != nil {
if _, err := time.ParseDuration(c.Gradle.BuildCache.MaxAge); err != nil { return fmt.Errorf("invalid gradle.build_cache.max_age %q: %w", g.MaxAge, err)
return fmt.Errorf("invalid gradle.build_cache.max_age %q: %w", c.Gradle.BuildCache.MaxAge, err)
} }
} }
// Validate Gradle max size if specified. if g.MaxSize != "" {
if c.Gradle.BuildCache.MaxSize != "" { if _, err := ParseSize(g.MaxSize); err != nil {
if _, err := ParseSize(c.Gradle.BuildCache.MaxSize); err != nil {
return fmt.Errorf("invalid gradle.build_cache.max_size: %w", err) return fmt.Errorf("invalid gradle.build_cache.max_size: %w", err)
} }
} }
// Validate Gradle sweep interval if specified. if g.SweepInterval != "" {
if c.Gradle.BuildCache.SweepInterval != "" { d, err := time.ParseDuration(g.SweepInterval)
d, err := time.ParseDuration(c.Gradle.BuildCache.SweepInterval)
if err != nil { if err != nil {
return fmt.Errorf("invalid gradle.build_cache.sweep_interval %q: %w", c.Gradle.BuildCache.SweepInterval, err) return fmt.Errorf("invalid gradle.build_cache.sweep_interval %q: %w", g.SweepInterval, err)
} }
if d <= 0 { if d <= 0 {
return fmt.Errorf("invalid gradle.build_cache.sweep_interval %q: must be > 0", c.Gradle.BuildCache.SweepInterval) return fmt.Errorf("invalid gradle.build_cache.sweep_interval %q: must be > 0", g.SweepInterval)
} }
} }
return nil return nil
} }
const defaultGradleBuildCacheMaxUploadSize = 100 << 20
const defaultGradleBuildCacheSweepInterval = 10 * time.Minute
const ( const (
defaultMetadataTTL = 5 * time.Minute //nolint:mnd // sensible default defaultMetadataTTL = 5 * time.Minute //nolint:mnd // sensible default
defaultDirectServeTTL = 15 * time.Minute //nolint:mnd // sensible default defaultDirectServeTTL = 15 * time.Minute //nolint:mnd // sensible default
defaultGradleBuildCacheMaxUploadSize = 100 << 20
defaultGradleBuildCacheSweepInterval = 10 * time.Minute
defaultGradleMaxUploadSizeStr = "100MB"
defaultGradleSweepIntervalStr = "10m"
) )
// ParseMaxSize returns the maximum cache size in bytes. // ParseMaxSize returns the maximum cache size in bytes.

View file

@ -6,7 +6,11 @@ import (
"time" "time"
) )
const postgresTimestamp = "TIMESTAMP" const (
postgresTimestamp = "TIMESTAMP"
sqliteDatetime = "DATETIME"
colTypeText = "TEXT"
)
// Schema for proxy-specific tables. The packages and versions tables // Schema for proxy-specific tables. The packages and versions tables
// are compatible with git-pkgs, allowing the proxy to use an existing // are compatible with git-pkgs, allowing the proxy to use an existing
@ -369,9 +373,9 @@ func isTableNotFound(err error) bool {
func (db *DB) createMigrationsTable() error { func (db *DB) createMigrationsTable() error {
var ts string var ts string
if db.dialect == DialectPostgres { if db.dialect == DialectPostgres {
ts = "TIMESTAMP" ts = postgresTimestamp
} else { } else {
ts = "DATETIME" ts = sqliteDatetime
} }
query := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS migrations ( query := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS migrations (
@ -457,12 +461,12 @@ func (db *DB) MigrateSchema() error {
func migrateAddPackagesEnrichmentColumns(db *DB) error { func migrateAddPackagesEnrichmentColumns(db *DB) error {
columns := map[string]string{ columns := map[string]string{
"registry_url": "TEXT", "registry_url": colTypeText,
"supplier_name": "TEXT", "supplier_name": colTypeText,
"supplier_type": "TEXT", "supplier_type": colTypeText,
"source": "TEXT", "source": colTypeText,
"enriched_at": "DATETIME", "enriched_at": sqliteDatetime,
"vulns_synced_at": "DATETIME", "vulns_synced_at": sqliteDatetime,
} }
if db.dialect == DialectPostgres { if db.dialect == DialectPostgres {
@ -487,10 +491,10 @@ func migrateAddPackagesEnrichmentColumns(db *DB) error {
func migrateAddVersionsEnrichmentColumns(db *DB) error { func migrateAddVersionsEnrichmentColumns(db *DB) error {
columns := map[string]string{ columns := map[string]string{
"integrity": "TEXT", "integrity": colTypeText,
"yanked": "INTEGER DEFAULT 0", "yanked": "INTEGER DEFAULT 0",
"source": "TEXT", "source": colTypeText,
"enriched_at": "DATETIME", "enriched_at": sqliteDatetime,
} }
if db.dialect == DialectPostgres { if db.dialect == DialectPostgres {

View file

@ -140,7 +140,7 @@ func (h *CondaHandler) handleRepodata(w http.ResponseWriter, r *http.Request) {
http.Error(w, "failed to create request", http.StatusInternalServerError) http.Error(w, "failed to create request", http.StatusInternalServerError)
return return
} }
req.Header.Set("Accept-Encoding", "gzip") req.Header.Set(headerAcceptEncoding, "gzip")
resp, err := h.proxy.HTTPClient.Do(req) resp, err := h.proxy.HTTPClient.Do(req)
if err != nil { if err != nil {
@ -241,5 +241,5 @@ func (h *CondaHandler) proxyCached(w http.ResponseWriter, r *http.Request) {
// proxyUpstream forwards a request to Anaconda without caching. // proxyUpstream forwards a request to Anaconda without caching.
func (h *CondaHandler) proxyUpstream(w http.ResponseWriter, r *http.Request) { func (h *CondaHandler) proxyUpstream(w http.ResponseWriter, r *http.Request) {
h.proxy.ProxyUpstream(w, r, h.upstreamURL+r.URL.Path, []string{"Accept-Encoding"}) h.proxy.ProxyUpstream(w, r, h.upstreamURL+r.URL.Path, []string{headerAcceptEncoding})
} }

View file

@ -159,5 +159,5 @@ func (h *CRANHandler) proxyCached(w http.ResponseWriter, r *http.Request) {
// proxyUpstream forwards a request to CRAN without caching. // proxyUpstream forwards a request to CRAN without caching.
func (h *CRANHandler) proxyUpstream(w http.ResponseWriter, r *http.Request) { func (h *CRANHandler) proxyUpstream(w http.ResponseWriter, r *http.Request) {
h.proxy.ProxyUpstream(w, r, h.upstreamURL+r.URL.Path, []string{"Accept-Encoding"}) h.proxy.ProxyUpstream(w, r, h.upstreamURL+r.URL.Path, []string{headerAcceptEncoding})
} }

View file

@ -182,7 +182,7 @@ func (h *GemHandler) fetchCompactIndex(r *http.Request, name string) (*http.Resp
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, hdr := range []string{"Accept", "Accept-Encoding", "If-None-Match", "If-Modified-Since"} { for _, hdr := range []string{"Accept", headerAcceptEncoding, "If-None-Match", "If-Modified-Since"} {
if v := r.Header.Get(hdr); v != "" { if v := r.Header.Get(hdr); v != "" {
req.Header.Set(hdr, v) req.Header.Set(hdr, v)
} }
@ -311,7 +311,7 @@ func (h *GemHandler) proxyUpstream(w http.ResponseWriter, r *http.Request) {
} }
// Copy relevant headers // Copy relevant headers
for _, h := range []string{"Accept", "Accept-Encoding", "If-None-Match", "If-Modified-Since"} { for _, h := range []string{"Accept", headerAcceptEncoding, "If-None-Match", "If-Modified-Since"} {
if v := r.Header.Get(h); v != "" { if v := r.Header.Get(h); v != "" {
req.Header.Set(h, v) req.Header.Set(h, v)
} }

View file

@ -74,7 +74,7 @@ func (h *GradleBuildCacheHandler) parseCacheKey(urlPath string) (string, int) {
return "", http.StatusBadRequest return "", http.StatusBadRequest
} }
if keyPath == "" || strings.Contains(keyPath, "/") { if strings.Contains(keyPath, "/") {
return "", http.StatusNotFound return "", http.StatusNotFound
} }

View file

@ -50,6 +50,8 @@ const defaultHTTPTimeout = 30 * time.Second
const contentTypeJSON = "application/json" const contentTypeJSON = "application/json"
const headerAcceptEncoding = "Accept-Encoding"
// maxMetadataSize is the maximum size of upstream metadata responses (100 MB). // maxMetadataSize is the maximum size of upstream metadata responses (100 MB).
// Package metadata (e.g. npm with many versions) can be large, but unbounded // Package metadata (e.g. npm with many versions) can be large, but unbounded
// reads risk OOM if an upstream misbehaves. // reads risk OOM if an upstream misbehaves.
@ -726,7 +728,7 @@ func (p *Proxy) proxyMetadataStream(w http.ResponseWriter, r *http.Request, upst
} }
req.Header.Set("Accept", accept) req.Header.Set("Accept", accept)
for _, header := range []string{"Accept-Encoding", "If-Modified-Since", "If-None-Match"} { for _, header := range []string{headerAcceptEncoding, "If-Modified-Since", "If-None-Match"} {
if v := r.Header.Get(header); v != "" { if v := r.Header.Get(header); v != "" {
req.Header.Set(header, v) req.Header.Set(header, v)
} }

View file

@ -172,7 +172,7 @@ func (h *NuGetHandler) handleRegistration(w http.ResponseWriter, r *http.Request
http.Error(w, "failed to create request", http.StatusInternalServerError) http.Error(w, "failed to create request", http.StatusInternalServerError)
return return
} }
req.Header.Set("Accept-Encoding", "gzip") req.Header.Set(headerAcceptEncoding, "gzip")
resp, err := h.proxy.HTTPClient.Do(req) resp, err := h.proxy.HTTPClient.Do(req)
if err != nil { if err != nil {
@ -338,8 +338,8 @@ func (h *NuGetHandler) proxyUpstream(w http.ResponseWriter, r *http.Request) {
} }
// Copy accept-encoding for compression // Copy accept-encoding for compression
if ae := r.Header.Get("Accept-Encoding"); ae != "" { if ae := r.Header.Get(headerAcceptEncoding); ae != "" {
req.Header.Set("Accept-Encoding", ae) req.Header.Set(headerAcceptEncoding, ae)
} }
resp, err := h.proxy.HTTPClient.Do(req) resp, err := h.proxy.HTTPClient.Do(req)

View file

@ -81,23 +81,7 @@ func sweepGradleBuildCache(
return 0, 0, nil return 0, 0, nil
} }
sort.Slice(entries, func(i, j int) bool { sortOldestFirst(entries)
iTime := entries[i].ModTime
jTime := entries[j].ModTime
switch {
case iTime.IsZero() && jTime.IsZero():
return entries[i].Path < entries[j].Path
case iTime.IsZero():
return true
case jTime.IsZero():
return false
case iTime.Equal(jTime):
return entries[i].Path < entries[j].Path
default:
return iTime.Before(jTime)
}
})
deletedCount := 0 deletedCount := 0
freedBytes := int64(0) freedBytes := int64(0)
@ -154,3 +138,12 @@ func sweepGradleBuildCache(
return deletedCount, freedBytes, nil return deletedCount, freedBytes, nil
} }
func sortOldestFirst(entries []storage.ObjectInfo) {
sort.Slice(entries, func(i, j int) bool {
if entries[i].ModTime.Equal(entries[j].ModTime) {
return entries[i].Path < entries[j].Path
}
return entries[i].ModTime.Before(entries[j].ModTime)
})
}