check / check (push) Canceled after 0s
Both tests run pixad as a child process. They fail against this code: every start writes the dependency-injection library's event log to stderr, and a startup error is printed twice inside its frames. Model: opus-5-5
117 lines
2.9 KiB
Go
117 lines
2.9 KiB
Go
// Package app reads the pixad command line and runs the server.
|
|
package app
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/spf13/cobra"
|
|
"go.uber.org/dig"
|
|
"go.uber.org/fx"
|
|
"sneak.berlin/go/pixa/internal/config"
|
|
"sneak.berlin/go/pixa/internal/database"
|
|
"sneak.berlin/go/pixa/internal/globals"
|
|
"sneak.berlin/go/pixa/internal/handlers"
|
|
"sneak.berlin/go/pixa/internal/healthcheck"
|
|
"sneak.berlin/go/pixa/internal/logger"
|
|
"sneak.berlin/go/pixa/internal/middleware"
|
|
"sneak.berlin/go/pixa/internal/providers"
|
|
"sneak.berlin/go/pixa/internal/server"
|
|
)
|
|
|
|
var configPath string //nolint:gochecknoglobals // cobra flag
|
|
|
|
// Run reads the command line and runs the server until it stops, with
|
|
// version as the version pixad logs and reports. It exits the process
|
|
// with status 1 when the command line is not valid.
|
|
func Run(version string) {
|
|
globals.Version = version
|
|
|
|
rootCmd := &cobra.Command{
|
|
Use: "pixad",
|
|
Short: "Pixa image caching proxy server",
|
|
Run: run,
|
|
}
|
|
|
|
rootCmd.Flags().StringVarP(&configPath, "config", "c", "", "path to config file")
|
|
|
|
err := rootCmd.Execute()
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func run(_ *cobra.Command, _ []string) {
|
|
// Set config path in environment if specified via flag
|
|
if configPath != "" {
|
|
_ = os.Setenv("PIXA_CONFIG_PATH", configPath)
|
|
}
|
|
|
|
// A write to a closed stdout or stderr must not end the process.
|
|
signal.Ignore(syscall.SIGPIPE)
|
|
|
|
app := fx.New(
|
|
// pixad logs with slog only; runApp prints a startup or shutdown
|
|
// error itself.
|
|
fx.NopLogger,
|
|
fx.Provide(
|
|
config.New,
|
|
database.New,
|
|
globals.New,
|
|
handlers.New,
|
|
logger.New,
|
|
server.New,
|
|
middleware.New,
|
|
healthcheck.New,
|
|
// The registry /metrics serves
|
|
func() prometheus.Registerer { return prometheus.DefaultRegisterer },
|
|
),
|
|
fx.Provide(providers.Constructors()...),
|
|
fx.Invoke(
|
|
func(log *logger.Logger) { log.Identify() },
|
|
func(*server.Server) {},
|
|
),
|
|
)
|
|
|
|
exitCode := runApp(app, os.Stderr)
|
|
if exitCode != 0 {
|
|
os.Exit(exitCode)
|
|
}
|
|
}
|
|
|
|
// runApp starts app, waits for a signal or a shutdown request, stops app,
|
|
// and returns the status pixad exits with, as fx.App.Run does. It prints
|
|
// a start or stop error to stderr, once, as the error that caused it:
|
|
// dig.RootCause drops the frames fx and dig wrap around it.
|
|
func runApp(app *fx.App, stderr io.Writer) int {
|
|
startCtx, cancelStart := context.WithTimeout(context.Background(), app.StartTimeout())
|
|
defer cancelStart()
|
|
|
|
err := app.Start(startCtx)
|
|
if err != nil {
|
|
_, _ = fmt.Fprintln(stderr, dig.RootCause(err))
|
|
|
|
return 1
|
|
}
|
|
|
|
shutdown := <-app.Wait()
|
|
|
|
stopCtx, cancelStop := context.WithTimeout(context.Background(), app.StopTimeout())
|
|
defer cancelStop()
|
|
|
|
err = app.Stop(stopCtx)
|
|
if err != nil {
|
|
_, _ = fmt.Fprintln(stderr, dig.RootCause(err))
|
|
|
|
return 1
|
|
}
|
|
|
|
return shutdown.ExitCode
|
|
}
|