refactor: make init logic more lazy and less verbose
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
- implement most services that need further initialization as lazy.Lazy - make config structure less dynamic and more explicit - it's always .buildr for the configs
This commit is contained in:
parent
c118822469
commit
3de7016a50
|
@ -1,8 +1,3 @@
|
|||
locals {
|
||||
linux_archs = toset(["amd64", "arm64"])
|
||||
macos_archs = toset(["arm64"])
|
||||
}
|
||||
|
||||
build "go_build" "linux" {
|
||||
for_each = local.linux_archs
|
||||
|
||||
|
@ -20,7 +15,7 @@ build "go_build" "linux" {
|
|||
|
||||
ldflags = [
|
||||
"-w -s",
|
||||
"-X 'code.icb4dc0.de/buildr/buildr/cmd.CurrentVersion=${vcs.tag == "" ? "v0.1.0" : vcs.tag}'"
|
||||
"-X 'code.icb4dc0.de/buildr/buildr/cmd.CurrentVersion=${vcs.tag == "" ? local.default_version : vcs.tag}'"
|
||||
]
|
||||
|
||||
environment = {
|
||||
|
@ -49,7 +44,7 @@ build "go_build" "darwin" {
|
|||
|
||||
ldflags = [
|
||||
"-w -s",
|
||||
"-X 'code.icb4dc0.de/buildr/buildr/cmd.CurrentVersion=${vcs.tag == "" ? vcs.branch : vcs.tag}'"
|
||||
"-X 'code.icb4dc0.de/buildr/buildr/cmd.CurrentVersion=${vcs.tag == "" ? local.default_version : vcs.tag}'"
|
||||
]
|
||||
|
||||
environment = {
|
||||
|
|
|
@ -7,4 +7,11 @@ buildr {
|
|||
url = "https://code.icb4dc0.de/api/packages/buildr/generic/golang_plugin/0.0.2/golang.wasm"
|
||||
checksum = "d82de8973447e036d4cc26a76f7b6ac8bc78afc3abdf49fcc31553ba5e119e46"
|
||||
}
|
||||
}
|
||||
|
||||
locals {
|
||||
linux_archs = toset(["amd64", "arm64"])
|
||||
macos_archs = toset(["arm64"])
|
||||
|
||||
default_version = "v0.1.0"
|
||||
}
|
|
@ -15,23 +15,10 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type InitLevel int
|
||||
|
||||
const (
|
||||
InitLevelNone InitLevel = iota
|
||||
InitLevelBasic
|
||||
InitLevelBuildRConfig
|
||||
InitLevelParseConfig
|
||||
)
|
||||
|
||||
const (
|
||||
defaultPasswordLength = 32
|
||||
)
|
||||
|
||||
type LevelInitializer interface {
|
||||
InitAt(ctx context.Context, lvl InitLevel) error
|
||||
}
|
||||
|
||||
type VaultInitConfig struct {
|
||||
PassphraseLength uint `mapstructure:"vault-pw-length"`
|
||||
}
|
||||
|
@ -50,7 +37,7 @@ func (c *VaultInitConfig) Flags() *flag.FlagSet {
|
|||
}
|
||||
|
||||
type BuildrConfigAccessor interface {
|
||||
BuildrConfig() config.Buildr
|
||||
BuildrConfig() (*config.Buildr, error)
|
||||
}
|
||||
|
||||
type AppConfigAccessor interface {
|
||||
|
@ -62,15 +49,15 @@ type TypeRegistryAccessor interface {
|
|||
}
|
||||
|
||||
type PluginsRepoAccessor interface {
|
||||
PluginsRepo() state.Plugins
|
||||
PluginsRepo() (state.Plugins, error)
|
||||
}
|
||||
|
||||
type ModuleRepositoryAccessor interface {
|
||||
Repository() *modules.Repository
|
||||
Repository() (*modules.Repository, error)
|
||||
}
|
||||
|
||||
type VaultAccessor interface {
|
||||
Vault() *vault.Vault
|
||||
Vault() (*vault.Vault, error)
|
||||
}
|
||||
|
||||
type VaultModifier interface {
|
||||
|
@ -115,6 +102,10 @@ type ManCommander interface {
|
|||
DisplayModulesManual(pager Pager) error
|
||||
}
|
||||
|
||||
type RunToolCommander interface {
|
||||
RunTool(ctx context.Context, name string, args []string) error
|
||||
}
|
||||
|
||||
type KnownTasksArgProviderFunc func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective)
|
||||
|
||||
func (f KnownTasksArgProviderFunc) ValidTasksArgs(
|
||||
|
@ -146,5 +137,5 @@ func (f AppInitializerFunc) Init(ctx context.Context) error {
|
|||
}
|
||||
|
||||
type EnvCommander interface {
|
||||
PrintPath(ctx context.Context, writer io.Writer) error
|
||||
PrintPath(writer io.Writer) error
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/client"
|
||||
|
@ -53,14 +55,20 @@ func init() {
|
|||
}
|
||||
}
|
||||
|
||||
func NewApp() *App {
|
||||
func NewApp(ctx context.Context) *App {
|
||||
app := &App{
|
||||
rootCmd: &cobra.Command{
|
||||
Use: "buildr",
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
},
|
||||
buildrCfg: new(config.Buildr),
|
||||
services: appServices{
|
||||
registry: modules.NewRegistry(
|
||||
task.Registration,
|
||||
packaging.Registration,
|
||||
),
|
||||
dockerClient: lazy.NewLazyCtx(ctx, prepareDockerAPIClient),
|
||||
},
|
||||
pluginMgr: &plugins.Manager{
|
||||
Downloader: plugins.GenericDownloader{
|
||||
"http": plugins.HTTPDownloader{},
|
||||
|
@ -71,20 +79,21 @@ func NewApp() *App {
|
|||
loggingCfg: logging.NewConfig(),
|
||||
}
|
||||
|
||||
app.buildrCfg = lazy.NewLazyCtx(ctx, app.prepareBuildrConfig)
|
||||
app.services.vault = lazy.NewLazy(app.appCfg.InitVault)
|
||||
app.services.stateDB = lazy.NewLazyCtx(ctx, app.appCfg.InitState)
|
||||
app.services.stateStore = lazy.NewLazy(app.prepareStateStore)
|
||||
app.services.cache = lazy.NewLazy(app.prepareCache)
|
||||
app.services.repo = lazy.NewLazyCtx(ctx, app.prepareModulesRepo)
|
||||
|
||||
app.rootCmd.PersistentPreRunE = func(cmd *cobra.Command, _ []string) error {
|
||||
return app.Init(cmd.Context())
|
||||
return app.Init()
|
||||
}
|
||||
|
||||
app.rootCmd.PersistentPostRunE = func(*cobra.Command, []string) error {
|
||||
return app.Shutdown()
|
||||
}
|
||||
|
||||
app.initializers = map[InitLevel]AppInitializer{
|
||||
InitLevelBasic: AppInitializerFunc(app.initBasic),
|
||||
InitLevelBuildRConfig: AppInitializerFunc(app.initBuildRConfig),
|
||||
InitLevelParseConfig: AppInitializerFunc(app.initParseConfigs),
|
||||
}
|
||||
|
||||
manApp, err := NewManApp(assets.Manual, app)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -95,17 +104,20 @@ func NewApp() *App {
|
|||
return ExecuteModuleCommand(
|
||||
c,
|
||||
app,
|
||||
TasksArgsProviderFor(app, app, c),
|
||||
TasksArgsProviderFor(app, c),
|
||||
WithShort(fmt.Sprintf("Run a %s by its name", modules.CategoryName(c))),
|
||||
)
|
||||
})...,
|
||||
)
|
||||
|
||||
//nolint:contextcheck // there's no context to pass here
|
||||
app.rootCmd.AddCommand(
|
||||
ModulesCommand(app, app, manApp, app),
|
||||
PluginsCommand(NewPluginApp(app.buildrCfg, app.pluginMgr, app), app),
|
||||
VaultCommand(NewVaultApp(app, app, app)),
|
||||
ServerCommand(NewServerApp(app, app)),
|
||||
EnvCommand(NewEnvApp(app, app)),
|
||||
PluginsCommand(NewPluginApp(app, app.pluginMgr, app)),
|
||||
VaultCommand(NewVaultApp(app, app)),
|
||||
ServerCommand(NewServerApp(app)),
|
||||
EnvCommand(NewEnvApp(app)),
|
||||
RunCommand(app, app, app),
|
||||
VersionCommand(),
|
||||
)
|
||||
|
||||
|
@ -115,48 +127,51 @@ func NewApp() *App {
|
|||
return app
|
||||
}
|
||||
|
||||
var (
|
||||
_ LevelInitializer = (*App)(nil)
|
||||
_ ModuleCommander = (*App)(nil)
|
||||
)
|
||||
var _ ModuleCommander = (*App)(nil)
|
||||
|
||||
type appServices struct {
|
||||
registry *modules.TypeRegistry
|
||||
vault *lazy.Lazy[*vault.Vault]
|
||||
dockerClient *lazy.Lazy[*client.Client]
|
||||
ignorer *ignore.Ignorer
|
||||
stateStore *lazy.Lazy[state.Store]
|
||||
cache *lazy.Lazy[state.Cache]
|
||||
stateDB *lazy.Lazy[*state.DB]
|
||||
diagsWriter hcl2.DiagnosticWriter
|
||||
repo *lazy.Lazy[*modules.Repository]
|
||||
}
|
||||
|
||||
type App struct {
|
||||
parsingState struct {
|
||||
parsingRemainder hcl2.Body
|
||||
currentEvalCtx *hcl2.EvalContext
|
||||
}
|
||||
services struct {
|
||||
registry *modules.TypeRegistry
|
||||
vault *vault.Vault
|
||||
dockerClient *lazy.Lazy[*client.Client]
|
||||
ignorer *ignore.Ignorer
|
||||
stateStore *lazy.Lazy[state.Store]
|
||||
cache *lazy.Lazy[state.Cache]
|
||||
stateDB *state.DB
|
||||
diagsWriter hcl2.DiagnosticWriter
|
||||
}
|
||||
vcs *lazy.Lazy[any]
|
||||
repoDetails *lazy.Lazy[repo.Repo]
|
||||
buildrCfg *config.Buildr
|
||||
rootCmd *cobra.Command
|
||||
recorder *profiling.Recorder
|
||||
initializers map[InitLevel]AppInitializer
|
||||
repo *modules.Repository
|
||||
pluginMgr *plugins.Manager
|
||||
loggingCfg logging.Config
|
||||
appCfg AppConfig
|
||||
}
|
||||
|
||||
func (a *App) Vault() *vault.Vault {
|
||||
return a.services.vault
|
||||
services appServices
|
||||
vcs *lazy.Lazy[any]
|
||||
repoDetails *lazy.Lazy[repo.Repo]
|
||||
buildrCfg *lazy.Lazy[*config.Buildr]
|
||||
rootCmd *cobra.Command
|
||||
recorder *profiling.Recorder
|
||||
pluginMgr *plugins.Manager
|
||||
loggingCfg logging.Config
|
||||
appCfg AppConfig
|
||||
}
|
||||
|
||||
func (a *App) SetVault(v *vault.Vault) {
|
||||
a.services.vault = v
|
||||
a.services.vault.Set(v)
|
||||
}
|
||||
|
||||
func (a *App) PluginsRepo() state.Plugins {
|
||||
return a.services.stateDB.Plugins
|
||||
func (a *App) Vault() (*vault.Vault, error) {
|
||||
return a.services.vault.Get()
|
||||
}
|
||||
|
||||
func (a *App) PluginsRepo() (state.Plugins, error) {
|
||||
s, err := a.services.stateDB.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.Plugins, nil
|
||||
}
|
||||
|
||||
func (a *App) TypeRegistry() *modules.TypeRegistry {
|
||||
|
@ -172,72 +187,24 @@ func (a *App) RunWithArgs(ctx context.Context, args ...string) error {
|
|||
return a.rootCmd.ExecuteContext(ctx)
|
||||
}
|
||||
|
||||
func (a *App) InitAt(ctx context.Context, lvl InitLevel) error {
|
||||
for cl := InitLevelNone; cl <= lvl; cl++ {
|
||||
if init, ok := a.initializers[cl]; ok {
|
||||
if err := init.Init(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) SetInitializer(lvl InitLevel, init AppInitializer) {
|
||||
a.initializers[lvl] = init
|
||||
}
|
||||
|
||||
func (a *App) Init(ctx context.Context) (err error) {
|
||||
func (a *App) Init() (err error) {
|
||||
slog.SetDefault(a.loggingCfg.Logger())
|
||||
|
||||
if a.recorder, err = a.appCfg.Profiling.Setup(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.services.registry = modules.NewRegistry(
|
||||
task.Registration,
|
||||
packaging.Registration,
|
||||
)
|
||||
|
||||
if err := a.appCfg.InitPaths(cwd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// initializing this in the background improves startup performance
|
||||
a.vcs = lazy.NewFuture[any](a.appCfg.ParseVCSInfo)
|
||||
a.repoDetails = lazy.NewFuture[repo.Repo](func() (repo.Repo, error) {
|
||||
var vi any
|
||||
if vi, err = a.vcs.Get(); err != nil {
|
||||
return repo.Repo{}, err
|
||||
} else {
|
||||
return a.appCfg.CollectRepoDetails(vi)
|
||||
}
|
||||
})
|
||||
|
||||
if a.services.ignorer, err = a.appCfg.Ignorer(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.services.dockerClient = lazy.NewLazy(func() (*client.Client, error) {
|
||||
const dockerClientCloseTimeout = 200 * time.Millisecond
|
||||
|
||||
cli, err := client.NewClientWithOpts(
|
||||
client.WithHostFromEnv(),
|
||||
client.WithVersionFromEnv(),
|
||||
client.WithTLSClientConfigFromEnv(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dockerCtx, dockerCancel := context.WithTimeout(ctx, dockerClientCloseTimeout)
|
||||
defer dockerCancel()
|
||||
|
||||
cli.NegotiateAPIVersion(dockerCtx)
|
||||
|
||||
return cli, nil
|
||||
})
|
||||
// init these in the background for better startup performance
|
||||
a.vcs = lazy.NewFuture[any](a.appCfg.ParseVCSInfo)
|
||||
a.repoDetails = lazy.NewFuture[repo.Repo](a.prepareRepoMetaInfo)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -250,19 +217,15 @@ func (a *App) AppConfig() AppConfig {
|
|||
return a.appCfg
|
||||
}
|
||||
|
||||
func (a *App) BuildrConfig() config.Buildr {
|
||||
return *a.buildrCfg
|
||||
func (a *App) BuildrConfig() (*config.Buildr, error) {
|
||||
return a.buildrCfg.Get()
|
||||
}
|
||||
|
||||
func (a *App) Repository() *modules.Repository {
|
||||
return a.repo
|
||||
func (a *App) Repository() (*modules.Repository, error) {
|
||||
return a.services.repo.Get()
|
||||
}
|
||||
|
||||
func (a *App) BootstrapModule(ctx context.Context, cat modules.Category, typeName, moduleName string) error {
|
||||
if err := a.InitAt(ctx, InitLevelBuildRConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *App) BootstrapModule(_ context.Context, cat modules.Category, typeName, moduleName string) error {
|
||||
mod, err := a.services.registry.Create(cat, typeName)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -276,13 +239,6 @@ func (a *App) BootstrapModule(ctx context.Context, cat modules.Category, typeNam
|
|||
}
|
||||
|
||||
func (a *App) RunModule(ctx context.Context, cat modules.Category, name string) error {
|
||||
if err := a.InitAt(ctx, InitLevelParseConfig); err != nil {
|
||||
if errors.Is(err, modules.ErrNoSuchModule) {
|
||||
return fmt.Errorf("%w, did you run 'buildr plugins update'", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
binPathProvider, err := containers.NewDefaultServerBinPathProvider(nil, CurrentVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create server bin path provider: %w", err)
|
||||
|
@ -296,127 +252,111 @@ func (a *App) RunModule(ctx context.Context, cat modules.Category, name string)
|
|||
return containers.NewOrchestrator(ctx, cli, a.services.ignorer, binPathProvider)
|
||||
})
|
||||
|
||||
modulesRepo, err := a.services.repo.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
factory := execution.NewTaskFactory(
|
||||
execution.WithProvider(local.Provider(a.services.stateStore.MustGet())),
|
||||
execution.WithProvider(container.Provider(orchestratorLazy, a.repo, a.services.stateStore.MustGet())),
|
||||
execution.WithProvider(container.Provider(orchestratorLazy, modulesRepo, a.services.stateStore.MustGet())),
|
||||
)
|
||||
|
||||
plan, err := execution.NewPlanFor(cat, name, a.repo, factory)
|
||||
plan, err := execution.NewPlanFor(cat, name, modulesRepo, factory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buildrCfg, err := a.buildrCfg.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return plan.Execute(ctx, execution.Spec{
|
||||
RepoRoot: a.appCfg.RepoRoot,
|
||||
BinariesDirectory: a.buildrCfg.BinariesDirectory,
|
||||
CacheDirectory: a.buildrCfg.CacheDirectory,
|
||||
OutDirectory: a.buildrCfg.OutDirectory,
|
||||
LogsDirectory: a.buildrCfg.LogsDirectory,
|
||||
LogToStdErr: a.buildrCfg.LogToStderr,
|
||||
ProjectRoot: a.appCfg.ProjectRoot,
|
||||
BinariesDirectory: buildrCfg.BinariesDirectory,
|
||||
CacheDirectory: buildrCfg.CacheDirectory,
|
||||
OutDirectory: buildrCfg.OutDirectory,
|
||||
LogsDirectory: buildrCfg.LogsDirectory,
|
||||
LogToStdErr: buildrCfg.LogToStderr,
|
||||
})
|
||||
}
|
||||
|
||||
func (a *App) initBasic(ctx context.Context) (err error) {
|
||||
var v *vault.Vault
|
||||
if v, err = a.appCfg.InitVault(); err != nil {
|
||||
func (a *App) RunTool(ctx context.Context, name string, args []string) error {
|
||||
buildrCfg, err := a.buildrCfg.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var pathBuilder strings.Builder
|
||||
_, _ = pathBuilder.WriteString(buildrCfg.BinariesDirectory)
|
||||
|
||||
currentPath, ok := os.LookupEnv("PATH")
|
||||
if ok {
|
||||
_, _ = pathBuilder.WriteRune(os.PathListSeparator)
|
||||
_, _ = pathBuilder.WriteString(currentPath)
|
||||
}
|
||||
|
||||
if err := os.Setenv("PATH", pathBuilder.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var db *state.DB
|
||||
if db, err = a.appCfg.InitState(ctx); err != nil {
|
||||
modulesRepo, err := a.services.repo.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.services.vault = v
|
||||
a.services.stateDB = db
|
||||
|
||||
a.services.stateStore = lazy.NewLazy(func() (state.Store, error) {
|
||||
return db.State, nil
|
||||
})
|
||||
|
||||
a.services.cache = lazy.NewLazy(func() (state.Cache, error) {
|
||||
return state.NewStateCache(a.appCfg.Cache.TTL, db.State), nil
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) initBuildRConfig(ctx context.Context) error {
|
||||
parser := hcl.NewParser(slog.Default(), a.appCfg.BuildRFS())
|
||||
slog.Debug("Reading files", "buildr_directory", a.appCfg.BuildRDirectory)
|
||||
|
||||
if err := parser.ReadFiles(ctx); err != nil {
|
||||
slog.Error("Failed to read files", err)
|
||||
return err
|
||||
mod := modulesRepo.Module(modules.CategoryTool, name)
|
||||
if mod == nil {
|
||||
return fmt.Errorf("no tool with name %s found", name)
|
||||
}
|
||||
|
||||
a.buildrCfg.LogToStderr = a.appCfg.Execution.LogToStderr
|
||||
|
||||
buildrCfg := struct {
|
||||
Remainder hcl2.Body `hcl:",remain"`
|
||||
*config.Buildr `hcl:"buildr,block"`
|
||||
}{
|
||||
Buildr: a.buildrCfg,
|
||||
if bn, ok := mod.Unwrap().(modules.BinaryNamer); ok {
|
||||
if binaryName, err := bn.BinaryName(ctx); err != nil {
|
||||
return err
|
||||
} else {
|
||||
name = binaryName
|
||||
}
|
||||
}
|
||||
|
||||
a.parsingState.currentEvalCtx = hcl.BasicContext(a.services.vault)
|
||||
if err := parser.Parse(a.parsingState.currentEvalCtx, &buildrCfg); err != nil {
|
||||
return err
|
||||
cmd := exec.CommandContext(ctx, name, args...)
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
|
||||
err = cmd.Run()
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
a.services.diagsWriter = parser.DiagsWriter()
|
||||
|
||||
if err := buildrCfg.SetupDirectories(a.appCfg.BuildRDirectory, a.appCfg.RepoRoot, a.appCfg.Execution.CleanDirectories); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.pluginMgr.Init(a.services.stateDB.Plugins, buildrCfg.CacheDirectory)
|
||||
|
||||
if err := a.pluginMgr.Register(ctx, a.services.registry); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.parsingState.parsingRemainder = buildrCfg.Remainder
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) initParseConfigs(ctx context.Context) (err error) {
|
||||
evalCtx := hcl.FullContext(ctx, a.parsingState.currentEvalCtx, a.services.vault, a.services.cache.MustGet())
|
||||
var rawSpec hcl.ModulesSpec
|
||||
|
||||
if diags := gohcl.DecodeBody(a.parsingState.parsingRemainder, evalCtx, &rawSpec); diags.HasErrors() {
|
||||
_ = a.services.diagsWriter.WriteDiagnostics(diags)
|
||||
var exitErr *exec.ExitError
|
||||
if errors.As(err, &exitErr) {
|
||||
slog.Default().Error(
|
||||
"Failed to run tool",
|
||||
slog.Bool("exited", exitErr.Exited()),
|
||||
slog.Int("exit_code", exitErr.ExitCode()),
|
||||
slog.Duration("duration", exitErr.UserTime()),
|
||||
)
|
||||
return errs.ErrAlreadyLogged
|
||||
}
|
||||
|
||||
vcsInfo, err := a.vcs.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
repoDetails, err := a.repoDetails.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
func (a *App) prepareRepoMetaInfo() (repo.Repo, error) {
|
||||
if vi, err := a.vcs.Get(); err != nil {
|
||||
if errors.Is(err, ErrNoVCSRootDetected) {
|
||||
return repo.Repo{}, nil
|
||||
}
|
||||
return repo.Repo{}, err
|
||||
} else {
|
||||
return a.appCfg.CollectRepoDetails(vi)
|
||||
}
|
||||
|
||||
a.repo, err = rawSpec.Repository(
|
||||
evalCtx,
|
||||
a.services.diagsWriter,
|
||||
a.services.registry,
|
||||
a.buildrCfg.OutDirectory,
|
||||
hcl.WithGlobalVariable("vcs", vcsInfo),
|
||||
hcl.WithGlobalVariable("repo", repoDetails),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) persistVaultState() error {
|
||||
if a.services.vault == nil {
|
||||
instance, err := a.services.vault.Get()
|
||||
if err != nil || instance == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -431,5 +371,134 @@ func (a *App) persistVaultState() error {
|
|||
|
||||
encoder := json.NewEncoder(outFile)
|
||||
|
||||
return encoder.Encode(a.services.vault)
|
||||
return encoder.Encode(instance)
|
||||
}
|
||||
|
||||
func (a *App) prepareStateStore() (state.Store, error) {
|
||||
db, err := a.services.stateDB.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return db.State, nil
|
||||
}
|
||||
|
||||
func (a *App) prepareCache() (state.Cache, error) {
|
||||
db, err := a.services.stateDB.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return state.NewStateCache(a.appCfg.Cache.TTL, db.State), nil
|
||||
}
|
||||
|
||||
func (a *App) prepareBuildrConfig(ctx context.Context) (*config.Buildr, error) {
|
||||
parser := hcl.NewParser(slog.Default(), a.appCfg.BuildRFS())
|
||||
slog.Debug("Reading files", "buildr_directory", a.appCfg.BuildRDirectory)
|
||||
|
||||
if err := parser.ReadFiles(ctx); err != nil {
|
||||
slog.Error("Failed to read files", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buildrCfg := struct {
|
||||
Remainder hcl2.Body `hcl:",remain"`
|
||||
*config.Buildr `hcl:"buildr,block"`
|
||||
}{
|
||||
Buildr: &config.Buildr{
|
||||
LogToStderr: a.appCfg.Execution.LogToStderr,
|
||||
},
|
||||
}
|
||||
|
||||
vaultInstance, err := a.services.vault.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a.parsingState.currentEvalCtx = hcl.BasicContext(vaultInstance)
|
||||
if err := parser.Parse(a.parsingState.currentEvalCtx, &buildrCfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a.services.diagsWriter = parser.DiagsWriter()
|
||||
|
||||
if err := buildrCfg.SetupDirectories(a.appCfg.BuildRDirectory(), a.appCfg.ProjectRoot, a.appCfg.Execution.CleanDirectories); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stateDB, err := a.services.stateDB.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a.pluginMgr.Init(stateDB.Plugins, buildrCfg.CacheDirectory)
|
||||
|
||||
if err := a.pluginMgr.Register(ctx, a.services.registry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a.parsingState.parsingRemainder = buildrCfg.Remainder
|
||||
|
||||
return buildrCfg.Buildr, nil
|
||||
}
|
||||
|
||||
func (a *App) prepareModulesRepo(ctx context.Context) (*modules.Repository, error) {
|
||||
vaultInstance, err := a.services.vault.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// call this early to ensure the parsing state is initialized
|
||||
buildrCfg, err := a.buildrCfg.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
evalCtx := hcl.FullContext(ctx, a.parsingState.currentEvalCtx, vaultInstance, a.services.cache.MustGet())
|
||||
var rawSpec hcl.ModulesSpec
|
||||
|
||||
if diags := gohcl.DecodeBody(a.parsingState.parsingRemainder, evalCtx, &rawSpec); diags.HasErrors() {
|
||||
_ = a.services.diagsWriter.WriteDiagnostics(diags)
|
||||
return nil, errs.ErrAlreadyLogged
|
||||
}
|
||||
|
||||
var repoOptions []hcl.RepositoryOption
|
||||
|
||||
vcsInfo, err := a.vcs.Get()
|
||||
if err == nil {
|
||||
repoOptions = append(repoOptions, hcl.WithGlobalVariable("vcs", vcsInfo))
|
||||
}
|
||||
|
||||
repoDetails, err := a.repoDetails.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
repoOptions = append(repoOptions, hcl.WithGlobalVariable("repo", repoDetails))
|
||||
}
|
||||
|
||||
return rawSpec.Repository(
|
||||
evalCtx,
|
||||
a.services.diagsWriter,
|
||||
a.services.registry,
|
||||
buildrCfg.OutDirectory,
|
||||
repoOptions...,
|
||||
)
|
||||
}
|
||||
|
||||
func prepareDockerAPIClient(ctx context.Context) (*client.Client, error) {
|
||||
const dockerClientCloseTimeout = 200 * time.Millisecond
|
||||
|
||||
cli, err := client.NewClientWithOpts(
|
||||
client.WithHostFromEnv(),
|
||||
client.WithVersionFromEnv(),
|
||||
client.WithTLSClientConfigFromEnv(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dockerCtx, dockerCancel := context.WithTimeout(ctx, dockerClientCloseTimeout)
|
||||
defer dockerCancel()
|
||||
|
||||
cli.NegotiateAPIVersion(dockerCtx)
|
||||
|
||||
return cli, nil
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
@ -10,24 +9,21 @@ import (
|
|||
|
||||
var _ EnvCommander = (*EnvApp)(nil)
|
||||
|
||||
func NewEnvApp(initializer LevelInitializer, accessor BuildrConfigAccessor) *EnvApp {
|
||||
func NewEnvApp(accessor BuildrConfigAccessor) *EnvApp {
|
||||
return &EnvApp{
|
||||
initializer: initializer,
|
||||
executionSpecAccessor: accessor,
|
||||
}
|
||||
}
|
||||
|
||||
type EnvApp struct {
|
||||
initializer LevelInitializer
|
||||
executionSpecAccessor BuildrConfigAccessor
|
||||
}
|
||||
|
||||
func (e EnvApp) PrintPath(ctx context.Context, writer io.Writer) error {
|
||||
if err := e.initializer.InitAt(ctx, InitLevelBuildRConfig); err != nil {
|
||||
func (e EnvApp) PrintPath(writer io.Writer) error {
|
||||
execSpec, err := e.executionSpecAccessor.BuildrConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
execSpec := e.executionSpecAccessor.BuildrConfig()
|
||||
currentPath, ok := os.LookupEnv("PATH")
|
||||
if !ok {
|
||||
_, err := fmt.Fprintf(writer, "PATH=%s", execSpec.BinariesDirectory)
|
||||
|
@ -35,6 +31,6 @@ func (e EnvApp) PrintPath(ctx context.Context, writer io.Writer) error {
|
|||
}
|
||||
|
||||
joinedPath := strings.Join([]string{execSpec.BinariesDirectory, currentPath}, string(os.PathListSeparator))
|
||||
_, err := fmt.Fprintf(writer, "PATH=%s", joinedPath)
|
||||
_, err = fmt.Fprintf(writer, "PATH=%s", joinedPath)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"io"
|
||||
|
||||
"code.icb4dc0.de/buildr/buildr/internal/config"
|
||||
"code.icb4dc0.de/buildr/buildr/internal/plugins"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
@ -12,22 +11,26 @@ import (
|
|||
|
||||
var _ PluginCommander = (*PluginApp)(nil)
|
||||
|
||||
func NewPluginApp(buildrCfg *config.Buildr, pluginMgr *plugins.Manager, svcAcc PluginsRepoAccessor) *PluginApp {
|
||||
func NewPluginApp(buildrCfgAccessor BuildrConfigAccessor, pluginMgr *plugins.Manager, svcAcc PluginsRepoAccessor) *PluginApp {
|
||||
return &PluginApp{
|
||||
buildrCfg: buildrCfg,
|
||||
pluginMgr: pluginMgr,
|
||||
svcAcc: svcAcc,
|
||||
buildrCfgAccessor: buildrCfgAccessor,
|
||||
pluginMgr: pluginMgr,
|
||||
svcAcc: svcAcc,
|
||||
}
|
||||
}
|
||||
|
||||
type PluginApp struct {
|
||||
buildrCfg *config.Buildr
|
||||
pluginMgr *plugins.Manager
|
||||
svcAcc PluginsRepoAccessor
|
||||
buildrCfgAccessor BuildrConfigAccessor
|
||||
pluginMgr *plugins.Manager
|
||||
svcAcc PluginsRepoAccessor
|
||||
}
|
||||
|
||||
func (p PluginApp) ListPlugins(ctx context.Context, writer io.Writer) error {
|
||||
knownPlugins, err := p.svcAcc.PluginsRepo().List(ctx)
|
||||
repo, err := p.svcAcc.PluginsRepo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
knownPlugins, err := repo.List(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -42,5 +45,9 @@ func (p PluginApp) ListPlugins(ctx context.Context, writer io.Writer) error {
|
|||
}
|
||||
|
||||
func (p PluginApp) UpdatePlugins(ctx context.Context) error {
|
||||
return p.pluginMgr.UpdatePlugins(ctx, p.buildrCfg.Plugins...)
|
||||
cfg, err := p.buildrCfgAccessor.BuildrConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.pluginMgr.UpdatePlugins(ctx, cfg.Plugins...)
|
||||
}
|
||||
|
|
|
@ -9,23 +9,17 @@ import (
|
|||
|
||||
var _ ServerCommander = (*ServerApp)(nil)
|
||||
|
||||
func NewServerApp(initializer LevelInitializer, accessor TypeRegistryAccessor) *ServerApp {
|
||||
func NewServerApp(accessor TypeRegistryAccessor) *ServerApp {
|
||||
return &ServerApp{
|
||||
initializer: initializer,
|
||||
accessor: accessor,
|
||||
accessor: accessor,
|
||||
}
|
||||
}
|
||||
|
||||
type ServerApp struct {
|
||||
initializer LevelInitializer
|
||||
accessor TypeRegistryAccessor
|
||||
accessor TypeRegistryAccessor
|
||||
}
|
||||
|
||||
func (s *ServerApp) ServeAPI(ctx context.Context, cfg *rpc.GrpcConfig) error {
|
||||
if err := s.initializer.InitAt(ctx, InitLevelBasic); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger := slog.Default()
|
||||
|
||||
logger.Info("Starting gRPC server", slog.Group("grpc", slog.String("addr", cfg.Host.Address)))
|
||||
|
|
|
@ -21,29 +21,26 @@ type VaultAppServiceAccess interface {
|
|||
}
|
||||
|
||||
func NewVaultApp(
|
||||
initializer LevelInitializer,
|
||||
serviceAccess VaultAppServiceAccess,
|
||||
configAccessor AppConfigAccessor,
|
||||
) *VaultApp {
|
||||
return &VaultApp{
|
||||
initializer: initializer,
|
||||
serviceAccess: serviceAccess,
|
||||
configAccessor: configAccessor,
|
||||
}
|
||||
}
|
||||
|
||||
type VaultApp struct {
|
||||
initializer LevelInitializer
|
||||
serviceAccess VaultAppServiceAccess
|
||||
configAccessor AppConfigAccessor
|
||||
}
|
||||
|
||||
func (v VaultApp) Init(ctx context.Context, initCfg VaultInitConfig) error {
|
||||
if err := v.initializer.InitAt(ctx, InitLevelBasic); err != nil {
|
||||
func (v VaultApp) Init(_ context.Context, initCfg VaultInitConfig) error {
|
||||
vault, err := v.serviceAccess.Vault()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vault := v.serviceAccess.Vault()
|
||||
if vault != nil {
|
||||
slog.Default().Info("vault already initialized")
|
||||
return nil
|
||||
|
@ -73,12 +70,12 @@ func (v VaultApp) Init(ctx context.Context, initCfg VaultInitConfig) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (v VaultApp) List(ctx context.Context, writer io.Writer) error {
|
||||
if err := v.initializer.InitAt(ctx, InitLevelBasic); err != nil {
|
||||
func (v VaultApp) List(_ context.Context, writer io.Writer) error {
|
||||
vaultInstance, err := v.serviceAccess.Vault()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vaultInstance := v.serviceAccess.Vault()
|
||||
if vaultInstance == nil {
|
||||
return ErrVaultNotInitiated
|
||||
}
|
||||
|
@ -94,12 +91,11 @@ func (v VaultApp) List(ctx context.Context, writer io.Writer) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (v VaultApp) Get(ctx context.Context, key string, writer io.Writer) error {
|
||||
if err := v.initializer.InitAt(ctx, InitLevelBasic); err != nil {
|
||||
func (v VaultApp) Get(_ context.Context, key string, writer io.Writer) error {
|
||||
vaultInstance, err := v.serviceAccess.Vault()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vaultInstance := v.serviceAccess.Vault()
|
||||
if vaultInstance == nil {
|
||||
return ErrVaultNotInitiated
|
||||
}
|
||||
|
@ -117,12 +113,11 @@ func (v VaultApp) Get(ctx context.Context, key string, writer io.Writer) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (v VaultApp) Set(ctx context.Context, key string, value []byte) error {
|
||||
if err := v.initializer.InitAt(ctx, InitLevelBasic); err != nil {
|
||||
func (v VaultApp) Set(_ context.Context, key string, value []byte) error {
|
||||
vaultInstance, err := v.serviceAccess.Vault()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vaultInstance := v.serviceAccess.Vault()
|
||||
if vaultInstance == nil {
|
||||
return ErrVaultNotInitiated
|
||||
}
|
||||
|
@ -130,12 +125,11 @@ func (v VaultApp) Set(ctx context.Context, key string, value []byte) error {
|
|||
return vaultInstance.SetValue(key, value)
|
||||
}
|
||||
|
||||
func (v VaultApp) Remove(ctx context.Context, key string) error {
|
||||
if err := v.initializer.InitAt(ctx, InitLevelBasic); err != nil {
|
||||
func (v VaultApp) Remove(_ context.Context, key string) error {
|
||||
vaultInstance, err := v.serviceAccess.Vault()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vaultInstance := v.serviceAccess.Vault()
|
||||
if vaultInstance == nil {
|
||||
return ErrVaultNotInitiated
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -11,18 +10,17 @@ import (
|
|||
)
|
||||
|
||||
func ModulesArgsProviderFor(
|
||||
initializer LevelInitializer,
|
||||
registryAcc TypeRegistryAccessor,
|
||||
buildrConfigAccessor BuildrConfigAccessor,
|
||||
cat modules.Category,
|
||||
) KnownModulesArgProvider {
|
||||
return KnownModulesArgProviderFunc(func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if err := initializer.InitAt(cmd.Context(), InitLevelBasic); err != nil {
|
||||
slog.Warn("Failed to parse config", slog.String("err", err.Error()))
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
if _, err := buildrConfigAccessor.BuildrConfig(); err != nil {
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
||||
knownModules := registryAcc.TypeRegistry().Inventory()[cat]
|
||||
|
@ -39,18 +37,18 @@ func ModulesArgsProviderFor(
|
|||
})
|
||||
}
|
||||
|
||||
func TasksArgsProviderFor(initializer LevelInitializer, repoAcc ModuleRepositoryAccessor, cat modules.Category) KnownTasksArgProvider {
|
||||
func TasksArgsProviderFor(repoAcc ModuleRepositoryAccessor, cat modules.Category) KnownTasksArgProvider {
|
||||
return KnownTasksArgProviderFunc(func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if err := initializer.InitAt(cmd.Context(), InitLevelBasic); err != nil {
|
||||
slog.Warn("Failed to parse config", slog.String("err", err.Error()))
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
tasks := maps.Keys(repoAcc.Repository().ModulesByCategory(cat))
|
||||
repo, err := repoAcc.Repository()
|
||||
if err != nil {
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
||||
tasks := maps.Keys(repo.ModulesByCategory(cat))
|
||||
|
||||
filtered := make([]string, 0, len(tasks))
|
||||
|
||||
|
|
|
@ -23,9 +23,16 @@ import (
|
|||
"code.icb4dc0.de/buildr/buildr/modules/vcs"
|
||||
)
|
||||
|
||||
const defaultCacheTTL = 15 * time.Minute
|
||||
const (
|
||||
defaultCacheTTL = 15 * time.Minute
|
||||
buildrDirectoryName = ".buildr"
|
||||
buildrProjectRootEnv = "BUILDR_PROJECT_ROOT"
|
||||
)
|
||||
|
||||
var ErrRepoRootNotFound = errors.New("failed to detect repo root")
|
||||
var (
|
||||
ErrFailedToFindDirectory = errors.New("failed to find directory")
|
||||
ErrNoVCSRootDetected = errors.New("no VCS root detected")
|
||||
)
|
||||
|
||||
type AppConfig struct {
|
||||
Vault struct {
|
||||
|
@ -33,20 +40,24 @@ type AppConfig struct {
|
|||
Passphrase string
|
||||
PassphraseFile string
|
||||
}
|
||||
VCSType vcs.Type
|
||||
BuildRDirectory string
|
||||
RepoRoot string
|
||||
State struct{ FilePath string }
|
||||
Profiling profiling.Config
|
||||
Cache struct{ TTL time.Duration }
|
||||
Execution struct {
|
||||
ProjectRoot string
|
||||
VCSRoot string
|
||||
VCSType vcs.Type
|
||||
State struct{ FilePath string }
|
||||
Profiling profiling.Config
|
||||
Cache struct{ TTL time.Duration }
|
||||
Execution struct {
|
||||
LogToStderr bool
|
||||
CleanDirectories bool
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AppConfig) CollectRepoDetails(vcsInfo any) (repoDetails repo.Repo, err error) {
|
||||
repoDetails.Root = c.RepoRoot
|
||||
if c.VCSRoot == "" || vcsInfo == nil {
|
||||
return repoDetails, nil
|
||||
}
|
||||
|
||||
repoDetails.Root = c.VCSRoot
|
||||
|
||||
if git, ok := vcsInfo.(*vcs.Git); ok && git.Tag != "" && semver.IsValid(git.Tag) {
|
||||
if repoDetails.Version, err = semver.ParseVersion(git.Tag); err != nil {
|
||||
|
@ -60,9 +71,13 @@ func (c *AppConfig) CollectRepoDetails(vcsInfo any) (repoDetails repo.Repo, err
|
|||
}
|
||||
|
||||
func (c *AppConfig) ParseVCSInfo() (vcsDetails any, err error) {
|
||||
if c.VCSRoot == "" {
|
||||
return nil, ErrNoVCSRootDetected
|
||||
}
|
||||
|
||||
switch c.VCSType {
|
||||
case vcs.TypeGit:
|
||||
gitInfo, err := vcs.ParseGitInfo(c.RepoRoot)
|
||||
gitInfo, err := vcs.ParseGitInfo(c.VCSRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -73,31 +88,39 @@ func (c *AppConfig) ParseVCSInfo() (vcsDetails any, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *AppConfig) RepoFS() fs.FS {
|
||||
return os.DirFS(c.RepoRoot)
|
||||
func (c *AppConfig) BuildRFS() fs.FS {
|
||||
return os.DirFS(c.BuildRDirectory())
|
||||
}
|
||||
|
||||
func (c *AppConfig) BuildRFS() fs.FS {
|
||||
return os.DirFS(c.BuildRDirectory)
|
||||
func (c *AppConfig) BuildRDirectory() string {
|
||||
return filepath.Join(c.ProjectRoot, buildrDirectoryName)
|
||||
}
|
||||
|
||||
func (c *AppConfig) Ignorer() (*ignore.Ignorer, error) {
|
||||
return ignore.NewIgnorer(c.RepoRoot, c.Vault.FilePath)
|
||||
return ignore.NewIgnorer(c.ProjectRoot, c.Vault.FilePath)
|
||||
}
|
||||
|
||||
func (c *AppConfig) InitPaths(from string) (err error) {
|
||||
if c.RepoRoot == "" {
|
||||
if c.RepoRoot, err = c.findRepoRoot(from); err != nil {
|
||||
return err
|
||||
}
|
||||
var projectRoot string
|
||||
|
||||
if root, ok := os.LookupEnv(buildrProjectRootEnv); ok {
|
||||
projectRoot = root
|
||||
} else if root, err = c.findDirectory(from, buildrDirectoryName); err != nil {
|
||||
return err
|
||||
} else {
|
||||
projectRoot = root
|
||||
}
|
||||
|
||||
if !filepath.IsAbs(c.BuildRDirectory) {
|
||||
c.BuildRDirectory = filepath.Join(c.RepoRoot, c.BuildRDirectory)
|
||||
c.ProjectRoot = projectRoot
|
||||
|
||||
if d, err := c.findDirectory(c.ProjectRoot, c.VCSType.Directory()); err != nil && !errors.Is(err, ErrFailedToFindDirectory) {
|
||||
return err
|
||||
} else {
|
||||
c.VCSRoot = d
|
||||
}
|
||||
|
||||
if stateFilePath := c.State.FilePath; stateFilePath != "" && !filepath.IsAbs(stateFilePath) {
|
||||
c.State.FilePath = filepath.Join(c.RepoRoot, stateFilePath)
|
||||
c.State.FilePath = filepath.Join(c.ProjectRoot, stateFilePath)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -123,7 +146,7 @@ func (c *AppConfig) InitVault() (*vault.Vault, error) {
|
|||
|
||||
if filePath := c.Vault.FilePath; filePath != "" {
|
||||
if !filepath.IsAbs(c.Vault.FilePath) {
|
||||
filePath = filepath.Join(c.RepoRoot, filePath)
|
||||
filePath = filepath.Join(c.ProjectRoot, filePath)
|
||||
}
|
||||
|
||||
vaultOpts = append(vaultOpts, vault.WithLoadFromPath(filePath))
|
||||
|
@ -136,19 +159,6 @@ func (c *AppConfig) Flags() *flag.FlagSet {
|
|||
flags := flag.NewFlagSet("buildr", flag.ExitOnError)
|
||||
|
||||
c.VCSType = config.EnvOr("BUILDR_VCS_TYPE", vcs.ParseType, vcs.TypeGit)
|
||||
flags.StringVar(
|
||||
&c.BuildRDirectory,
|
||||
"buildr.directory",
|
||||
config.StringEnvOr("BUILDR_DIRECTORY", ".buildr"),
|
||||
"Directory where to look for BuildR files - if relative path will be relative to repository root (depending on the VCS directory)",
|
||||
)
|
||||
|
||||
flags.StringVar(
|
||||
&c.RepoRoot,
|
||||
"buildr.repo-root",
|
||||
config.StringEnvOr("BUILDR_REPO_ROOT", ""),
|
||||
"Repository root directory - normally auto-detected based on VCS directory",
|
||||
)
|
||||
|
||||
flags.StringVar(
|
||||
&c.Vault.FilePath,
|
||||
|
@ -210,19 +220,19 @@ func (c *AppConfig) Flags() *flag.FlagSet {
|
|||
return flags
|
||||
}
|
||||
|
||||
func (c *AppConfig) findRepoRoot(dir string) (string, error) {
|
||||
if dir == "." || dir == "" || dir == "/" {
|
||||
return "", ErrRepoRootNotFound
|
||||
func (c *AppConfig) findDirectory(from, dirToSearch string) (string, error) {
|
||||
if from == "." || from == "" || from == "/" {
|
||||
return "", fmt.Errorf("%w: %s", ErrFailedToFindDirectory, dirToSearch)
|
||||
}
|
||||
|
||||
info, err := os.Stat(filepath.Join(dir, c.VCSType.Directory()))
|
||||
info, err := os.Stat(filepath.Join(from, dirToSearch))
|
||||
if err != nil {
|
||||
return c.findRepoRoot(filepath.Dir(dir))
|
||||
return c.findDirectory(filepath.Dir(from), dirToSearch)
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return dir, nil
|
||||
return from, nil
|
||||
}
|
||||
|
||||
return c.findRepoRoot(filepath.Dir(dir))
|
||||
return c.findDirectory(filepath.Dir(from), dirToSearch)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ func EnvCommand(cmder EnvCommander) *cobra.Command {
|
|||
Short: "Helper to configure your shell's $PATH to include local tools",
|
||||
Example: `export $(buildr env path)`,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return cmder.PrintPath(cmd.Context(), os.Stdout)
|
||||
return cmder.PrintPath(os.Stdout)
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ func (m *ManConfig) Pager(ctx context.Context, title string) (Pager, error) {
|
|||
func ModuleManCommand(
|
||||
category modules.Category,
|
||||
cmder ManCommander,
|
||||
initializer LevelInitializer,
|
||||
buildrConfigAccessor BuildrConfigAccessor,
|
||||
argsProvider KnownModulesArgProvider,
|
||||
manCfg *ManConfig,
|
||||
) *cobra.Command {
|
||||
|
@ -86,10 +86,9 @@ func ModuleManCommand(
|
|||
Args: cobra.ExactArgs(1),
|
||||
ValidArgsFunction: argsProvider.ValidModulesArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
if err := initializer.InitAt(cmd.Context(), InitLevelBuildRConfig); err != nil {
|
||||
if _, err = buildrConfigAccessor.BuildrConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p, err := manCfg.Pager(cmd.Context(), fmt.Sprintf("Manual - %s/%s", modules.CategoryName(category), args[0]))
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -102,8 +101,8 @@ func ModuleManCommand(
|
|||
|
||||
func ManCmd(
|
||||
cmder ManCommander,
|
||||
initializer LevelInitializer,
|
||||
registryAcc TypeRegistryAccessor,
|
||||
buildrConfigAccessor BuildrConfigAccessor,
|
||||
) *cobra.Command {
|
||||
var manCfg ManConfig
|
||||
manCmd := &cobra.Command{
|
||||
|
@ -112,10 +111,6 @@ func ManCmd(
|
|||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := initializer.InitAt(cmd.Context(), InitLevelBuildRConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p, err := manCfg.Pager(cmd.Context(), "Manual - modules")
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -128,8 +123,8 @@ func ManCmd(
|
|||
return ModuleManCommand(
|
||||
c,
|
||||
cmder,
|
||||
initializer,
|
||||
ModulesArgsProviderFor(initializer, registryAcc, c),
|
||||
buildrConfigAccessor,
|
||||
ModulesArgsProviderFor(registryAcc, buildrConfigAccessor, c),
|
||||
&manCfg,
|
||||
)
|
||||
})...)
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
)
|
||||
|
||||
func ModulesCommand(
|
||||
initializer LevelInitializer,
|
||||
registryAcc TypeRegistryAccessor,
|
||||
buildrConfigAccessor BuildrConfigAccessor,
|
||||
manCmder ManCommander,
|
||||
moduleCmder BootstrapModuleCommander,
|
||||
) *cobra.Command {
|
||||
|
@ -25,14 +25,15 @@ func ModulesCommand(
|
|||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
ModulesListCommand(initializer, registryAcc, os.Stdout),
|
||||
ManCmd(manCmder, initializer, registryAcc),
|
||||
ModulesListCommand(registryAcc, buildrConfigAccessor, os.Stdout),
|
||||
ManCmd(manCmder, registryAcc, buildrConfigAccessor),
|
||||
NewCmd(
|
||||
slices.Map(modules.Categories(), func(c modules.Category) *cobra.Command {
|
||||
return BootstrapModuleCmd(
|
||||
c,
|
||||
moduleCmder,
|
||||
ModulesArgsProviderFor(initializer, registryAcc, c),
|
||||
buildrConfigAccessor,
|
||||
ModulesArgsProviderFor(registryAcc, buildrConfigAccessor, c),
|
||||
WithShort(fmt.Sprintf("Bootstrap %s module", modules.CategoryName(c))),
|
||||
)
|
||||
})...,
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
)
|
||||
|
||||
func ModulesListCommand(
|
||||
initializer LevelInitializer,
|
||||
registryAcc TypeRegistryAccessor,
|
||||
buildrConfigaccessor BuildrConfigAccessor,
|
||||
out io.Writer,
|
||||
) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
|
@ -20,7 +20,7 @@ func ModulesListCommand(
|
|||
SilenceErrors: true,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := initializer.InitAt(cmd.Context(), InitLevelBuildRConfig); err != nil {
|
||||
if _, err := buildrConfigaccessor.BuildrConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ func NewCmd(subCommands ...*cobra.Command) *cobra.Command {
|
|||
func BootstrapModuleCmd(
|
||||
category modules.Category,
|
||||
cmder BootstrapModuleCommander,
|
||||
buildrConfigAccessor BuildrConfigAccessor,
|
||||
argsProvider KnownModulesArgProvider,
|
||||
opts ...ModuleCommandOption,
|
||||
) *cobra.Command {
|
||||
|
@ -31,6 +32,9 @@ func BootstrapModuleCmd(
|
|||
ValidArgsFunction: argsProvider.ValidModulesArgs,
|
||||
Args: cobra.RangeArgs(1, argsWithModuleName),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if _, err := buildrConfigAccessor.BuildrConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
typeName = args[0]
|
||||
moduleName string
|
||||
|
|
|
@ -6,15 +6,12 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func PluginListCommand(cmder PluginCommander, initializer LevelInitializer) *cobra.Command {
|
||||
func PluginListCommand(cmder PluginCommander) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List plugins",
|
||||
Aliases: []string{"ls", "dir"},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := initializer.InitAt(cmd.Context(), InitLevelBasic); err != nil {
|
||||
return err
|
||||
}
|
||||
return cmder.ListPlugins(cmd.Context(), os.Stdout)
|
||||
},
|
||||
}
|
||||
|
@ -22,14 +19,11 @@ func PluginListCommand(cmder PluginCommander, initializer LevelInitializer) *cob
|
|||
return cmd
|
||||
}
|
||||
|
||||
func PluginUpdateCommand(cmder PluginCommander, initializer LevelInitializer) *cobra.Command {
|
||||
func PluginUpdateCommand(cmder PluginCommander) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "update",
|
||||
Short: "Update plugins",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := initializer.InitAt(cmd.Context(), InitLevelBuildRConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
return cmder.UpdatePlugins(cmd.Context())
|
||||
},
|
||||
}
|
||||
|
@ -37,7 +31,7 @@ func PluginUpdateCommand(cmder PluginCommander, initializer LevelInitializer) *c
|
|||
return cmd
|
||||
}
|
||||
|
||||
func PluginsCommand(cmder PluginCommander, initializer LevelInitializer) *cobra.Command {
|
||||
func PluginsCommand(cmder PluginCommander) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "plugins",
|
||||
Short: "Manage plugins",
|
||||
|
@ -45,7 +39,7 @@ func PluginsCommand(cmder PluginCommander, initializer LevelInitializer) *cobra.
|
|||
SilenceErrors: true,
|
||||
}
|
||||
|
||||
cmd.AddCommand(PluginListCommand(cmder, initializer), PluginUpdateCommand(cmder, initializer))
|
||||
cmd.AddCommand(PluginListCommand(cmder), PluginUpdateCommand(cmder))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
68
internal/cmd/run.go
Normal file
68
internal/cmd/run.go
Normal file
|
@ -0,0 +1,68 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"code.icb4dc0.de/buildr/buildr/modules"
|
||||
)
|
||||
|
||||
func RunCommand(
|
||||
mra ModuleRepositoryAccessor,
|
||||
moduleExecCmder ModuleCommander,
|
||||
toolExecCmder RunToolCommander,
|
||||
) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "run",
|
||||
Aliases: []string{"exec"},
|
||||
Short: "Run a tool - tool will be installed on demand",
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
ValidArgsFunction: completeTools(mra),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := moduleExecCmder.RunModule(cmd.Context(), modules.CategoryTool, args[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return toolExecCmder.RunTool(cmd.Context(), args[0], args[1:])
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:lll // return type is what it is
|
||||
func completeTools(mra ModuleRepositoryAccessor) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) > 0 {
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
|
||||
toComplete = strings.ToLower(toComplete)
|
||||
|
||||
repo, err := mra.Repository()
|
||||
if err != nil {
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
||||
tools := repo.ModulesByCategory(modules.CategoryTool)
|
||||
|
||||
completions := make([]string, 0, len(tools))
|
||||
for _, t := range tools {
|
||||
toolName := t.Name()
|
||||
if bn, ok := t.Unwrap().(modules.BinaryNamer); ok {
|
||||
if name, err := bn.BinaryName(cmd.Context()); err != nil {
|
||||
continue
|
||||
} else {
|
||||
toolName = name
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(toolName, toComplete) {
|
||||
completions = append(completions, toolName)
|
||||
}
|
||||
}
|
||||
|
||||
return completions, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ func (c Buildr) PluginURLs() (pluginUrls []*url.URL, err error) {
|
|||
return pluginUrls, nil
|
||||
}
|
||||
|
||||
func (c *Buildr) SetupDirectories(buildRDir, repoRoot string, cleanDirectories bool) error {
|
||||
func (c *Buildr) SetupDirectories(buildRDir, projectRoot string, cleanDirectories bool) error {
|
||||
if ucd, err := os.UserCacheDir(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
|
@ -50,7 +50,7 @@ func (c *Buildr) SetupDirectories(buildRDir, repoRoot string, cleanDirectories b
|
|||
if c.BinariesDirectory == "" {
|
||||
c.BinariesDirectory = filepath.Join(buildRDir, "bin")
|
||||
} else if !filepath.IsAbs(c.BinariesDirectory) {
|
||||
c.BinariesDirectory = filepath.Join(repoRoot, c.BinariesDirectory)
|
||||
c.BinariesDirectory = filepath.Join(projectRoot, c.BinariesDirectory)
|
||||
}
|
||||
|
||||
if err := createCleanDir(c.BinariesDirectory, false); err != nil {
|
||||
|
@ -60,7 +60,7 @@ func (c *Buildr) SetupDirectories(buildRDir, repoRoot string, cleanDirectories b
|
|||
if c.OutDirectory == "" {
|
||||
c.OutDirectory = filepath.Join(buildRDir, "out")
|
||||
} else if !filepath.IsAbs(c.OutDirectory) {
|
||||
c.OutDirectory = filepath.Join(repoRoot, c.OutDirectory)
|
||||
c.OutDirectory = filepath.Join(projectRoot, c.OutDirectory)
|
||||
}
|
||||
|
||||
if err := createCleanDir(c.OutDirectory, cleanDirectories); err != nil {
|
||||
|
@ -70,7 +70,7 @@ func (c *Buildr) SetupDirectories(buildRDir, repoRoot string, cleanDirectories b
|
|||
if c.LogsDirectory == "" {
|
||||
c.LogsDirectory = filepath.Join(buildRDir, "logs")
|
||||
} else if !filepath.IsAbs(c.LogsDirectory) {
|
||||
c.LogsDirectory = filepath.Join(repoRoot, c.LogsDirectory)
|
||||
c.LogsDirectory = filepath.Join(projectRoot, c.LogsDirectory)
|
||||
}
|
||||
|
||||
if err := createCleanDir(c.LogsDirectory, cleanDirectories); err != nil {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package containers
|
||||
|
||||
import (
|
||||
"compress/bzip2"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -10,6 +9,8 @@ import (
|
|||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/klauspost/pgzip"
|
||||
|
||||
"code.icb4dc0.de/buildr/buildr/internal/ioutils"
|
||||
)
|
||||
|
||||
|
@ -70,7 +71,7 @@ func (p DefaultServerBinPathProvider) ServerBinPath(ctx context.Context, platfor
|
|||
req, err := http.NewRequestWithContext(
|
||||
ctx,
|
||||
http.MethodGet,
|
||||
fmt.Sprintf("https://code.icb4dc0.de/buildr/buildr/releases/download/%s/buildr_%s_%s.bz2", p.currentVersion, platform.OS, platform.Arch),
|
||||
fmt.Sprintf("https://code.icb4dc0.de/buildr/buildr/releases/download/%s/buildr_%s_%s.gz", p.currentVersion, platform.OS, platform.Arch),
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -99,7 +100,12 @@ func (p DefaultServerBinPathProvider) ServerBinPath(ctx context.Context, platfor
|
|||
err = errors.Join(err, outFile.Close())
|
||||
}()
|
||||
|
||||
_, err = ioutils.CopyWithPooledBuffer(outFile, bzip2.NewReader(resp.Body))
|
||||
gzipReader, err := pgzip.NewReader(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create gzip reader: %w", err)
|
||||
}
|
||||
|
||||
_, err = ioutils.CopyWithPooledBuffer(outFile, gzipReader)
|
||||
|
||||
return binaryPath, err
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ func (s *BuildRContainerSpec) containerSpec(buildrExecutableName string) (contai
|
|||
ExposedPorts: []string{"3000/tcp"},
|
||||
Env: map[string]string{
|
||||
"BUILDR_GRPC_SERVE_ADDRESS": "0.0.0.0:3000",
|
||||
"BUILDR_REPO_ROOT": containerRepoRoot,
|
||||
"BUILDR_PROJECT_ROOT": containerRepoRoot,
|
||||
"BUILDR_STATE_FILE_PATH": "/tmp/buildr.state",
|
||||
},
|
||||
Entrypoint: []string{
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
)
|
||||
|
||||
type Spec struct {
|
||||
RepoRoot string
|
||||
ProjectRoot string
|
||||
BinariesDirectory string
|
||||
CacheDirectory string
|
||||
OutDirectory string
|
||||
|
|
|
@ -114,7 +114,7 @@ func (c *containerTask) doExecute(ctx context.Context, spec execution.Spec) (err
|
|||
inputMappings := c.moduleWithMeta.InputMappings()
|
||||
|
||||
if len(inputMappings) == 0 {
|
||||
inputMappings[spec.RepoRoot] = "."
|
||||
inputMappings[spec.ProjectRoot] = "."
|
||||
}
|
||||
|
||||
containerSpec := c.moduleWithMeta.ContainerSpec()
|
||||
|
@ -124,7 +124,7 @@ func (c *containerTask) doExecute(ctx context.Context, spec execution.Spec) (err
|
|||
Image: containerSpec.Image,
|
||||
User: containerSpec.User,
|
||||
Privileged: containerSpec.Privileged,
|
||||
RepoRoot: spec.RepoRoot,
|
||||
RepoRoot: spec.ProjectRoot,
|
||||
Content: inputMappings,
|
||||
BinariesDir: spec.BinariesDirectory,
|
||||
ExtraBinaries: extraBinaries,
|
||||
|
|
|
@ -62,7 +62,7 @@ func (t *localTask) doExecute(ctx context.Context, spec execution.Spec) error {
|
|||
ctx,
|
||||
t.module,
|
||||
spec,
|
||||
spec.RepoRoot,
|
||||
spec.ProjectRoot,
|
||||
t.module.OutDir(),
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -111,7 +111,7 @@ func (t *localTask) executeIsolated(ctx context.Context, spec execution.Spec) er
|
|||
}
|
||||
|
||||
if !filepath.IsAbs(outDir) {
|
||||
outDir = filepath.Join(spec.RepoRoot, outDir)
|
||||
outDir = filepath.Join(spec.ProjectRoot, outDir)
|
||||
}
|
||||
|
||||
ufs := storage.NewUnionFS(outDir)
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package lazy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
@ -19,30 +21,51 @@ func NewFuture[T any](provider func() (T, error)) *Lazy[T] {
|
|||
return l
|
||||
}
|
||||
|
||||
func NewLazyCtx[T any](ctx context.Context, provider func(ctx context.Context) (T, error)) *Lazy[T] {
|
||||
return &Lazy[T]{
|
||||
baseContext: ctx,
|
||||
provider: provider,
|
||||
}
|
||||
}
|
||||
|
||||
func NewLazy[T any](provider func() (T, error)) *Lazy[T] {
|
||||
return &Lazy[T]{
|
||||
provider: provider,
|
||||
baseContext: context.Background(),
|
||||
provider: func(ctx context.Context) (T, error) {
|
||||
return provider()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type Lazy[T any] struct {
|
||||
out T
|
||||
err error
|
||||
provider func() (T, error)
|
||||
once sync.Once
|
||||
out T
|
||||
err error
|
||||
baseContext context.Context
|
||||
provider func(ctx context.Context) (T, error)
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func (l *Lazy[T]) Get() (v T, e error) {
|
||||
if l == nil {
|
||||
return v, ErrEmptyLazy
|
||||
var t T
|
||||
return v, fmt.Errorf("%w type: %T", ErrEmptyLazy, t)
|
||||
}
|
||||
l.once.Do(func() {
|
||||
l.out, l.err = l.provider()
|
||||
ctx, cancel := context.WithCancel(l.baseContext)
|
||||
defer cancel()
|
||||
l.out, l.err = l.provider(ctx)
|
||||
})
|
||||
|
||||
return l.out, l.err
|
||||
}
|
||||
|
||||
func (l *Lazy[T]) Set(t T) {
|
||||
l.provider = func(ctx context.Context) (T, error) {
|
||||
return t, nil
|
||||
}
|
||||
l.out = t
|
||||
}
|
||||
|
||||
func (l *Lazy[T]) MustGet() (v T) {
|
||||
var err error
|
||||
v, err = l.Get()
|
||||
|
|
Loading…
Reference in a new issue