stdfx

package module
v0.1.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 24, 2025 License: Apache-2.0 Imports: 19 Imported by: 2

README

stdfx

Go Reference Actions Status

Documentation

Description

stdfx provides standard functionality for golang apps built using uber-go/fx.

By using stdfx as an application starter you benefit from:

  • common app interface
  • cli arguments to adjust behavior
  • config file discovery and parsing
  • override config using environment variables
  • builtin cobra subcommands like config or version
  • configurable structured logging

See examples/webserver to test and experience it in action.

It acts as a demo for every stdfx feature.

Usage example

A minimal usage might look like this:

package main

import (
	"go.uber.org/fx"
	"github.com/choopm/stdfx"
	"github.com/choopm/stdfx/configfx"
	"github.com/choopm/stdfx/loggingfx/zerologfx"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"github.com/spf13/cobra"
)

// version is provided by `-ldflags "-X main.version=1.0.0"`
var version string = "unknown"

func main() {
	fx.New(
		// logging
		zerologfx.Module,
		fx.WithLogger(zerologfx.ToFx),
		fx.Decorate(zerologfx.Decorator[yourapp.Config]),

		// viper configuration
		fx.Provide(stdfx.ConfigFile[yourapp.Config]("yourapp")),

		// cobra commands
		fx.Provide(
			stdfx.AutoRegister(stdfx.VersionCommand(version)),
			stdfx.AutoRegister(stdfx.ConfigCommand[yourapp.Config]),
			stdfx.AutoRegister(yourCobraCommand),
			stdfx.AutoCommand, // add registered commands to root
		),

		// app start
		fx.Invoke(stdfx.ContainerEntrypoint("*")), // program is container entrypoint
		fx.Invoke(stdfx.Unprivileged), // abort when being run as root
		fx.Invoke(stdfx.Commander),    // run root cobra command
	).Run()
}

// yourCobraCommand returns a *cobra.Command to start the server from a ConfigProvider
func yourCobraCommand(
	configProvider configfx.Provider[yourapp.Config],
) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "server",
		Short: "server starts the server",
		RunE: func(cmd *cobra.Command, args []string) error {
			// fetch the config
			cfg, err := configProvider.Config()
			if err != nil {
				return err
			}

			// rebuild logger and make it global
			logger, err := zerologfx.New(cfg.Logging)
			if err != nil {
				return err
			}
			log.Logger = *logger

			// create server instance
			server, err := yourapp.NewServer(cfg, logger)
			if err != nil {
				return err
			}

			// start server using context
			return server.Start(cmd.Context())
		},
	}

	return cmd
}

Development

Dev container

Open this project in Visual Studio Code and select to reopen it inside a dev container.

If you experience any issues, make sure your IDE supports dev containers: https://code.visualstudio.com/docs/devcontainers/containers

Tasks

This project uses task.

Run task --list to list all available tasks.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var AppVersion = "unknown"

AppVersion is the version given to VersionCommand

View Source
var AutoCommand = fx.Annotate(
	newRootCommand,
	fx.ParamTags(`group:"commands"`),
)

AutoCommand is an annotated version of NewRootCommand which passes anything previously called with AutoRegister to an annotated version of NewRootCommand. Usage example:

fx.Provide(
	stdfx.AutoRegister(firstCommandConstructor),
	stdfx.AutoRegister(secondCommandConstructor),
	stdfx.AutoCommand,
),
fx.Invoke(stdfx.Commander),
View Source
var ContainerEntrypointDefaultTools = []string{"sh", "/bin/sh", "bash", "/bin/bash"}

ContainerEntrypointDefaultTools are the default tools for ContainerEntrypoint

View Source
var ErrContextMissingShutdowner = errors.New("context is missing shutdowner")

ErrContextMissingShutdowner can be returned by Shutdown

View Source
var (
	// ErrRunningAsRoot can be returned by [Unprivileged]
	ErrRunningAsRoot = errors.New("running as root is dangerous and prohibited")
)

Functions

func AutoRegister

func AutoRegister(f any) any

AutoRegister annotates a *cobra.Command constructor f to be automatically registered as a sub command in NewRootCommand. Usage example:

