Files
Kevin Minehart 13f4cf162e CI: move grafana-build into pkg/build (#105640)
* move grafana-build into pkg/build
2025-05-20 10:48:00 -05:00

228 lines
5.4 KiB
Go

package pipeline
import (
"context"
"errors"
"fmt"
"log/slog"
"dagger.io/dagger"
"github.com/grafana/grafana/pkg/build/daggerbuild/cliutil"
"github.com/urfave/cli/v2"
)
var (
ErrorFlagNotProvided = errors.New("flag not provided, ex: '--go-version=1.21.0'")
)
type ArgumentType int
const (
ArgumentTypeString ArgumentType = iota
ArgumentTypeInt64
ArgumentTypeDirectory
ArgumentTypeCacheVolume
ArgumentTypeFile
ArgumentTypeBool
)
type ArgumentOpts struct {
Log *slog.Logger
CLIContext cliutil.CLIContext
Client *dagger.Client
State StateHandler
Platform dagger.Platform
}
type ArgumentValueFunc func(ctx context.Context, opts *ArgumentOpts) (any, error)
// An Argument is an input to a artifact command.
// It wraps the concept of a general CLI "Flag" to allow it to
// All arguments are required.
type Argument struct {
ArgumentType ArgumentType
Name string
Description string
// ValueFunc defines the behavior for how this artifact is populated.
// Maybe this could be an interface instead.
ValueFunc ArgumentValueFunc
// If Flags are set here, then it is safe to assume that these flags will be globally set and any other pipeline / artifact using this
// argument will be able to use these same flags.
// Example: `--grafana-dir`, `--grafana-ref`, etc.
Flags []cli.Flag
// Some arguments require other arguments to be set in order to derive their value.
// For example, the "version" argument(s) require the GrafanaDir (if the --version flag) was not set.
Requires []Argument
}
func (a Argument) Directory(ctx context.Context, opts *ArgumentOpts) (*dagger.Directory, error) {
if a.ValueFunc == nil {
return nil, fmt.Errorf("error: %w. Flag missing: %s (%s)", ErrorFlagNotProvided, a.Name, a.Description)
}
value, err := a.ValueFunc(ctx, opts)
if err != nil {
return nil, err
}
dir, ok := value.(*dagger.Directory)
if !ok {
return nil, errors.New("value returned by valuefunc is not a *dagger.Directory")
}
return dir, nil
}
func (a Argument) MustDirectory(ctx context.Context, opts *ArgumentOpts) *dagger.Directory {
v, err := a.Directory(ctx, opts)
if err != nil {
panic(err)
}
return v
}
func (a Argument) String(ctx context.Context, opts *ArgumentOpts) (string, error) {
if a.ValueFunc == nil {
return "", fmt.Errorf("error: %w. %s (%s)", ErrorFlagNotProvided, a.Name, a.Description)
}
value, err := a.ValueFunc(ctx, opts)
if err != nil {
return "", err
}
v, ok := value.(string)
if !ok {
return "", errors.New("value returned by valuefunc is not a string")
}
return v, nil
}
func (a Argument) MustString(ctx context.Context, opts *ArgumentOpts) string {
v, err := a.String(ctx, opts)
if err != nil {
panic(err)
}
return v
}
func (a Argument) Int64(ctx context.Context, opts *ArgumentOpts) (int64, error) {
if a.ValueFunc == nil {
return 0, fmt.Errorf("error: %w. %s (%s)", ErrorFlagNotProvided, a.Name, a.Description)
}
value, err := a.ValueFunc(ctx, opts)
if err != nil {
return 0, err
}
v, ok := value.(int64)
if !ok {
return 0, errors.New("value returned by valuefunc is not an int64")
}
return v, nil
}
func (a Argument) MustInt64(ctx context.Context, opts *ArgumentOpts) int64 {
v, err := a.Int64(ctx, opts)
if err != nil {
panic(err)
}
return v
}
func (a Argument) Bool(ctx context.Context, opts *ArgumentOpts) (bool, error) {
if a.ValueFunc == nil {
return false, fmt.Errorf("error: %w. %s (%s)", ErrorFlagNotProvided, a.Name, a.Description)
}
value, err := a.ValueFunc(ctx, opts)
if err != nil {
return false, err
}
v, ok := value.(bool)
if !ok {
return false, errors.New("value returned by valuefunc is not a bool")
}
return v, nil
}
func (a Argument) MustBool(ctx context.Context, opts *ArgumentOpts) bool {
v, err := a.Bool(ctx, opts)
if err != nil {
panic(err)
}
return v
}
func (a Argument) File(ctx context.Context, opts *ArgumentOpts) (*dagger.File, error) {
if a.ValueFunc == nil {
return nil, fmt.Errorf("error: %w. %s (%s)", ErrorFlagNotProvided, a.Name, a.Description)
}
value, err := a.ValueFunc(ctx, opts)
if err != nil {
return nil, err
}
dir, ok := value.(*dagger.File)
if !ok {
return nil, errors.New("value returned by valuefunc is not a *dagger.File")
}
return dir, nil
}
func (a Argument) MustFile(ctx context.Context, opts *ArgumentOpts) *dagger.File {
v, err := a.File(ctx, opts)
if err != nil {
panic(err)
}
return v
}
func (a Argument) CacheVolume(ctx context.Context, opts *ArgumentOpts) (*dagger.CacheVolume, error) {
if a.ValueFunc == nil {
return nil, fmt.Errorf("error: %w. %s (%s)", ErrorFlagNotProvided, a.Name, a.Description)
}
value, err := a.ValueFunc(ctx, opts)
if err != nil {
return nil, err
}
dir, ok := value.(*dagger.CacheVolume)
if !ok {
return nil, errors.New("value returned by valuefunc is not a *dagger.File")
}
return dir, nil
}
func (a Argument) MustCacheVolume(ctx context.Context, opts *ArgumentOpts) *dagger.CacheVolume {
v, err := a.CacheVolume(ctx, opts)
if err != nil {
panic(err)
}
return v
}
func StringFlagValueFunc(f *cli.StringFlag) func(context.Context, *ArgumentOpts) (any, error) {
return func(ctx context.Context, opts *ArgumentOpts) (any, error) {
return opts.CLIContext.String(f.Name), nil
}
}
func NewStringFlagArgument(flag *cli.StringFlag) Argument {
return Argument{
Name: flag.Name,
Description: flag.Usage,
Flags: []cli.Flag{
flag,
},
ValueFunc: StringFlagValueFunc(flag),
}
}