github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/cli/create_cmd.go (about) 1 package cli 2 3 import ( 4 "context" 5 "os" 6 "path/filepath" 7 "time" 8 9 "github.com/spf13/cobra" 10 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 "k8s.io/cli-runtime/pkg/genericclioptions" 12 13 "github.com/tilt-dev/tilt/internal/analytics" 14 engineanalytics "github.com/tilt-dev/tilt/internal/engine/analytics" 15 "github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1" 16 "github.com/tilt-dev/tilt/pkg/model" 17 ) 18 19 // A human-friendly CLI for creating cmds. 20 // 21 // The name is unfortunate. 22 // 23 // (as opposed to the machine-friendly CLIs of create -f or apply -f) 24 type createCmdCmd struct { 25 helper *createHelper 26 27 dir string 28 env []string 29 filewatches []string 30 } 31 32 var _ tiltCmd = &createCmdCmd{} 33 34 func newCreateCmdCmd(streams genericclioptions.IOStreams) *createCmdCmd { 35 helper := newCreateHelper(streams) 36 return &createCmdCmd{ 37 helper: helper, 38 } 39 } 40 41 func (c *createCmdCmd) name() model.TiltSubcommand { return "create" } 42 43 func (c *createCmdCmd) register() *cobra.Command { 44 cmd := &cobra.Command{ 45 Use: "cmd NAME COMMAND [ARG...]", 46 DisableFlagsInUseLine: true, 47 Short: "Create a local command in a running tilt session", 48 Long: `Create a local command in a running tilt session. 49 50 Intended to compose with other Tilt APIs that 51 can restart the command or monitor its status. 52 53 COMMAND should be an executable. A shell script will not work. 54 55 To run a shell script, use 'sh -c' (as shown in the examples). 56 `, 57 Args: cobra.MinimumNArgs(2), 58 Example: ` 59 tilt create cmd my-cmd echo hello world 60 61 tilt create cmd my-cmd sh -c "echo hi && echo bye" 62 `, 63 } 64 65 // Interpret any flags after the object name as part of the 66 // command args. 67 cmd.Flags().SetInterspersed(false) 68 69 cmd.Flags().StringVarP(&c.dir, "workdir", "w", "", 70 "Working directory of the command. If not specified, uses the current working directory.") 71 cmd.Flags().StringArrayVarP(&c.env, "env", "e", nil, 72 "Set environment variables in the form NAME=VALUE.") 73 cmd.Flags().StringSliceVar(&c.filewatches, "filewatch", nil, 74 ("Re-run the command whenever the named filewatches detect a change. " + 75 "See 'tilt create filewatch' for more.")) 76 77 c.helper.addFlags(cmd) 78 79 return cmd 80 } 81 82 func (c *createCmdCmd) run(ctx context.Context, args []string) error { 83 a := analytics.Get(ctx) 84 cmdTags := engineanalytics.CmdTags(map[string]string{}) 85 a.Incr("cmd.create-cmd", cmdTags.AsMap()) 86 defer a.Flush(time.Second) 87 88 err := c.helper.interpretFlags(ctx) 89 if err != nil { 90 return err 91 } 92 93 cmd, err := c.object(args) 94 if err != nil { 95 return err 96 } 97 98 return c.helper.create(ctx, cmd) 99 } 100 101 // Interprets the flags specified on the commandline to the Cmd to create. 102 func (c *createCmdCmd) object(args []string) (*v1alpha1.Cmd, error) { 103 name := args[0] 104 command := args[1:] 105 106 dir, err := c.workdir() 107 if err != nil { 108 return nil, err 109 } 110 111 env := c.env 112 restartOn := c.restartOn() 113 cmd := v1alpha1.Cmd{ 114 ObjectMeta: metav1.ObjectMeta{ 115 Name: name, 116 }, 117 Spec: v1alpha1.CmdSpec{ 118 Args: command, 119 Dir: dir, 120 Env: env, 121 RestartOn: restartOn, 122 }, 123 } 124 return &cmd, nil 125 } 126 127 // Determine the working directory of the command. 128 func (c *createCmdCmd) workdir() (string, error) { 129 cwd, err := os.Getwd() 130 if err != nil { 131 return "", err 132 } 133 134 if c.dir == "" { 135 return cwd, nil 136 } 137 if filepath.IsAbs(c.dir) { 138 return c.dir, nil 139 } 140 return filepath.Join(cwd, c.dir), nil 141 } 142 143 // Determine the restart conditions of the command. 144 func (c *createCmdCmd) restartOn() *v1alpha1.RestartOnSpec { 145 return &v1alpha1.RestartOnSpec{ 146 FileWatches: c.filewatches, 147 } 148 }