Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ linters:
- goprintffuncname # checks that printf-like functions are named with f at the end
- gosec # inspects source code for security problems
- govet # reports suspicious constructs (go vet)
- importas # enforces one alias per import path (uber-go: Import Aliasing)
- ineffassign # detects when assignments to existing variables are not used
- lll # reports long lines
- loggercheck # checks key-value pairs for common logger libraries (zap, zerolog, ...)
Expand Down Expand Up @@ -70,6 +71,33 @@ linters:
govet:
enable:
- shadow # reports shadowed variables (disabled by default)
importas:
# Conflict-only map: every entry below is an import path the tree already
# aliases two or more different ways, pinned to the alias the majority of
# call sites already use. Packages with a single consistent alias are left
# out on purpose — listing them would add no findings today and only risk
# a version-stamped path (go.opentelemetry.io/otel/semconv/v1.NN.0) going
# stale and silently matching nothing after the next bump.
#
# `no-unaliased` and `no-extra-aliases` stay OFF: either one turns this
# 11-site cleanup into a ~40-file sweep over imports that read fine.
#
# net/http is deliberately absent — 56 unaliased sites against 7 nethttp
# and 6 stdhttp is not a convention to ratchet, it is a decision nobody
# has made yet.
alias:
- pkg: github.com/gaborage/go-bricks/database/testing
alias: dbtesting
- pkg: github.com/gaborage/go-bricks/jose/testing
alias: jositest
- pkg: github.com/go-jose/go-jose/v4
alias: jose
- pkg: github.com/rabbitmq/amqp091-go
alias: amqp
# metricznoop (3 sites) outnumbered metricnoop (2) and otelnoop (2), but
# it is a typo — the extra findings buy its deletion.
- pkg: go.opentelemetry.io/otel/metric/noop
alias: metricnoop
lll:
line-length: 215
misspell:
Expand Down Expand Up @@ -119,6 +147,7 @@ linters:
- name: atomic # non-atomic assignment to an atomic value
- name: bare-return # "Avoid Naked Returns"
- name: confusing-results # unnamed same-type multi-returns
- name: deep-exit # "Exit in Main": os.Exit/log.Fatal outside main/init
- name: early-return # "Reduce Nesting"
- name: defer # defer in loop, recover, return-in-defer
- name: identical-branches # if/else with identical bodies
Expand Down Expand Up @@ -178,6 +207,54 @@ linters:
- revive
path: "trace/"
text: "var-naming: avoid package names"
# cmd/seal-payload is the only file set that imports BOTH go-jose/v4 (whose
# package name is `jose`) and github.com/gaborage/go-bricks/jose. One of
# them must be aliased away, and the go-bricks package is the dominant one
# here — so the vendor import keeps `gojose`, which is exactly the
# collision-avoidance case uber-go's "Import Aliasing" prescribes. The
# importas entry still holds everywhere else.
- linters:
- importas
path: ^cmd/seal-payload/
text: 'go-jose/go-jose/v4'
paths:
- third_party$
- builtin$
# Formatters are a separate top-level block in golangci-lint v2 — listing gofumpt
# or gci under `linters.enable` is a hard config error ("can't load config:
# gofumpt is a formatter"). `golangci-lint run` still reports their output as
# ordinary issues ("File is not properly formatted (gci)"), so `make fmt` must
# invoke `golangci-lint fmt`; `go fmt` cannot fix either of these.
formatters:
enable:
- gci # uber-go: "Import Grouping"
- gofumpt # uber-go: "Group Similar Declarations" and the rest of gofmt's stricter superset
settings:
gci:
# Section ORDER is the decision here, and it is deliberate. gci's natural
# order already puts `default` (third party) ahead of a `prefix` group,
# which is how most of this tree was written by hand. Measured against the
# pre-adoption tree at v2.12.2:
# standard, default, prefix(go-bricks) -> 70 files / 290 lines
# standard, default (no prefix section) -> 138 files / 589 lines
# standard, prefix(go-bricks), default -> 199 files / 806 lines
# (that third one needs custom-order: true; without it gci normalises
# the sections straight back to the first order)
# Dropping the prefix section is not merely 2x the churn: re-running the
# no-prefix order against the formatted tree rewrites 197 files, MERGING
# third-party imports back into the go-bricks block in every one. Do not
# "simplify" this by removing the prefix section or hoisting it above
# default.
sections:
- standard
- default
- prefix(github.com/gaborage/go-bricks)
gofumpt:
# extra-rules was measured (+24 lines over the base rules) and rejected: it
# implements no rule the uber-go guide asks for.
extra-rules: false
exclusions:
generated: lax
paths:
- third_party$
- builtin$
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Thank you for your interest in contributing to GoBricks! This document provides

