@@ -11,6 +11,7 @@ import (
1111
1212 "cuelang.org/go/cue"
1313 "github.com/spf13/cobra"
14+ "github.com/spf13/pflag"
1415 "github.com/spf13/viper"
1516 "go.dagger.io/dagger/cmd/dagger/cmd/common"
1617 "go.dagger.io/dagger/cmd/dagger/logger"
@@ -21,27 +22,88 @@ import (
2122)
2223
2324var doCmd = & cobra.Command {
24- Use : "do [OPTIONS] ACTION [SUBACTION...]" ,
25- Short : "Execute a dagger action." ,
25+ Use : "do ACTION [SUBACTION...]" ,
26+ // Short: "Execute a dagger action.",
2627 PreRun : func (cmd * cobra.Command , args []string ) {
2728 // Fix Viper bug for duplicate flags:
2829 // https://github.com/spf13/viper/issues/233
2930 if err := viper .BindPFlags (cmd .Flags ()); err != nil {
3031 panic (err )
3132 }
3233 },
34+ // Don't fail on unknown flags for the first parse
35+ FParseErrWhitelist : cobra.FParseErrWhitelist {
36+ UnknownFlags : true ,
37+ },
38+ // We're going to take care of flag parsing ourselves
39+ DisableFlagParsing : true ,
3340 Run : func (cmd * cobra.Command , args []string ) {
34- if len (args ) < 1 {
35- doHelpCmd (cmd , nil )
36- return
37- }
38-
3941 var (
4042 lg = logger .New ()
4143 tty * logger.TTYOutput
4244 err error
4345 )
4446
47+ cmd .Flags ().Parse (args )
48+
49+ targetPath := getTargetPath (cmd .Flags ().Args ())
50+
51+ daggerPlan , err := loadPlan (viper .GetString ("plan" ))
52+ if err != nil && ! viper .GetBool ("help" ) {
53+ err = fmt .Errorf ("failed to load plan: %w" , err )
54+ doHelpCmd (cmd , nil , nil , nil , targetPath , []string {err .Error ()})
55+ os .Exit (1 )
56+ } else if err != nil && viper .GetBool ("help" ) {
57+ doHelpCmd (cmd , nil , nil , nil , targetPath , nil )
58+ os .Exit (0 )
59+ }
60+
61+ action := daggerPlan .Action ().FindByPath (targetPath )
62+
63+ if action == nil {
64+ selectorStrs := []string {}
65+ for _ , selector := range targetPath .Selectors ()[1 :] {
66+ selectorStrs = append (selectorStrs , selector .String ())
67+ }
68+ targetStr := strings .Join (selectorStrs , " " )
69+
70+ err = fmt .Errorf ("failed to find action: %s" , targetStr )
71+ l := len (targetPath .Selectors ())
72+ for i := l - 1 ; i >= 0 ; i -- {
73+ tmpPath := cue .MakePath (targetPath .Selectors ()[:i ]... )
74+ tmpAction := daggerPlan .Action ().FindByPath (tmpPath )
75+ if tmpAction != nil {
76+ action = tmpAction
77+ targetPath = tmpPath
78+ break
79+ }
80+ }
81+ doHelpCmd (cmd , daggerPlan , action , nil , targetPath , []string {err .Error ()})
82+ os .Exit (1 )
83+ }
84+
85+ actionFlags := getActionFlags (action )
86+
87+ cmd .Flags ().AddFlagSet (actionFlags )
88+
89+ cmd .Flags ().ParseErrorsWhitelist = pflag.ParseErrorsWhitelist {
90+ UnknownFlags : false ,
91+ }
92+ err = cmd .Flags ().Parse (args )
93+ if err != nil {
94+ doHelpCmd (cmd , daggerPlan , action , actionFlags , targetPath , []string {err .Error ()})
95+ os .Exit (1 )
96+ }
97+
98+ if err := viper .BindPFlags (cmd .Flags ()); err != nil {
99+ panic (err )
100+ }
101+
102+ if len (cmd .Flags ().Args ()) < 1 || viper .GetBool ("help" ) {
103+ doHelpCmd (cmd , daggerPlan , action , actionFlags , targetPath , []string {})
104+ os .Exit (0 )
105+ }
106+
45107 if f := viper .GetString ("log-format" ); f == "tty" || f == "auto" && term .IsTerminal (int (os .Stdout .Fd ())) {
46108 tty , err = logger .NewTTYOutput (os .Stderr )
47109 if err != nil {
@@ -56,34 +118,36 @@ var doCmd = &cobra.Command{
56118 ctx := lg .WithContext (cmd .Context ())
57119 cl := common .NewClient (ctx )
58120
59- p , err := loadPlan ()
60- if err != nil {
61- lg .Fatal ().Err (err ).Msg ("failed to load plan" )
62- }
63- target := getTargetPath (args )
121+ actionFlags .VisitAll (func (flag * pflag.Flag ) {
122+ if cmd .Flags ().Changed (flag .Name ) {
123+ fmt .Printf ("Changed: %s: %s\n " , flag .Name , cmd .Flags ().Lookup (flag .Name ).Value .String ())
124+ flagPath := []cue.Selector {}
125+ flagPath = append (flagPath , targetPath .Selectors ()... )
126+ flagPath = append (flagPath , cue .Str (flag .Name ))
127+ daggerPlan .Source ().FillPath (cue .MakePath (flagPath ... ), viper .Get (flag .Name ))
128+ }
129+ })
64130
65131 doneCh := common .TrackCommand (ctx , cmd , & telemetry.Property {
66132 Name : "action" ,
67- Value : target .String (),
133+ Value : targetPath .String (),
68134 })
69135
70- err = cl .Do (ctx , p .Context (), func (ctx context.Context , s solver.Solver ) error {
71- return p .Do (ctx , target , s )
136+ err = cl .Do (ctx , daggerPlan .Context (), func (ctx context.Context , s solver.Solver ) error {
137+ return daggerPlan .Do (ctx , targetPath , s )
72138 })
73139
74140 <- doneCh
75141
76- p .Context ().TempDirs .Clean ()
142+ daggerPlan .Context ().TempDirs .Clean ()
77143
78144 if err != nil {
79145 lg .Fatal ().Err (err ).Msg ("failed to execute plan" )
80146 }
81147 },
82148}
83149
84- func loadPlan () (* plan.Plan , error ) {
85- planPath := viper .GetString ("plan" )
86-
150+ func loadPlan (planPath string ) (* plan.Plan , error ) {
87151 // support only local filesystem paths
88152 // even though CUE supports loading module and package names
89153 absPlanPath , err := filepath .Abs (planPath )
@@ -110,27 +174,87 @@ func getTargetPath(args []string) cue.Path {
110174 return cue .MakePath (selectors ... )
111175}
112176
113- func doHelpCmd (cmd * cobra.Command , _ []string ) {
177+ func doHelpCmd (cmd * cobra.Command , daggerPlan * plan. Plan , action * plan. Action , actionFlags * pflag. FlagSet , target cue. Path , preamble []string ) {
114178 lg := logger .New ()
115179
116- fmt .Println (cmd .Short )
180+ if len (preamble ) > 0 {
181+ fmt .Println (strings .Join (preamble , "\n " ))
182+ fmt .Println ()
183+ }
117184
118- err := printActions (os .Stdout , getTargetPath (cmd .Flags ().Args ()))
185+ target = cue .MakePath (target .Selectors ()[1 :]... )
186+
187+ // fmt.Println(cmd.Short)
188+
189+ if action != nil {
190+ selectorStrs := []string {}
191+ for _ , selector := range target .Selectors () {
192+ selectorStrs = append (selectorStrs , selector .String ())
193+ }
194+ targetStr := strings .Join (selectorStrs , " " )
195+ fmt .Printf ("Usage: \n dagger do %s [flags]\n \n " , targetStr )
196+ if actionFlags != nil {
197+ fmt .Println ("Options" )
198+ actionFlags .VisitAll (func (flag * pflag.Flag ) {
199+ flag .Hidden = false
200+ })
201+ fmt .Println (actionFlags .FlagUsages ())
202+ actionFlags .VisitAll (func (flag * pflag.Flag ) {
203+ flag .Hidden = true
204+ })
205+ }
206+ } else {
207+ fmt .Println ("Usage: \n dagger do [flags]" )
208+ }
209+
210+ var err error
211+ if daggerPlan != nil {
212+ err = printActions (daggerPlan , action , os .Stdout , target )
213+ }
119214
120215 fmt .Printf ("\n %s" , cmd .UsageString ())
121216
122- if err ! = nil {
123- lg .Fatal ().Err (err ). Msg ( "failed to load plan" )
217+ if err = = nil {
218+ lg .Fatal ().Err (err )
124219 }
125220}
126221
127- func printActions (w io.Writer , target cue.Path ) error {
128- p , err := loadPlan ()
129- if err != nil {
130- return err
222+ func getActionFlags (action * plan.Action ) * pflag.FlagSet {
223+ flags := pflag .NewFlagSet ("action inputs" , pflag .ContinueOnError )
224+ flags .Usage = func () {}
225+
226+ if action == nil {
227+ panic ("action is nil" )
228+ }
229+
230+ if action .Inputs == nil {
231+ panic ("action inputs is nil" )
232+ }
233+
234+ for _ , input := range action .Inputs {
235+ switch input .Type {
236+ case "string" :
237+ flags .String (input .Name , "" , input .Documentation )
238+ case "int" :
239+ flags .Int (input .Name , 0 , input .Documentation )
240+ case "bool" :
241+ flags .Bool (input .Name , false , input .Documentation )
242+ case "float" :
243+ flags .Float64 (input .Name , 0 , input .Documentation )
244+ case "number" :
245+ flags .Float64 (input .Name , 0 , input .Documentation )
246+ default :
247+ }
248+ flags .MarkHidden (input .Name )
249+ }
250+ return flags
251+ }
252+
253+ func printActions (p * plan.Plan , action * plan.Action , w io.Writer , target cue.Path ) error {
254+ if p == nil {
255+ return nil
131256 }
132257
133- action := p .Action ().FindByPath (target )
134258 if action == nil {
135259 return fmt .Errorf ("action %s not found" , target .String ())
136260 }
@@ -162,9 +286,20 @@ func init() {
162286 doCmd .Flags ().StringArray ("cache-from" , []string {},
163287 "External cache sources (eg. user/app:cache, type=local,src=path/to/dir)" )
164288
165- doCmd .SetHelpFunc (doHelpCmd )
166-
167- if err := viper .BindPFlags (doCmd .Flags ()); err != nil {
168- panic (err )
169- }
289+ doCmd .SetUsageTemplate (`{{if .HasAvailableSubCommands}}
290+ {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
291+ Aliases:
292+ {{.NameAndAliases}}{{end}}{{if .HasExample}}
293+ Examples:
294+ {{.Example}}{{end}}{{if .HasAvailableSubCommands}}
295+ Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
296+ {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
297+ Flags:
298+ {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
299+ Global Flags:
300+ {{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
301+ Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
302+ {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
303+ Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
304+ ` )
170305}
0 commit comments