dagger do
action options flags
Signed-off-by: Joel Longtine <joel@dagger.io>
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
"go.dagger.io/dagger/cmd/dagger/cmd/common"
|
||||
"go.dagger.io/dagger/cmd/dagger/logger"
|
||||
@@ -21,8 +22,8 @@ import (
|
||||
)
|
||||
|
||||
var doCmd = &cobra.Command{
|
||||
Use: "do [OPTIONS] ACTION [SUBACTION...]",
|
||||
Short: "Execute a dagger action.",
|
||||
Use: "do ACTION [SUBACTION...]",
|
||||
// Short: "Execute a dagger action.",
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
@@ -30,10 +31,16 @@ var doCmd = &cobra.Command{
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
// Don't fail on unknown flags for the first parse
|
||||
FParseErrWhitelist: cobra.FParseErrWhitelist{
|
||||
UnknownFlags: true,
|
||||
},
|
||||
// We're going to take care of flag parsing ourselves
|
||||
DisableFlagParsing: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) < 1 {
|
||||
doHelpCmd(cmd, nil)
|
||||
return
|
||||
cmd.Flags().Parse(args)
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -47,6 +54,58 @@ var doCmd = &cobra.Command{
|
||||
lg.Fatal().Err(err).Msg("--platform requires --experimental flag")
|
||||
}
|
||||
|
||||
targetPath := getTargetPath(cmd.Flags().Args())
|
||||
|
||||
daggerPlan, err := loadPlan(viper.GetString("plan"))
|
||||
if err != nil {
|
||||
if viper.GetBool("help") {
|
||||
doHelpCmd(cmd, nil, nil, nil, targetPath, nil)
|
||||
os.Exit(0)
|
||||
}
|
||||
err = fmt.Errorf("failed to load plan: %w", err)
|
||||
doHelpCmd(cmd, nil, nil, nil, targetPath, []string{err.Error()})
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
action := daggerPlan.Action().FindByPath(targetPath)
|
||||
|
||||
if action == nil {
|
||||
selectorStrs := []string{}
|
||||
for _, selector := range targetPath.Selectors()[1:] {
|
||||
selectorStrs = append(selectorStrs, selector.String())
|
||||
}
|
||||
targetStr := strings.Join(selectorStrs, " ")
|
||||
|
||||
err = fmt.Errorf("action not found: %s", targetStr)
|
||||
// Find closest action
|
||||
action = daggerPlan.Action().FindClosest(targetPath)
|
||||
targetPath = action.Path
|
||||
doHelpCmd(cmd, daggerPlan, action, nil, action.Path, []string{err.Error()})
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
actionFlags := getActionFlags(action)
|
||||
|
||||
cmd.Flags().AddFlagSet(actionFlags)
|
||||
|
||||
cmd.Flags().ParseErrorsWhitelist = pflag.ParseErrorsWhitelist{
|
||||
UnknownFlags: false,
|
||||
}
|
||||
err = cmd.Flags().Parse(args)
|
||||
if err != nil {
|
||||
doHelpCmd(cmd, daggerPlan, action, actionFlags, targetPath, []string{err.Error()})
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if len(cmd.Flags().Args()) < 1 || viper.GetBool("help") {
|
||||
doHelpCmd(cmd, daggerPlan, action, actionFlags, targetPath, []string{})
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if f := viper.GetString("log-format"); f == "tty" || f == "auto" && term.IsTerminal(int(os.Stdout.Fd())) {
|
||||
tty, err = logger.NewTTYOutput(os.Stderr)
|
||||
if err != nil {
|
||||
@@ -61,24 +120,28 @@ var doCmd = &cobra.Command{
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
cl := common.NewClient(ctx)
|
||||
|
||||
p, err := loadPlan()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to load plan")
|
||||
}
|
||||
target := getTargetPath(args)
|
||||
actionFlags.VisitAll(func(flag *pflag.Flag) {
|
||||
if cmd.Flags().Changed(flag.Name) {
|
||||
fmt.Printf("Changed: %s: %s\n", flag.Name, cmd.Flags().Lookup(flag.Name).Value.String())
|
||||
flagPath := []cue.Selector{}
|
||||
flagPath = append(flagPath, targetPath.Selectors()...)
|
||||
flagPath = append(flagPath, cue.Str(flag.Name))
|
||||
daggerPlan.Source().FillPath(cue.MakePath(flagPath...), viper.Get(flag.Name))
|
||||
}
|
||||
})
|
||||
|
||||
doneCh := common.TrackCommand(ctx, cmd, &telemetry.Property{
|
||||
Name: "action",
|
||||
Value: target.String(),
|
||||
Value: targetPath.String(),
|
||||
})
|
||||
|
||||
err = cl.Do(ctx, p.Context(), func(ctx context.Context, s *solver.Solver) error {
|
||||
return p.Do(ctx, target, s)
|
||||
err = cl.Do(ctx, daggerPlan.Context(), func(ctx context.Context, s *solver.Solver) error {
|
||||
return daggerPlan.Do(ctx, targetPath, s)
|
||||
})
|
||||
|
||||
<-doneCh
|
||||
|
||||
p.Context().TempDirs.Clean()
|
||||
daggerPlan.Context().TempDirs.Clean()
|
||||
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to execute plan")
|
||||
@@ -86,9 +149,7 @@ var doCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func loadPlan() (*plan.Plan, error) {
|
||||
planPath := viper.GetString("plan")
|
||||
|
||||
func loadPlan(planPath string) (*plan.Plan, error) {
|
||||
// support only local filesystem paths
|
||||
// even though CUE supports loading module and package names
|
||||
absPlanPath, err := filepath.Abs(planPath)
|
||||
@@ -115,27 +176,85 @@ func getTargetPath(args []string) cue.Path {
|
||||
return cue.MakePath(selectors...)
|
||||
}
|
||||
|
||||
func doHelpCmd(cmd *cobra.Command, _ []string) {
|
||||
func doHelpCmd(cmd *cobra.Command, daggerPlan *plan.Plan, action *plan.Action, actionFlags *pflag.FlagSet, target cue.Path, preamble []string) {
|
||||
lg := logger.New()
|
||||
|
||||
fmt.Println(cmd.Short)
|
||||
if len(preamble) > 0 {
|
||||
fmt.Println(strings.Join(preamble, "\n"))
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
err := printActions(os.Stdout, getTargetPath(cmd.Flags().Args()))
|
||||
target = cue.MakePath(target.Selectors()[1:]...)
|
||||
|
||||
if action != nil {
|
||||
selectorStrs := []string{}
|
||||
for _, selector := range target.Selectors() {
|
||||
selectorStrs = append(selectorStrs, selector.String())
|
||||
}
|
||||
targetStr := strings.Join(selectorStrs, " ")
|
||||
fmt.Printf("Usage: \n dagger do %s [flags]\n\n", targetStr)
|
||||
if actionFlags != nil {
|
||||
fmt.Println("Options")
|
||||
actionFlags.VisitAll(func(flag *pflag.Flag) {
|
||||
flag.Hidden = false
|
||||
})
|
||||
fmt.Println(actionFlags.FlagUsages())
|
||||
actionFlags.VisitAll(func(flag *pflag.Flag) {
|
||||
flag.Hidden = true
|
||||
})
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Usage: \n dagger do [flags]")
|
||||
}
|
||||
|
||||
var err error
|
||||
if daggerPlan != nil {
|
||||
err = printActions(daggerPlan, action, os.Stdout, target)
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s", cmd.UsageString())
|
||||
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to load plan")
|
||||
if err == nil {
|
||||
lg.Fatal().Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
func printActions(w io.Writer, target cue.Path) error {
|
||||
p, err := loadPlan()
|
||||
if err != nil {
|
||||
return err
|
||||
func getActionFlags(action *plan.Action) *pflag.FlagSet {
|
||||
flags := pflag.NewFlagSet("action inputs", pflag.ContinueOnError)
|
||||
flags.Usage = func() {}
|
||||
|
||||
if action == nil {
|
||||
panic("action is nil")
|
||||
}
|
||||
|
||||
if action.Inputs() == nil {
|
||||
panic("action inputs is nil")
|
||||
}
|
||||
|
||||
for _, input := range action.Inputs() {
|
||||
switch input.Type {
|
||||
case "string":
|
||||
flags.String(input.Name, "", input.Documentation)
|
||||
case "int":
|
||||
flags.Int(input.Name, 0, input.Documentation)
|
||||
case "bool":
|
||||
flags.Bool(input.Name, false, input.Documentation)
|
||||
case "float":
|
||||
flags.Float64(input.Name, 0, input.Documentation)
|
||||
case "number":
|
||||
flags.Float64(input.Name, 0, input.Documentation)
|
||||
default:
|
||||
}
|
||||
flags.MarkHidden(input.Name)
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
func printActions(p *plan.Plan, action *plan.Action, w io.Writer, target cue.Path) error {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
action := p.Action().FindByPath(target)
|
||||
if action == nil {
|
||||
return fmt.Errorf("action %s not found", target.String())
|
||||
}
|
||||
@@ -168,9 +287,20 @@ func init() {
|
||||
doCmd.Flags().StringArray("cache-from", []string{},
|
||||
"External cache sources (eg. user/app:cache, type=local,src=path/to/dir)")
|
||||
|
||||
doCmd.SetHelpFunc(doHelpCmd)
|
||||
|
||||
if err := viper.BindPFlags(doCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
doCmd.SetUsageTemplate(`{{if .HasAvailableSubCommands}}
|
||||
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
|
||||
Aliases:
|
||||
{{.NameAndAliases}}{{end}}{{if .HasExample}}
|
||||
Examples:
|
||||
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}
|
||||
Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
|
||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
|
||||
Flags:
|
||||
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
|
||||
Global Flags:
|
||||
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
|
||||
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
|
||||
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
|
||||
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
|
||||
`)
|
||||
}
|
||||
|
Reference in New Issue
Block a user