### Code Quality Standards

- **Formatting**: Use `make fmt` to format code with `go fmt`
- **Formatting**: Use `make fmt` to format code with `golangci-lint fmt` (gofmt + gofumpt + gci)
- **Linting**: Code must pass `make lint` (golangci-lint)
- **Testing**: Add tests for new functionality and ensure `make test` passes
- **Security**: Code must pass `gosec` security checks
Expand Down
13 changes: 11 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,17 @@ lint: ## Run golangci-lint (pinned + GOWORK=off, mirroring CI; LINT_CLEAN=1 wipe
lint-md: ## Run markdownlint-cli2 on Markdown files (pinned; globs and ignores come from .markdownlint-cli2.jsonc)
npx --yes markdownlint-cli2@$(MARKDOWNLINT_VERSION)

fmt: ## Format Go code
go fmt ./...
# `golangci-lint fmt`, not `go fmt`: .golangci.yml declares a formatters block
# (gofumpt + gci) and `golangci-lint run` reports its output as ordinary issues
# ("File is not properly formatted (gci)"). `go fmt` cannot fix either one, so
# with it here `make check` — which is `fmt lint ...` — would reformat and then
# fail lint anyway. Same pinned binary as the `lint` target so both agree on the
# rules — but NOT on the file set: `fmt` reaches //go:build integration files
# (5 needed reformatting at adoption) while `run` does not, neither here nor in
# CI, since no lint job passes -tags=integration. So `run` never fails on drift
# in those files and this target is the only thing that keeps them formatted.
fmt: ## Format Go code (gofmt + gofumpt + gci, per .golangci.yml's formatters block)
GOWORK=off go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) fmt

