refactor: use pinned golangci-lint Docker image for linting
All checks were successful
check / check (push) Successful in 1m37s
All checks were successful
check / check (push) Successful in 1m37s
Refactor Dockerfile to use a separate lint stage with a pinned golangci-lint v2.11.3 Docker image instead of installing golangci-lint via curl in the builder stage. This follows the pattern used by sneak/pixa. Changes: - Dockerfile: separate lint stage using golangci/golangci-lint:v2.11.3 (Debian-based, pinned by sha256) with COPY --from=lint dependency - Bump Go from 1.24 to 1.26.1 (golang:1.26.1-bookworm, pinned) - Bump golangci-lint from v1.64.8 to v2.11.3 - Migrate .golangci.yml from v1 to v2 format (same linters, format only) - All Docker images pinned by sha256 digest - Fix all lint issues from the v2 linter upgrade: - Add package comments to all packages - Add doc comments to all exported types, functions, and methods - Fix unchecked errors (errcheck) - Fix unused parameters (revive) - Fix gosec warnings (MaxBytesReader for form parsing) - Fix staticcheck suggestions (fmt.Fprintf instead of WriteString) - Rename DeliveryTask to Task to avoid stutter (delivery.Task) - Rename shadowed builtin 'max' parameter - Update README.md version requirements
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
// Package server wires up HTTP routes and manages the
|
||||
// application lifecycle.
|
||||
package server
|
||||
|
||||
import (
|
||||
@@ -21,9 +23,20 @@ import (
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
// nolint:revive // ServerParams is a standard fx naming convention
|
||||
const (
|
||||
// shutdownTimeout is the maximum time to wait for the HTTP
|
||||
// server to finish in-flight requests during shutdown.
|
||||
shutdownTimeout = 5 * time.Second
|
||||
|
||||
// sentryFlushTimeout is the maximum time to wait for Sentry
|
||||
// to flush pending events during shutdown.
|
||||
sentryFlushTimeout = 2 * time.Second
|
||||
)
|
||||
|
||||
//nolint:revive // ServerParams is a standard fx naming convention.
|
||||
type ServerParams struct {
|
||||
fx.In
|
||||
|
||||
Logger *logger.Logger
|
||||
Globals *globals.Globals
|
||||
Config *config.Config
|
||||
@@ -31,12 +44,13 @@ type ServerParams struct {
|
||||
Handlers *handlers.Handlers
|
||||
}
|
||||
|
||||
// Server is the main HTTP server that wires up routes and manages
|
||||
// graceful shutdown.
|
||||
type Server struct {
|
||||
startupTime time.Time
|
||||
exitCode int
|
||||
sentryEnabled bool
|
||||
log *slog.Logger
|
||||
ctx context.Context
|
||||
cancelFunc context.CancelFunc
|
||||
httpServer *http.Server
|
||||
router *chi.Mux
|
||||
@@ -45,6 +59,8 @@ type Server struct {
|
||||
h *handlers.Handlers
|
||||
}
|
||||
|
||||
// New creates a Server that starts the HTTP listener on fx start
|
||||
// and stops it gracefully.
|
||||
func New(lc fx.Lifecycle, params ServerParams) (*Server, error) {
|
||||
s := new(Server)
|
||||
s.params = params
|
||||
@@ -53,19 +69,23 @@ func New(lc fx.Lifecycle, params ServerParams) (*Server, error) {
|
||||
s.log = params.Logger.Get()
|
||||
|
||||
lc.Append(fx.Hook{
|
||||
OnStart: func(ctx context.Context) error {
|
||||
OnStart: func(_ context.Context) error {
|
||||
s.startupTime = time.Now()
|
||||
go s.Run()
|
||||
|
||||
return nil
|
||||
},
|
||||
OnStop: func(ctx context.Context) error {
|
||||
s.cleanShutdown()
|
||||
s.cleanShutdown(ctx)
|
||||
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Run configures Sentry and starts serving HTTP requests.
|
||||
func (s *Server) Run() {
|
||||
s.configure()
|
||||
|
||||
@@ -75,6 +95,12 @@ func (s *Server) Run() {
|
||||
s.serve()
|
||||
}
|
||||
|
||||
// MaintenanceMode returns whether the server is in maintenance
|
||||
// mode.
|
||||
func (s *Server) MaintenanceMode() bool {
|
||||
return s.params.Config.MaintenanceMode
|
||||
}
|
||||
|
||||
func (s *Server) enableSentry() {
|
||||
s.sentryEnabled = false
|
||||
|
||||
@@ -83,29 +109,37 @@ func (s *Server) enableSentry() {
|
||||
}
|
||||
|
||||
err := sentry.Init(sentry.ClientOptions{
|
||||
Dsn: s.params.Config.SentryDSN,
|
||||
Release: fmt.Sprintf("%s-%s", s.params.Globals.Appname, s.params.Globals.Version),
|
||||
Dsn: s.params.Config.SentryDSN,
|
||||
Release: fmt.Sprintf(
|
||||
"%s-%s",
|
||||
s.params.Globals.Appname,
|
||||
s.params.Globals.Version,
|
||||
),
|
||||
})
|
||||
if err != nil {
|
||||
s.log.Error("sentry init failure", "error", err)
|
||||
// Don't use fatal since we still want the service to run
|
||||
return
|
||||
}
|
||||
|
||||
s.log.Info("sentry error reporting activated")
|
||||
s.sentryEnabled = true
|
||||
}
|
||||
|
||||
func (s *Server) serve() int {
|
||||
s.ctx, s.cancelFunc = context.WithCancel(context.Background())
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
s.cancelFunc = cancelFunc
|
||||
|
||||
// signal watcher
|
||||
go func() {
|
||||
c := make(chan os.Signal, 1)
|
||||
|
||||
signal.Ignore(syscall.SIGPIPE)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
// block and wait for signal
|
||||
sig := <-c
|
||||
s.log.Info("signal received", "signal", sig.String())
|
||||
|
||||
if s.cancelFunc != nil {
|
||||
// cancelling the main context will trigger a clean
|
||||
// shutdown via the fx OnStop hook.
|
||||
@@ -115,9 +149,9 @@ func (s *Server) serve() int {
|
||||
|
||||
go s.serveUntilShutdown()
|
||||
|
||||
<-s.ctx.Done()
|
||||
<-ctx.Done()
|
||||
// Shutdown is handled by the fx OnStop hook (cleanShutdown).
|
||||
// Do not call cleanShutdown() here to avoid a double invocation.
|
||||
// Do not call cleanShutdown() here to avoid double invocation.
|
||||
return s.exitCode
|
||||
}
|
||||
|
||||
@@ -125,27 +159,29 @@ func (s *Server) cleanupForExit() {
|
||||
s.log.Info("cleaning up")
|
||||
}
|
||||
|
||||
func (s *Server) cleanShutdown() {
|
||||
func (s *Server) cleanShutdown(ctx context.Context) {
|
||||
// initiate clean shutdown
|
||||
s.exitCode = 0
|
||||
ctxShutdown, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
|
||||
ctxShutdown, shutdownCancel := context.WithTimeout(
|
||||
ctx, shutdownTimeout,
|
||||
)
|
||||
defer shutdownCancel()
|
||||
|
||||
if err := s.httpServer.Shutdown(ctxShutdown); err != nil {
|
||||
s.log.Error("server clean shutdown failed", "error", err)
|
||||
err := s.httpServer.Shutdown(ctxShutdown)
|
||||
if err != nil {
|
||||
s.log.Error(
|
||||
"server clean shutdown failed", "error", err,
|
||||
)
|
||||
}
|
||||
|
||||
s.cleanupForExit()
|
||||
|
||||
if s.sentryEnabled {
|
||||
sentry.Flush(2 * time.Second)
|
||||
sentry.Flush(sentryFlushTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) MaintenanceMode() bool {
|
||||
return s.params.Config.MaintenanceMode
|
||||
}
|
||||
|
||||
func (s *Server) configure() {
|
||||
// identify ourselves in the logs
|
||||
s.params.Logger.Identify()
|
||||
|
||||
Reference in New Issue
Block a user