Skip to content

Commit

Permalink
cli: tilt args allows editing args in $EDITOR (#5241)
Browse files Browse the repository at this point in the history
  • Loading branch information
landism committed Dec 3, 2021
1 parent d92932e commit 34b9d2b
Show file tree
Hide file tree
Showing 10 changed files with 552 additions and 78 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ require (
github.com/grpc-ecosystem/grpc-gateway v1.16.0
github.com/jonboulle/clockwork v0.2.2
github.com/json-iterator/go v1.1.11
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/looplab/tarjan v0.0.0-20161115091335-9cc6d6cebfb5
github.com/mattn/go-colorable v0.1.7
github.com/mattn/go-isatty v0.0.12
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
Expand Down
129 changes: 92 additions & 37 deletions internal/cli/args.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
package cli

import (
"bufio"
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strings"

"github.com/kballard/go-shellquote"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubectl/pkg/cmd/util/editor"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1"
"github.com/tilt-dev/tilt/pkg/model"
)

type argsCmd struct {
clear bool
post httpPoster
}

func newArgsCmd() *argsCmd {
return &argsCmd{post: http.Post}
return &argsCmd{}
}

func (c *argsCmd) name() model.TiltSubcommand { return "args" }
Expand All @@ -32,13 +34,24 @@ func (c *argsCmd) register() *cobra.Command {
DisableFlagsInUseLine: true,
Short: "Changes the Tiltfile args in use by a running Tilt",
Long: `Changes the Tiltfile args in use by a running Tilt.
If no args are specified, (i.e., just "tilt args"), opens the current args for editing in
the editor defined by your TILT_EDITOR or EDITOR environment variables, or fall back to
an OS-appropriate default.
Note that this does not affect built-in Tilt args (e.g. --hud, --host), but rather the extra args that come after,
i.e., those specifying which resources to run and/or handled by a Tiltfile calling config.parse.
The editor can be skipped by providing new args on the command line, e.g.: "tilt args svc1 svc2".
To provide args starting with --, insert a standalone --, e.g.:
# Edit the args
tilt args
# Use an alternate editor
EDITOR=nano tilt args
# skip the editor
# note: "--" here indicates the end of the tilt args and the start of the tiltfile args
tilt args -- --foo=bar frontend backend
Note that this does not affect built-in Tilt args (e.g. --hud, --host), but rather the extra args that come after,
i.e., those specifying which resources to run and/or handled by a Tiltfile calling config.parse.
`,
}

Expand All @@ -48,44 +61,86 @@ tilt args -- --foo=bar frontend backend
return cmd
}

type httpPoster func(url string, contentType string, body io.Reader) (*http.Response, error)
func newClient(ctx context.Context) (client.Client, error) {
getter, err := wireClientGetter(ctx)
if err != nil {
return nil, err
}

cfg, err := getter.ToRESTConfig()
if err != nil {
return nil, err
}

func (c *argsCmd) run(ctx context.Context, args []string) error {
// require --clear instead of an empty args list to ensure an experimental `tilt flags` doesn't unintentionally wipe state
if len(args) == 0 {
if !c.clear {
return errors.New("no args specified. If your intent is to empty the args, run `tilt args --clear`.")
ctrlclient, err := client.New(cfg, client.Options{Scheme: v1alpha1.NewScheme()})
if err != nil {
return nil, err
}

return ctrlclient, err
}

func parseEditResult(b []byte) ([]string, error) {
sc := bufio.NewScanner(bytes.NewReader(b))
var argsLine *string
for sc.Scan() {
line := strings.TrimSpace(sc.Text())
if len(line) == 0 || line[0] == '#' {
continue
}
} else {
if c.clear {
return errors.New("--clear cannot be specified with other values. either use --clear to clear the args or specify args to replace the args with a new (non-empty) value")
if argsLine != nil {
return nil, errors.New("cannot have multiple non-comment lines")
}
s := line
argsLine = &s
}
if argsLine == nil {
return nil, errors.New("must have exactly one non-comment line, found zero. If you want to clear the args, use `tilt args --clear`")
}
args, err := shellquote.Split(*argsLine)
if err != nil {
return nil, errors.Wrapf(err, "error parsing %q", string(b))
}
url := apiURL("set_tiltfile_args")
body := &bytes.Buffer{}
err := json.NewEncoder(body).Encode(args)

return args, nil
}

func (c *argsCmd) run(ctx context.Context, args []string) error {
ctrlclient, err := newClient(ctx)
if err != nil {
return errors.Wrap(err, "failed to encode args as json")
return err
}

res, err := c.post(url, "application/json", body)
var tf v1alpha1.Tiltfile
err = ctrlclient.Get(ctx, types.NamespacedName{Name: model.MainTiltfileManifestName.String()}, &tf)
if err != nil {
fmt.Println("tilt args requires a running Tilt instance")
return errors.Wrapf(err, "error making http request to Tilt at %s", url)
return err
}
defer func() {
_ = res.Body.Close()
}()

if res.StatusCode != http.StatusOK {
// don't print the response body for 404 since it's full of html and more noise than it's worth on the command line
if res.StatusCode != http.StatusNotFound {
_, err := io.Copy(os.Stderr, res.Body)
if err != nil {
return errors.Wrapf(err, "http request to Tilt returned non-OK status %s and writing the content of the http response failed", res.Status)
}

if c.clear {
if len(args) != 0 {
return errors.New("--clear cannot be specified with other values")
}
args = nil
} else if len(args) == 0 {
input := fmt.Sprintf("# edit args for the running Tilt here\n%s\n", shellquote.Join(tf.Spec.Args...))
e := editor.NewDefaultEditor([]string{"TILT_EDITOR", "EDITOR"})
b, _, err := e.LaunchTempFile("", "", strings.NewReader(input))
if err != nil {
return err
}

args, err = parseEditResult(b)
if err != nil {
return err
}
return fmt.Errorf("http request to Tilt failed: %s", res.Status)
}

tf.Spec.Args = args

err = ctrlclient.Update(ctx, &tf)
if err != nil {
return err
}

fmt.Printf("changed config args for Tilt running at %s to %v\n", apiHost(), args)
Expand Down

0 comments on commit 34b9d2b

Please sign in to comment.