update: ## Update dependencies to latest versions
go get -u ./...
Expand Down
1 change: 0 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,6 @@ func NewWithConfig(cfg *config.Config, opts *Options) (*App, logger.Logger, erro
RegisterClosers().
RegisterReadyHandler().
Build()

if err != nil {
// Return the logger from builder if available, otherwise create bootstrap logger
if log == nil {
Expand Down
5 changes: 3 additions & 2 deletions app/app_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/gaborage/go-bricks/cache"
cachetesting "github.com/gaborage/go-bricks/cache/testing"
"github.com/gaborage/go-bricks/config"
"github.com/gaborage/go-bricks/database"
"github.com/gaborage/go-bricks/logger"
"github.com/gaborage/go-bricks/messaging"
testmocks "github.com/gaborage/go-bricks/testing/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const (
Expand Down
25 changes: 17 additions & 8 deletions app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,10 @@ func (m *MockSchedulerModule) MonthlyAt(jobID string, job any, dayOfMonth int, l
return m.Called(jobID, job, dayOfMonth, localTime).Error(0)
}

var _ Module = (*MockSchedulerModule)(nil)
var _ JobRegistrar = (*MockSchedulerModule)(nil)
var (
_ Module = (*MockSchedulerModule)(nil)
_ JobRegistrar = (*MockSchedulerModule)(nil)
)

// MockJobProviderModule implements Module + JobProvider for testing job registration
type MockJobProviderModule struct {
Expand Down Expand Up @@ -349,8 +351,10 @@ func (m *MockJobProviderModule) RegisterJobs(registrar JobRegistrar) error {
return m.Called(registrar).Error(0)
}

var _ Module = (*MockJobProviderModule)(nil)
var _ JobProvider = (*MockJobProviderModule)(nil)
var (
_ Module = (*MockJobProviderModule)(nil)
_ JobProvider = (*MockJobProviderModule)(nil)
)

// MockKeyStoreModule implements Module + KeyStoreProvider for testing keystore wiring
type MockKeyStoreModule struct {
Expand Down Expand Up @@ -379,8 +383,10 @@ func (m *MockKeyStoreModule) KeyStore() KeyStore {
return m.keyStore
}

var _ Module = (*MockKeyStoreModule)(nil)
var _ KeyStoreProvider = (*MockKeyStoreModule)(nil)
var (
_ Module = (*MockKeyStoreModule)(nil)
_ KeyStoreProvider = (*MockKeyStoreModule)(nil)
)

// stubKeyStore is a minimal KeyStore implementation for testing wiring only
type stubKeyStore struct{}
Expand Down Expand Up @@ -686,8 +692,10 @@ func (m *sharedResolverModule) SetSharedResolvers(
m.msg = msg
}

var _ Module = (*sharedResolverModule)(nil)
var _ sharedResolverSetter = (*sharedResolverModule)(nil)
var (
_ Module = (*sharedResolverModule)(nil)
_ sharedResolverSetter = (*sharedResolverModule)(nil)
)

// TestRegisterModuleInjectsSharedResolvers pins the Step-3 wiring: RegisterModule
// must inject non-nil shared ("" key) DB/messaging resolvers into any module
Expand Down Expand Up @@ -1714,6 +1722,7 @@ func (m *describerModule) DescribeModule() ModuleDescriptor {
Version: "1.0.0",
}
}

func (m *describerModule) DescribeRoutes() []server.RouteDescriptor {
return []server.RouteDescriptor{}
}
16 changes: 9 additions & 7 deletions app/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@ import (
"testing"
"time"

"github.com/gaborage/go-bricks/config"
"github.com/gaborage/go-bricks/logger"
"github.com/gaborage/go-bricks/observability"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/metric"
metricnoop "go.opentelemetry.io/otel/metric/noop"
sdklog "go.opentelemetry.io/otel/sdk/log"
"go.opentelemetry.io/otel/trace"
tracenoop "go.opentelemetry.io/otel/trace/noop"

"github.com/gaborage/go-bricks/config"
"github.com/gaborage/go-bricks/logger"
"github.com/gaborage/go-bricks/observability"
)

const (
Expand Down Expand Up @@ -158,6 +159,7 @@ type mockLogEvent struct{}
func (e *mockLogEvent) Msg(string) {
// No-op
}

func (e *mockLogEvent) Msgf(string, ...any) {
// No-op
}
Expand Down Expand Up @@ -279,7 +281,7 @@ observability:
// Create temporary directory and config file
tmpDir := t.TempDir()
configPath := filepath.Join(tmpDir, testConfigFile)
err := os.WriteFile(configPath, []byte(yamlContent), 0600)
err := os.WriteFile(configPath, []byte(yamlContent), 0o600)
require.NoError(t, err)

// Change to temp directory to load config
Expand Down Expand Up @@ -379,7 +381,7 @@ observability:
// Create temporary directory and config file
tmpDir := t.TempDir()
configPath := filepath.Join(tmpDir, testConfigFile)
err := os.WriteFile(configPath, []byte(yamlContent), 0600)
err := os.WriteFile(configPath, []byte(yamlContent), 0o600)
require.NoError(t, err)

// Set environment variables to override config
Expand Down Expand Up @@ -466,7 +468,7 @@ observability:
// Create temporary directory and config file
tmpDir := t.TempDir()
configPath := filepath.Join(tmpDir, testConfigFile)
err := os.WriteFile(configPath, []byte(yamlContent), 0600)
err := os.WriteFile(configPath, []byte(yamlContent), 0o600)
require.NoError(t, err)

// Change to temp directory to load config
Expand Down Expand Up @@ -525,7 +527,7 @@ debug:
// Create temporary directory and config file
tmpDir := t.TempDir()
configPath := filepath.Join(tmpDir, testConfigFile)
err := os.WriteFile(configPath, []byte(yamlContent), 0600)
err := os.WriteFile(configPath, []byte(yamlContent), 0o600)
require.NoError(t, err)

// Change to temp directory to load config
Expand Down
4 changes: 2 additions & 2 deletions app/debug_health_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"github.com/gaborage/go-bricks/cache"
"github.com/gaborage/go-bricks/config"
"github.com/gaborage/go-bricks/database"
dbtest "github.com/gaborage/go-bricks/database/testing"
dbtesting "github.com/gaborage/go-bricks/database/testing"
"github.com/gaborage/go-bricks/logger"
"github.com/gaborage/go-bricks/messaging"
"github.com/gaborage/go-bricks/server"
Expand Down Expand Up @@ -472,7 +472,7 @@ func TestHealthDebugKeepsPooledConnectionKeysWhileReadyOmitsThem(t *testing.T) {

log := &recLogger{}
connector := func(*config.DatabaseConfig, logger.Logger) (database.Interface, error) {
return dbtest.NewTestDB(dbTypePostgres), nil
return dbtesting.NewTestDB(dbTypePostgres), nil
}
dbManager := database.NewDbManager(&stubTenantResource{}, log,
database.DbManagerOptions{MaxSize: 5, IdleTTL: time.Hour}, connector)
Expand Down
5 changes: 3 additions & 2 deletions app/factory_resolver_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

cachepkg "github.com/gaborage/go-bricks/cache"
"github.com/gaborage/go-bricks/config"
"github.com/gaborage/go-bricks/logger"
"github.com/gaborage/go-bricks/testing/containers"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestFactoryResolverRedisConnectorIntegration tests the Redis cache connector
Expand Down
3 changes: 2 additions & 1 deletion app/factory_resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import (
"testing"
"time"

"github.com/stretchr/testify/assert"

"github.com/gaborage/go-bricks/cache"
"github.com/gaborage/go-bricks/config"
"github.com/gaborage/go-bricks/logger"
"github.com/stretchr/testify/assert"
)

const (
Expand Down
5 changes: 3 additions & 2 deletions app/health_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/gaborage/go-bricks/cache"
cachetesting "github.com/gaborage/go-bricks/cache/testing"
"github.com/gaborage/go-bricks/config"
Expand All @@ -15,8 +18,6 @@ import (
"github.com/gaborage/go-bricks/logger"
"github.com/gaborage/go-bricks/messaging"
testmocks "github.com/gaborage/go-bricks/testing/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// Note: Since the health probe functions work with concrete types (*database.DbManager, *messaging.Manager),
Expand Down
4 changes: 2 additions & 2 deletions app/lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

"github.com/gaborage/go-bricks/config"
"github.com/gaborage/go-bricks/database"
dbtest "github.com/gaborage/go-bricks/database/testing"
dbtesting "github.com/gaborage/go-bricks/database/testing"
"github.com/gaborage/go-bricks/logger"
"github.com/gaborage/go-bricks/messaging"
"github.com/gaborage/go-bricks/server"
Expand Down Expand Up @@ -506,7 +506,7 @@ func TestStartMaintenanceLoopsUsesConfiguredDatabaseCleanupInterval(t *testing.T
log := logger.New("error", false)

connector := func(*config.DatabaseConfig, logger.Logger) (database.Interface, error) {
return dbtest.NewTestDB("postgresql"), nil
return dbtesting.NewTestDB("postgresql"), nil
}
dbManager := database.NewDbManager(&stubTenantResource{}, log, database.DbManagerOptions{MaxSize: 5, IdleTTL: 10 * time.Millisecond}, connector)
defer func() { _ = dbManager.Close() }()
Expand Down
5 changes: 3 additions & 2 deletions app/managers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/gaborage/go-bricks/cache"
"github.com/gaborage/go-bricks/config"
"github.com/gaborage/go-bricks/database"
"github.com/gaborage/go-bricks/logger"
"github.com/gaborage/go-bricks/messaging"
testmocks "github.com/gaborage/go-bricks/testing/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const (
Expand Down
Loading