Ctrl K
ComunidadGuías…

cli/root.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package cli

import (
	"context"
	"errors"
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"sync"

	"github.com/spf13/cobra"
	"go.keploy.io/server/v3/cli/provider"
	"go.keploy.io/server/v3/config"
	"go.keploy.io/server/v3/pkg/agent/token"
	"go.keploy.io/server/v3/utils"
	"go.keploy.io/server/v3/utils/log"
	"go.uber.org/zap"
)

// removeAgentTokenOnce registers the control-plane token cleanup with cobra at
// most once, however many times Root is called.
var removeAgentTokenOnce sync.Once

func Root(ctx context.Context, logger *zap.Logger, svcFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
	conf := config.New()

	// A natively started agent is handed its control-plane token in a file in
	// a private temp directory (token.WriteFile), and that directory must not
	// outlive the command. Registered here, as a cobra finalizer, rather than
	// in a main: every keploy binary builds its commands with Root, but not
	// every one of them runs this repository's main — the enterprise binary
	// has its own — and a finalizer runs when the command finishes however it
	// finishes, error and panic included.
	removeAgentTokenOnce.Do(func() {
		cobra.OnFinalize(func() {
			if err := token.Cleanup(); err != nil {
				utils.LogError(logger, err, "failed to remove the agent control-plane token directory")
			}
		})
	})

	var rootCmd = &cobra.Command{
		Use:     "keploy",
		Short:   "Keploy CLI",
		Example: provider.RootExamples,
		Version: utils.Version,
		PreRun: func(cmd *cobra.Command, _ []string) {
			disableAnsi, _ := cmd.Flags().GetBool("disable-ansi")
			provider.PrintLogo(log.PrimarySink(), disableAnsi)
		},
	}

	defaultHelpFunc := rootCmd.HelpFunc()

	rootCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
		disableAnsi, _ := cmd.Flags().GetBool("disable-ansi")
		provider.PrintLogo(log.PrimarySink(), disableAnsi)

		// Use the default help function instead of calling the parent's HelpFunc
		defaultHelpFunc(cmd, args)
	})

	rootCmd.CompletionOptions.DisableDefaultCmd = true

	rootCmd.SetHelpTemplate(provider.RootCustomHelpTemplate)

	rootCmd.SetVersionTemplate(provider.VersionTemplate)

	err := cmdConfigurator.AddFlags(rootCmd)
	if err != nil {
		utils.LogError(logger, err, "failed to set flags")
		return nil
	}

	for _, cmd := range Registered {
		c := cmd(ctx, logger, conf, svcFactory, cmdConfigurator)
		rootCmd.AddCommand(c)
	}

	// Every binary (OSS and enterprise — enterprise builds its root through this
	// same Root) gets unknown-subcommand hardening, and its command-line errors
	// tagged as usage errors, in one place.
	HardenUnknownSubcommands(rootCmd)
	TagUsageErrors(rootCmd)
	armForcedCrash(rootCmd)

	return rootCmd
}

// HardenUnknownSubcommands makes every command GROUP under root reject an unknown
// verb instead of printing help and exiting 0. A cobra group with subcommands but
// no Run/RunE is not "runnable", so for a NON-root group `legacyArgs` passes the
// unmatched verb through and cobra falls back to Help() returning nil — so
// `keploy mock bogus` (and `ca`/`contract`/any future group) silently exited 0, a
// false success a CI job calling a mistyped verb would pass on. (The root command
// already errors on an unknown top-level command; only descendant groups need
// this.) A hardened group still shows help and exits 0 when given no verb; a
// valid subcommand still resolves to its own command, so this RunE never runs for
// it. The "unknown command" error maps to utils.ExitUsageError (8) in main's
// exitCodeForCmdErr. Exported so a downstream build can harden commands it adds
// after Root returns. (Design §P0b: an unknown mock verb is a usage error.)
func HardenUnknownSubcommands(root *cobra.Command) {
	for _, c := range root.Commands() {
		if len(c.Commands()) > 0 && c.Run == nil && c.RunE == nil {
			rejectUnknownSubcommand(c)
		}
		HardenUnknownSubcommands(c)
	}
}

// TagUsageErrors tags, as usage errors (utils.UsageError), the errors cobra
// raises while parsing the command line, at the one place each is raised: a
// command's flag-error func (an unknown flag or shorthand, a flag missing its
// value, a value of the wrong type) and its argument validator (the wrong
// number of arguments). Nothing else is tagged, so an error a command returns
// while running stays its own, even one of pflag's types (keploy reading a
// flag it never defined is keploy's bug, not the user's). A command's existing
// flag-error func still runs, and still prints; its error is only tagged.
// Exported so a downstream build can tag commands it adds after Root returns.
//
// A tagged error is also left to the binary's main to say (sayUsageError):
// cobra followed it with the command's whole usage text, so `keploy record
// --delay 10` printed every flag record has, some 80 lines, under the one line
// that said what was wrong, and that line scrolled away. Each main says the
// error once, then where the command's help is (`Run 'keploy record --help'
// for usage.`). An error a command returns while it runs is not touched
// here: cobra prints it, and its usage, as it did. A refusal of its PreRunE
// (CmdConfigurator.Validate) is said with no usage text under it, which
// Validate sees to.
func TagUsageErrors(root *cobra.Command) {
	flagErr := root.FlagErrorFunc()
	root.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
		return sayUsageError(cmd, utils.UsageError(flagErr(cmd, err)))
	})
	if args := root.Args; args != nil {
		root.Args = func(cmd *cobra.Command, a []string) error {
			return sayUsageError(cmd, utils.UsageError(args(cmd, a)))
		}
	}
	for _, c := range root.Commands() {
		TagUsageErrors(c)
	}
}