fx.Provide(
	stdfx.AutoRegister(firstCommandConstructor),
	stdfx.AutoRegister(secondCommandConstructor),
	stdfx.AutoCommand,
),
fx.Invoke(stdfx.Commander),

func Commander

func Commander(
	lc fx.Lifecycle,
	shutdowner fx.Shutdowner,
	cmd *cobra.Command,
)

Commander can be used as a *cobra.Command invoker for fx. It will start cmd with Context.Background() in a goroutine. It is typically used as last Invoke option in an fx.App to actually start the application using a previously provided root *cobra.Command. The started *cobra.Command shall use cmd.Context() to watch for Done(). The ctx of cmd.Context() will be cancelled when it is time to shutdown. Failure to track cmd.Context() will kill your application after fx.DefaultTimeout - 15 seconds. fx.Lifecycle and fx.Shutdowner are injected into cmd.Context() and can be retrieved by calling [ExtractFromContext].

func ConfigCommand

func ConfigCommand[T any](
	log *slog.Logger,
	configProvider configfx.Provider[T],
) *cobra.Command

ConfigCommand is a *cobra.Command constructor to print, modify and validate config.

func ConfigFile

func ConfigFile[T any](
	configName string,
) func(log *slog.Logger) configfx.Provider[T]

ConfigFile provides your fx.App with a ConfigProvider[T] constructor. The provider - when being constructed - can be used to search for, read and unmarshal your config file to a struct of type *T or error. [configName] shall be the name of a config file without extension. Internally this curries both functions [config.NewSourceFile] and [config.NewProvider] for syntactic sugar. Usage example:

fx.Provide(stdfx.Config[mypkg.ConfStruct]("configname")),

After providing as described above, you will be able to request this provider to fetch the actual config *struct like this:

func buildCommand(
	provider stdfx.ConfigProvider[mypkg.ConfStruct],
) *cobra.Command {
	// fetch the config of type *mypkg.ConfStruct
	cfg, err := provider.Config()
	if err != nil {
		// ...
	}
	// do something using cfg (construct a *cobra.Command for example)
}

func ContainerEntrypoint added in v0.1.8

func ContainerEntrypoint(tools ...string) func()

ContainerEntrypoint might be used with fx.Invoke and tooling when the calling go program is packaged into a container where it is used as the entrypoint. This will execute any value of `tools` when given as the first argument and if found in $PATH. If tools is empty it will use a default list: ContainerEntrypointDefaultTools. A special value of '*' allows for any tool. There is extra handling when the first argument is the binary name itself: For such cases that argument is silbently shifted out and execution continues.

Example usage:

  • fx.Invoke(stdfx.ContainerEntrypoint())
  • fx.Invoke(stdfx.ContainerEntrypoint("sh", "bash", "whoami"))
  • fx.Invoke(stdfx.ContainerEntrypoint("*"))

Resulting container image invocations:

  • docker run --rm -it ghcr.io/choopm/myproject:latest sh -c 'echo hello world'
  • docker run --rm -it ghcr.io/choopm/myproject:latest bash -i
  • docker run --rm -it ghcr.io/choopm/myproject:latest whoami
  • docker run --rm -it ghcr.io/choopm/myproject:latest myproject -c ...

func Shutdown

func Shutdown(ctx context.Context, exitCode int) error

Shutdown uses fx.Shutdowner from ctx to shutdown a fx.App using exitCode. This works when Commander was used to start it. You can use this to shutdown an application unaware of fx during runtime. It might return ErrContextMissingShutdowner in which case it is up to you to directly call os.Exit or panic.

func Unprivileged

func Unprivileged() error

Unprivileged returns an error if being run as root. This takes effect whenever the real or effective user id of the current user process is 0.

func UnprivilegedWarn

func UnprivilegedWarn(log *slog.Logger)

UnprivilegedWarn warns if being run as root. This takes effect whenever the real or effective user id of the current user process is 0.

func VersionCommand

func VersionCommand(version string) func(log *slog.Logger) *cobra.Command

VersionCommand a version *cobra.Command constructor to print version information. Supply your build tag as version and it will add runtime and compiler details.

Types

This section is empty.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL