Update golangci-lint to v2.12.2 with canonical config (#187)
All checks were successful
Check / check (push) Successful in 4s
All checks were successful
Check / check (push) Successful in 4s
Bumps golangci-lint from v2.10.1 to v2.12.2 everywhere it is pinned and installs the canonical `.golangci.yml`, then fixes every finding the new linter surfaces so `make check` is green. ## Version pins - `Dockerfile` lint stage: `golangci/golangci-lint:v2.12.2` (Debian-based), tag plus digest pin - `script/bootstrap`: `GOLANGCI_LINT_VERSION=2.12.2` with updated `linux-amd64`/`linux-arm64` release-archive sha256 pins ## Config `.golangci.yml` replaced with the canonical config. Material change: the old file declared `version: "2"` but kept settings under the legacy top-level `linters-settings` key, which golangci-lint v2 ignores — so the intended thresholds (`lll` 88, `funlen` 80/50, `cyclop` 15, `dupl` 100) were not being applied. The canonical file moves them under `linters.settings` and drops `issues.exclude-use-default`. ## Lint fixes (216 findings) - `lll` (96): wrapped lines to the 88-column limit - `noctx` (46): `httptest.NewRequestWithContext` with `t.Context()` throughout the tests - `goconst` (24): shared constants for template/JSON keys in `internal/handlers` and repeated test literals - `gosec` (23): app-page redirects now go through a `redirectToApp` helper that path-escapes the app ID (G710 open redirect); `http.ServeFile` of the internally derived deployment log path annotated like the adjacent `os.Stat` (G703) - `dupl` (22): extracted a generic `findAllByAppID` in `internal/models`, a `deleteAppResource` helper in `internal/handlers`, a shared `parsePush` in `internal/service/webhook`, and table-driven/helper-based dedup in tests - `nolintlint` (5): removed `//nolint:funlen` directives made obsolete by the new limits (plus one more that became obsolete after refactoring) - `nilerr` (3, surfaced during fixing): resource-delete lookups now propagate the find error to the caller No behavior changes intended; all tests pass and `make check` is green. Note: golangci-lint v2.12 warns that `gomodguard` is deprecated in favor of `gomodguard_v2` — a future canonical-config update should address this centrally. Co-authored-by: sneak <sneak@sneak.berlin> Reviewed-on: #187 Co-authored-by: clawbot <clawbot@noreply.example.org> Co-committed-by: clawbot <clawbot@noreply.example.org>
This commit was merged in pull request #187.
This commit is contained in:
@@ -93,6 +93,41 @@ func FindEnvVar(
|
||||
return envVar, nil
|
||||
}
|
||||
|
||||
// findAllByAppID loads all rows for an app, scanning each row into a
|
||||
// new model created by newFn. entity names the model in error messages.
|
||||
func findAllByAppID[T interface{ scanDest() []any }](
|
||||
ctx context.Context,
|
||||
db *database.Database,
|
||||
query, appID, entity string,
|
||||
newFn func(*database.Database) T,
|
||||
) ([]T, error) {
|
||||
rows, err := db.Query(ctx, query, appID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("querying %s by app: %w", entity, err)
|
||||
}
|
||||
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
var items []T
|
||||
|
||||
for rows.Next() {
|
||||
item := newFn(db)
|
||||
|
||||
scanErr := rows.Scan(item.scanDest()...)
|
||||
if scanErr != nil {
|
||||
return nil, scanErr
|
||||
}
|
||||
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
return items, rows.Err()
|
||||
}
|
||||
|
||||
func (e *EnvVar) scanDest() []any {
|
||||
return []any{&e.ID, &e.AppID, &e.Key, &e.Value}
|
||||
}
|
||||
|
||||
// FindEnvVarsByAppID finds all env vars for an app.
|
||||
func FindEnvVarsByAppID(
|
||||
ctx context.Context,
|
||||
@@ -103,29 +138,7 @@ func FindEnvVarsByAppID(
|
||||
SELECT id, app_id, key, value FROM app_env_vars
|
||||
WHERE app_id = ? ORDER BY key`
|
||||
|
||||
rows, err := db.Query(ctx, query, appID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("querying env vars by app: %w", err)
|
||||
}
|
||||
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
var envVars []*EnvVar
|
||||
|
||||
for rows.Next() {
|
||||
envVar := NewEnvVar(db)
|
||||
|
||||
scanErr := rows.Scan(
|
||||
&envVar.ID, &envVar.AppID, &envVar.Key, &envVar.Value,
|
||||
)
|
||||
if scanErr != nil {
|
||||
return nil, scanErr
|
||||
}
|
||||
|
||||
envVars = append(envVars, envVar)
|
||||
}
|
||||
|
||||
return envVars, rows.Err()
|
||||
return findAllByAppID(ctx, db, query, appID, "env vars", NewEnvVar)
|
||||
}
|
||||
|
||||
// EnvVarPair is a key-value pair for bulk env var operations.
|
||||
|
||||
@@ -93,6 +93,10 @@ func FindLabel(
|
||||
return label, nil
|
||||
}
|
||||
|
||||
func (l *Label) scanDest() []any {
|
||||
return []any{&l.ID, &l.AppID, &l.Key, &l.Value}
|
||||
}
|
||||
|
||||
// FindLabelsByAppID finds all labels for an app.
|
||||
func FindLabelsByAppID(
|
||||
ctx context.Context,
|
||||
@@ -103,27 +107,7 @@ func FindLabelsByAppID(
|
||||
SELECT id, app_id, key, value FROM app_labels
|
||||
WHERE app_id = ? ORDER BY key`
|
||||
|
||||
rows, err := db.Query(ctx, query, appID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("querying labels by app: %w", err)
|
||||
}
|
||||
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
var labels []*Label
|
||||
|
||||
for rows.Next() {
|
||||
label := NewLabel(db)
|
||||
|
||||
scanErr := rows.Scan(&label.ID, &label.AppID, &label.Key, &label.Value)
|
||||
if scanErr != nil {
|
||||
return nil, scanErr
|
||||
}
|
||||
|
||||
labels = append(labels, label)
|
||||
}
|
||||
|
||||
return labels, rows.Err()
|
||||
return findAllByAppID(ctx, db, query, appID, "labels", NewLabel)
|
||||
}
|
||||
|
||||
// DeleteLabelsByAppID deletes all labels for an app.
|
||||
|
||||
@@ -317,33 +317,54 @@ func TestAllApps(t *testing.T) {
|
||||
|
||||
// EnvVar Tests.
|
||||
|
||||
// testKVCreateAndFind exercises the create-and-find round trip shared
|
||||
// by key-value models (env vars, labels).
|
||||
func testKVCreateAndFind[T any](
|
||||
t *testing.T,
|
||||
wantKey string,
|
||||
create func(db *database.Database, appID string) (int64, error),
|
||||
find func(context.Context, *database.Database, string) ([]T, error),
|
||||
keyOf func(T) string,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
testDB, cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
// Create app first.
|
||||
app := createTestApp(t, testDB)
|
||||
|
||||
id, err := create(testDB, app.ID)
|
||||
require.NoError(t, err)
|
||||
assert.NotZero(t, id)
|
||||
|
||||
found, err := find(context.Background(), testDB, app.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, found, 1)
|
||||
assert.Equal(t, wantKey, keyOf(found[0]))
|
||||
}
|
||||
|
||||
func saveTestEnvVar(db *database.Database, appID string) (int64, error) {
|
||||
envVar := models.NewEnvVar(db)
|
||||
envVar.AppID = appID
|
||||
envVar.Key = "DATABASE_URL"
|
||||
envVar.Value = "postgres://localhost/db"
|
||||
|
||||
err := envVar.Save(context.Background())
|
||||
|
||||
return envVar.ID, err
|
||||
}
|
||||
|
||||
func TestEnvVarCRUD(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("creates and finds env vars", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testDB, cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
// Create app first.
|
||||
app := createTestApp(t, testDB)
|
||||
|
||||
envVar := models.NewEnvVar(testDB)
|
||||
envVar.AppID = app.ID
|
||||
envVar.Key = "DATABASE_URL"
|
||||
envVar.Value = "postgres://localhost/db"
|
||||
|
||||
err := envVar.Save(context.Background())
|
||||
require.NoError(t, err)
|
||||
assert.NotZero(t, envVar.ID)
|
||||
|
||||
envVars, err := models.FindEnvVarsByAppID(
|
||||
context.Background(), testDB, app.ID,
|
||||
testKVCreateAndFind(t, "DATABASE_URL", saveTestEnvVar,
|
||||
models.FindEnvVarsByAppID,
|
||||
func(e *models.EnvVar) string { return e.Key },
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, envVars, 1)
|
||||
assert.Equal(t, "DATABASE_URL", envVars[0].Key)
|
||||
})
|
||||
|
||||
t.Run("deletes env var", func(t *testing.T) {
|
||||
@@ -375,32 +396,27 @@ func TestEnvVarCRUD(t *testing.T) {
|
||||
|
||||
// Label Tests.
|
||||
|
||||
func saveTestLabel(db *database.Database, appID string) (int64, error) {
|
||||
label := models.NewLabel(db)
|
||||
label.AppID = appID
|
||||
label.Key = "traefik.enable"
|
||||
label.Value = "true"
|
||||
|
||||
err := label.Save(context.Background())
|
||||
|
||||
return label.ID, err
|
||||
}
|
||||
|
||||
func TestLabelCRUD(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("creates and finds labels", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testDB, cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
app := createTestApp(t, testDB)
|
||||
|
||||
label := models.NewLabel(testDB)
|
||||
label.AppID = app.ID
|
||||
label.Key = "traefik.enable"
|
||||
label.Value = "true"
|
||||
|
||||
err := label.Save(context.Background())
|
||||
require.NoError(t, err)
|
||||
assert.NotZero(t, label.ID)
|
||||
|
||||
labels, err := models.FindLabelsByAppID(
|
||||
context.Background(), testDB, app.ID,
|
||||
testKVCreateAndFind(t, "traefik.enable", saveTestLabel,
|
||||
models.FindLabelsByAppID,
|
||||
func(l *models.Label) string { return l.Key },
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, labels, 1)
|
||||
assert.Equal(t, "traefik.enable", labels[0].Key)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -569,7 +585,9 @@ func TestDeploymentFindByAppID(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
deployments, err := models.FindDeploymentsByAppID(context.Background(), testDB, app.ID, 3)
|
||||
deployments, err := models.FindDeploymentsByAppID(
|
||||
context.Background(), testDB, app.ID, 3,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, deployments, 3)
|
||||
}
|
||||
@@ -706,7 +724,6 @@ func TestAppGetWebhookEvents(t *testing.T) {
|
||||
|
||||
// Cascade Delete Tests.
|
||||
|
||||
//nolint:funlen // Test function with many assertions - acceptable for integration tests
|
||||
func TestCascadeDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -783,7 +800,8 @@ func TestCascadeDelete(t *testing.T) {
|
||||
|
||||
// Resource Limits Tests.
|
||||
|
||||
func TestAppResourceLimits(t *testing.T) { //nolint:funlen // integration test with multiple subtests
|
||||
//nolint:funlen // integration test with multiple subtests
|
||||
func TestAppResourceLimits(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("saves and loads CPU limit", func(t *testing.T) {
|
||||
|
||||
@@ -112,6 +112,12 @@ func FindPort(
|
||||
return port, nil
|
||||
}
|
||||
|
||||
func (p *Port) scanDest() []any {
|
||||
return []any{
|
||||
&p.ID, &p.AppID, &p.HostPort, &p.ContainerPort, &p.Protocol,
|
||||
}
|
||||
}
|
||||
|
||||
// FindPortsByAppID finds all ports for an app.
|
||||
func FindPortsByAppID(
|
||||
ctx context.Context,
|
||||
@@ -122,30 +128,7 @@ func FindPortsByAppID(
|
||||
SELECT id, app_id, host_port, container_port, protocol
|
||||
FROM app_ports WHERE app_id = ? ORDER BY host_port`
|
||||
|
||||
rows, err := db.Query(ctx, query, appID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("querying ports by app: %w", err)
|
||||
}
|
||||
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
var ports []*Port
|
||||
|
||||
for rows.Next() {
|
||||
port := NewPort(db)
|
||||
|
||||
scanErr := rows.Scan(
|
||||
&port.ID, &port.AppID, &port.HostPort,
|
||||
&port.ContainerPort, &port.Protocol,
|
||||
)
|
||||
if scanErr != nil {
|
||||
return nil, scanErr
|
||||
}
|
||||
|
||||
ports = append(ports, port)
|
||||
}
|
||||
|
||||
return ports, rows.Err()
|
||||
return findAllByAppID(ctx, db, query, appID, "ports", NewPort)
|
||||
}
|
||||
|
||||
// DeletePortsByAppID deletes all ports for an app.
|
||||
|
||||
@@ -103,6 +103,12 @@ func FindVolume(
|
||||
return vol, nil
|
||||
}
|
||||
|
||||
func (v *Volume) scanDest() []any {
|
||||
return []any{
|
||||
&v.ID, &v.AppID, &v.HostPath, &v.ContainerPath, &v.ReadOnly,
|
||||
}
|
||||
}
|
||||
|
||||
// FindVolumesByAppID finds all volumes for an app.
|
||||
func FindVolumesByAppID(
|
||||
ctx context.Context,
|
||||
@@ -113,30 +119,7 @@ func FindVolumesByAppID(
|
||||
SELECT id, app_id, host_path, container_path, readonly
|
||||
FROM app_volumes WHERE app_id = ? ORDER BY container_path`
|
||||
|
||||
rows, err := db.Query(ctx, query, appID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("querying volumes by app: %w", err)
|
||||
}
|
||||
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
var volumes []*Volume
|
||||
|
||||
for rows.Next() {
|
||||
vol := NewVolume(db)
|
||||
|
||||
scanErr := rows.Scan(
|
||||
&vol.ID, &vol.AppID, &vol.HostPath,
|
||||
&vol.ContainerPath, &vol.ReadOnly,
|
||||
)
|
||||
if scanErr != nil {
|
||||
return nil, scanErr
|
||||
}
|
||||
|
||||
volumes = append(volumes, vol)
|
||||
}
|
||||
|
||||
return volumes, rows.Err()
|
||||
return findAllByAppID(ctx, db, query, appID, "volumes", NewVolume)
|
||||
}
|
||||
|
||||
// DeleteVolumesByAppID deletes all volumes for an app.
|
||||
|
||||
Reference in New Issue
Block a user