// sayUsageError hands a usage error raised on cmd to the binary's main to say:
// cobra prints neither the error nor the usage text it would print under it.
// Both mains say an error cobra was silenced for -- this repository's, unless
// the command's flag-error func said it already; the enterprise binary's, for
// a command cobra was silenced for -- and point at `<command> --help`, which
// is where the flags cobra would have listed are. Set on cmd only once the
// error is raised, so nothing else the command does is silenced.
func sayUsageError(cmd *cobra.Command, err error) error {
	if err != nil {
		cmd.SilenceErrors = true
		cmd.SilenceUsage = true
	}
	return err
}

// rejectUnknownSubcommand installs the reject-on-unknown-verb RunE on one group,
// and the same refusal for an unknown verb followed by flags: cobra parses the
// group's own flags first, so `keploy cloud request-changes --branch x` failed
// on --branch ("unknown flag: --branch"), which sent a coding agent looking for
// a flag when what it had wrong was the command.
func rejectUnknownSubcommand(c *cobra.Command) {
	c.SilenceUsage = true
	c.SilenceErrors = true
	c.RunE = func(cmd *cobra.Command, args []string) error {
		if len(args) == 0 {
			return cmd.Help()
		}
		return UnknownSubcommand(cmd, args[0])
	}
	// The func c answered flag errors with before: its own, or the one it
	// inherits. It still says every flag error that is not an unknown verb.
	prev := c.FlagErrorFunc()
	c.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
		// pflag keeps the arguments it read before the flag it failed on.
		if args := cmd.Flags().Args(); cmd == c && len(args) > 0 {
			return UnknownSubcommand(cmd, args[0])
		}
		// An error raised on a command under c, which reaches this func by
		// inheritance, is not an unknown verb of c's.
		return prev(cmd, err)
	})
}

// maxSubcommandHints bounds the commands an unknown verb's error names.
const maxSubcommandHints = 5

// suggestionDistance is the edit distance cobra suggests within when a
// command sets none (cobra's findSuggestions uses the same 2).
const suggestionDistance = 2

// UnknownSubcommand is group's refusal of verb, naming the commands under
// group at any depth whose name is verb or close to it (cobra's own
// suggestions, made at each level), exact names first: `keploy cloud
// request-changes` points at `keploy cloud review request-changes`, which
// cobra's suggestions, one level deep and made only for the root, never
// reached. None when suggestions are disabled. Its message starts as cobra's
// own does (`unknown command "verb" for "path"`), which utils.IsUsageError
// reads as a usage error.
func UnknownSubcommand(group *cobra.Command, verb string) error {
	type hint struct {
		path  string
		exact bool
	}
	var hints []hint
	var walk func(*cobra.Command)
	walk = func(c *cobra.Command) {
		if c.DisableSuggestions {
			return
		}
		// SuggestionsFor reads the distance from the command; at 0 it matches
		// only an exact name, a prefix or an explicit SuggestFor. Set as cobra
		// would, then put back: the commands are shared.
		if d := c.SuggestionsMinimumDistance; d <= 0 {
			c.SuggestionsMinimumDistance = suggestionDistance
			defer func() { c.SuggestionsMinimumDistance = d }()
		}
		for _, s := range c.SuggestionsFor(verb) {
			hints = append(hints, hint{c.CommandPath() + " " + s, s == verb})
		}
		for _, sub := range c.Commands() {
			if sub.IsAvailableCommand() {
				walk(sub)
			}
		}
	}
	if !group.Root().DisableSuggestions {
		walk(group)
	}
	sort.SliceStable(hints, func(i, j int) bool { return hints[i].exact && !hints[j].exact })
	if len(hints) > maxSubcommandHints {
		hints = hints[:maxSubcommandHints]
	}
	msg := fmt.Sprintf("unknown command %q for %q", verb, group.CommandPath())
	if len(hints) > 0 {
		paths := make([]string, len(hints))
		for i, h := range hints {
			paths[i] = h.path
		}
		msg += "\n\nDid you mean this?\n\t" + strings.Join(paths, "\n\t")
	}
	return errors.New(msg)
}

// unknownRootCommand is cobra's refusal of an unknown top-level command:
// `unknown command "verb" for "keploy"`, then its own one-level suggestions.
var unknownRootCommand = regexp.MustCompile(`^unknown command ("(?:[^"\\]|\\.)*") for ("(?:[^"\\]|\\.)*")`)

// HintUnknownCommand is err, or, when err is cobra's refusal of an unknown
// top-level command of root, the same refusal naming where a command of that
// name, or a near one, lives at any depth (UnknownSubcommand): `keploy
// generate` points at `keploy contract generate`. cobra raises it while it
// finds the command, before any of root's funcs run, so a binary's main calls
// this on the error ExecuteC returns.
func HintUnknownCommand(root *cobra.Command, err error) error {
	if err == nil {
		return nil
	}
	m := unknownRootCommand.FindStringSubmatch(err.Error())
	if m == nil {
		return err
	}
	// cobra quoted both with %q.
	verb, verr := strconv.Unquote(m[1])
	path, perr := strconv.Unquote(m[2])
	if verr != nil || perr != nil || path != root.CommandPath() {
		return err
	}
	return UnknownSubcommand(root, verb)
}