github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/cli/cli.go (about)

     1  package cli
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"os/signal"
     8  	"syscall"
     9  	"time"
    10  
    11  	"github.com/go-logr/logr"
    12  	"github.com/spf13/cobra"
    13  	"go.lsp.dev/protocol"
    14  	"k8s.io/cli-runtime/pkg/genericclioptions"
    15  	ctrllog "sigs.k8s.io/controller-runtime/pkg/log"
    16  
    17  	"github.com/tilt-dev/starlark-lsp/pkg/cli"
    18  	tiltanalytics "github.com/tilt-dev/tilt/internal/analytics"
    19  	"github.com/tilt-dev/tilt/internal/controllers"
    20  	"github.com/tilt-dev/tilt/internal/output"
    21  	"github.com/tilt-dev/tilt/pkg/logger"
    22  	"github.com/tilt-dev/tilt/pkg/model"
    23  	"github.com/tilt-dev/wmclient/pkg/analytics"
    24  )
    25  
    26  var debug bool
    27  var verbose bool
    28  
    29  func logLevel(verbose, debug bool) logger.Level {
    30  	if debug {
    31  		return logger.DebugLvl
    32  	} else if verbose {
    33  		return logger.VerboseLvl
    34  	} else {
    35  		return logger.InfoLvl
    36  	}
    37  }
    38  
    39  func Execute() {
    40  	err := readEnvDefaults()
    41  	if err != nil {
    42  		fmt.Println(err)
    43  		os.Exit(1)
    44  	}
    45  
    46  	rootCmd := &cobra.Command{
    47  		Use:   "tilt",
    48  		Short: "Multi-service development with no stress",
    49  		Long: `
    50  Tilt helps you develop your microservices locally.
    51  Run 'tilt up' to start working on your services in a complete dev environment
    52  configured for your team.
    53  
    54  Tilt watches your files for edits, automatically builds your container images,
    55  and applies any changes to bring your environment
    56  up-to-date in real-time. Think 'docker build && kubectl apply' or 'docker-compose up'.
    57  `,
    58  	}
    59  	streams := genericclioptions.IOStreams{Out: os.Stdout, ErrOut: os.Stderr, In: os.Stdin}
    60  
    61  	addCommand(rootCmd, &ciCmd{})
    62  	addCommand(rootCmd, &upCmd{})
    63  	addCommand(rootCmd, &dockerCmd{})
    64  	addCommand(rootCmd, &doctorCmd{})
    65  	addCommand(rootCmd, newDownCmd())
    66  	addCommand(rootCmd, &versionCmd{})
    67  	addCommand(rootCmd, &verifyInstallCmd{})
    68  	addCommand(rootCmd, &dockerPruneCmd{})
    69  	addCommand(rootCmd, newArgsCmd(streams))
    70  	addCommand(rootCmd, &logsCmd{})
    71  	addCommand(rootCmd, newDescribeCmd(streams))
    72  	addCommand(rootCmd, newGetCmd(streams))
    73  	addCommand(rootCmd, newExplainCmd(streams))
    74  	addCommand(rootCmd, newEditCmd(streams))
    75  	addCommand(rootCmd, newApiresourcesCmd(streams))
    76  	addCommand(rootCmd, newDeleteCmd(streams))
    77  	addCommand(rootCmd, newApplyCmd(streams))
    78  	addCommand(rootCmd, newCreateCmd(streams))
    79  	addCommand(rootCmd, newPatchCmd(streams))
    80  	addCommand(rootCmd, newWaitCmd(streams))
    81  	addCommand(rootCmd, &demoCmd{})
    82  	addCommand(rootCmd, newEnableCmd())
    83  	addCommand(rootCmd, newDisableCmd())
    84  	addCommand(rootCmd, newTriggerCmd(streams))
    85  
    86  	rootCmd.AddCommand(analytics.NewCommand())
    87  	rootCmd.AddCommand(newDumpCmd(rootCmd, streams))
    88  	rootCmd.AddCommand(newAlphaCmd(streams))
    89  	rootCmd.AddCommand(newLspCmd())
    90  	rootCmd.AddCommand(newSnapshotCmd())
    91  
    92  	globalFlags := rootCmd.PersistentFlags()
    93  	globalFlags.BoolVarP(&debug, "debug", "d", false, "Enable debug logging")
    94  	globalFlags.BoolVarP(&verbose, "verbose", "v", false, "Enable verbose logging")
    95  	controllers.AddKlogFlags(globalFlags)
    96  
    97  	ctx, cleanup := createContext()
    98  	defer cleanup()
    99  	if err := rootCmd.ExecuteContext(ctx); err != nil {
   100  		fmt.Println(err)
   101  		os.Exit(1)
   102  	}
   103  }
   104  
   105  type tiltCmd interface {
   106  	name() model.TiltSubcommand
   107  	register() *cobra.Command
   108  	run(ctx context.Context, args []string) error
   109  }
   110  
   111  func createContext() (ctx context.Context, cleanup func()) {
   112  	l, cleanup := cli.NewLogger()
   113  	return protocol.WithLogger(context.Background(), l), cleanup
   114  }
   115  
   116  func preCommand(ctx context.Context, cmdName model.TiltSubcommand) context.Context {
   117  
   118  	l := logger.NewLogger(logLevel(verbose, debug), os.Stdout)
   119  	ctx = logger.WithLogger(ctx, l)
   120  
   121  	a, err := wireAnalytics(l, cmdName)
   122  	if err != nil {
   123  		l.Errorf("Fatal error initializing analytics: %v", err)
   124  		os.Exit(1)
   125  	}
   126  
   127  	ctx = tiltanalytics.WithAnalytics(ctx, a)
   128  
   129  	// Users don't care about controller-runtime logs.
   130  	ctrllog.SetLogger(logr.New(ctrllog.NullLogSink{}))
   131  
   132  	controllers.InitKlog(l.Writer(logger.InfoLvl))
   133  
   134  	// SIGNAL TRAPPING
   135  	ctx, cancel := context.WithCancel(ctx)
   136  	sigs := make(chan os.Signal, 1)
   137  	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
   138  	go func() {
   139  		<-sigs
   140  
   141  		cancel()
   142  
   143  		// If we get another signal, OR it takes too long for tilt to
   144  		// exit after canceling context, just exit
   145  		select {
   146  		case <-sigs:
   147  			l.Debugf("force quitting...")
   148  			os.Exit(1)
   149  		case <-time.After(2 * time.Second):
   150  			l.Debugf("Context canceled but app still running; forcibly exiting.")
   151  			os.Exit(1)
   152  		}
   153  	}()
   154  
   155  	return ctx
   156  }
   157  
   158  func addCommand(parent *cobra.Command, child tiltCmd) {
   159  	cobraChild := child.register()
   160  	cobraChild.Run = func(cmd *cobra.Command, args []string) {
   161  		ctx := preCommand(cmd.Context(), child.name())
   162  
   163  		err := child.run(ctx, args)
   164  		if err != nil {
   165  			// TODO(maia): this shouldn't print if we've already pretty-printed it
   166  			_, printErr := fmt.Fprintf(output.OriginalStderr, "Error: %v\n", err)
   167  			if printErr != nil {
   168  				panic(printErr)
   169  			}
   170  			os.Exit(1)
   171  		}
   172  	}
   173  
   174  	parent.AddCommand(cobraChild)
   175  }