github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/cli/args.go (about) 1 package cli 2 3 import ( 4 "bufio" 5 "bytes" 6 "context" 7 "fmt" 8 "strings" 9 "time" 10 11 "github.com/kballard/go-shellquote" 12 "github.com/pkg/errors" 13 "github.com/spf13/cobra" 14 "k8s.io/apimachinery/pkg/types" 15 "k8s.io/cli-runtime/pkg/genericclioptions" 16 "k8s.io/kubectl/pkg/cmd/util/editor" 17 18 "github.com/tilt-dev/tilt/internal/analytics" 19 engineanalytics "github.com/tilt-dev/tilt/internal/engine/analytics" 20 "github.com/tilt-dev/tilt/internal/sliceutils" 21 "github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1" 22 "github.com/tilt-dev/tilt/pkg/logger" 23 "github.com/tilt-dev/tilt/pkg/model" 24 ) 25 26 type argsCmd struct { 27 streams genericclioptions.IOStreams 28 clear bool 29 } 30 31 func newArgsCmd(streams genericclioptions.IOStreams) *argsCmd { 32 return &argsCmd{ 33 streams: streams, 34 } 35 } 36 37 func (c *argsCmd) name() model.TiltSubcommand { return "args" } 38 39 func (c *argsCmd) register() *cobra.Command { 40 cmd := &cobra.Command{ 41 Use: "args [<flags>] [-- <Tiltfile args>]", 42 DisableFlagsInUseLine: true, 43 Short: "Changes the Tiltfile args in use by a running Tilt", 44 Long: `Changes the Tiltfile args in use by a running Tilt. 45 46 The "args" can be used to specify what resources Tilt enables. They can also 47 be used to define custom key-value pairs that can be accessed in a Tiltfile 48 by calling "config.parse()". 49 50 If no args are explicitly specified, (i.e., just "tilt args"), it opens the current args for editing in 51 the editor defined by your TILT_EDITOR or EDITOR environment variables, or fall back to 52 an OS-appropriate default. 53 54 Note that Tiltfile arguments do not affect built-in Tilt args (i.e., the things that show up in "tilt up --help", such as "--legacy", "--port"), and they 55 are defined after built-in args, following a "--".`, 56 Example: `# Set new args 57 tilt args frontend_service backend_service -- --debug on 58 59 # Edit the current args 60 tilt args 61 62 # Use an alternative editor 63 EDITOR=nano tilt args`, 64 } 65 66 addConnectServerFlags(cmd) 67 cmd.Flags().BoolVar(&c.clear, "clear", false, "Clear the Tiltfile args, as if you'd run tilt with no args") 68 69 return cmd 70 } 71 72 func parseEditResult(b []byte) ([]string, error) { 73 sc := bufio.NewScanner(bytes.NewReader(b)) 74 var argsLine *string 75 for sc.Scan() { 76 line := strings.TrimSpace(sc.Text()) 77 if len(line) == 0 || line[0] == '#' { 78 continue 79 } 80 if argsLine != nil { 81 return nil, errors.New("cannot have multiple non-comment lines") 82 } 83 s := line 84 argsLine = &s 85 } 86 if argsLine == nil { 87 return nil, errors.New("must have exactly one non-comment line, found zero. If you want to clear the args, use `tilt args --clear`") 88 } 89 args, err := shellquote.Split(*argsLine) 90 if err != nil { 91 return nil, errors.Wrapf(err, "error parsing %q", string(b)) 92 } 93 94 return args, nil 95 } 96 97 func (c *argsCmd) run(ctx context.Context, args []string) error { 98 ctx = logger.WithLogger(ctx, logger.NewLogger(logger.Get(ctx).Level(), c.streams.ErrOut)) 99 100 ctrlclient, err := newClient(ctx) 101 if err != nil { 102 return err 103 } 104 105 var tf v1alpha1.Tiltfile 106 err = ctrlclient.Get(ctx, types.NamespacedName{Name: model.MainTiltfileManifestName.String()}, &tf) 107 if err != nil { 108 return err 109 } 110 111 tags := make(engineanalytics.CmdTags) 112 if c.clear { 113 if len(args) != 0 { 114 return errors.New("--clear cannot be specified with other values") 115 } 116 args = nil 117 tags["clear"] = "true" 118 } else if len(args) == 0 { 119 input := fmt.Sprintf("# edit args for the running Tilt here\n%s\n", shellquote.Join(tf.Spec.Args...)) 120 e := editor.NewDefaultEditor([]string{"TILT_EDITOR", "EDITOR"}) 121 b, _, err := e.LaunchTempFile("", "", strings.NewReader(input)) 122 if err != nil { 123 return err 124 } 125 126 args, err = parseEditResult(b) 127 if err != nil { 128 return err 129 } 130 tags["edit"] = "true" 131 } else { 132 tags["set"] = "true" 133 } 134 135 a := analytics.Get(ctx) 136 a.Incr("cmd.args", tags.AsMap()) 137 defer a.Flush(time.Second) 138 139 if sliceutils.StringSliceEquals(tf.Spec.Args, args) { 140 logger.Get(ctx).Infof("Tilt is already running with those args -- no action taken") 141 return nil 142 } 143 tf.Spec.Args = args 144 145 err = ctrlclient.Update(ctx, &tf) 146 if err != nil { 147 return err 148 } 149 150 logger.Get(ctx).Infof("Changed config args for Tilt running at %s to %v", apiHost(), args) 151 152 return nil 153 }