Files
holos/internal/cli/root.go
Jeff McCune a8ab4dabaa cue: fix holos cue vet exit code (#358)
Without this patch `holos cue vet` always returns exit code 0, even when
there are errors.

This patch fixes the problem by catching the error and returning it to
our own top level error handler.  Note the final error, "could not run:
terminating because of errors" which wraps the generic error reported by
cue in the presence of multiple errors.

Result:

```
❯ holos cue vet ./policy --path 'strings.ToLower(kind)' /tmp/podinfo.gen.yaml
deployment.kind: conflicting values "Forbidden" and "Deployment":
    ./policy/validations.cue:18:8
    ../../../../../tmp/podinfo.gen.yaml:25:7
deployment.spec.template.spec.containers.0.resources.limits: conflicting values null and {[string]:"k8s.io/apimachinery/pkg/api/resource".#Quantity} (mismatched types null and struct):
    ./cue.mod/gen/k8s.io/api/apps/v1/types_go_gen.cue:355:9
    ./cue.mod/gen/k8s.io/api/apps/v1/types_go_gen.cue:376:12
    ./cue.mod/gen/k8s.io/api/core/v1/types_go_gen.cue:2840:11
    ./cue.mod/gen/k8s.io/api/core/v1/types_go_gen.cue:2968:14
    ./cue.mod/gen/k8s.io/api/core/v1/types_go_gen.cue:3882:15
    ./cue.mod/gen/k8s.io/api/core/v1/types_go_gen.cue:3882:18
    ./cue.mod/gen/k8s.io/api/core/v1/types_go_gen.cue:5027:9
    ./cue.mod/gen/k8s.io/api/core/v1/types_go_gen.cue:6407:16
    ./policy/validations.cue:17:13
    ../../../../../tmp/podinfo.gen.yaml:104:19
could not run: terminating because of errors
```
2024-11-24 16:39:56 -08:00

138 lines
4.1 KiB
Go

package cli
import (
_ "embed"
"fmt"
"log/slog"
"github.com/spf13/cobra"
"github.com/holos-run/holos/version"
"github.com/holos-run/holos/internal/holos"
"github.com/holos-run/holos/internal/logger"
"github.com/holos-run/holos/internal/server"
"github.com/holos-run/holos/internal/cli/command"
"github.com/holos-run/holos/internal/cli/create"
"github.com/holos-run/holos/internal/cli/destroy"
"github.com/holos-run/holos/internal/cli/get"
"github.com/holos-run/holos/internal/cli/kv"
"github.com/holos-run/holos/internal/cli/login"
"github.com/holos-run/holos/internal/cli/logout"
"github.com/holos-run/holos/internal/cli/preflight"
"github.com/holos-run/holos/internal/cli/pull"
"github.com/holos-run/holos/internal/cli/push"
"github.com/holos-run/holos/internal/cli/register"
"github.com/holos-run/holos/internal/cli/render"
"github.com/holos-run/holos/internal/cli/token"
"github.com/holos-run/holos/internal/cli/txtar"
cue "cuelang.org/go/cmd/cue/cmd"
)
//go:embed help.txt
var helpLong string
// New returns a new root *cobra.Command for command line execution.
func New(cfg *holos.Config, feature holos.Flagger) *cobra.Command {
rootCmd := &cobra.Command{
Use: "holos",
Short: "holos manages a holistic integrated software development platform",
Long: helpLong,
Version: version.GetVersion(),
Args: cobra.NoArgs,
CompletionOptions: cobra.CompletionOptions{
HiddenDefaultCmd: true, // Don't complete the complete subcommand itself
},
SilenceUsage: true,
SilenceErrors: true,
PersistentPreRunE: func(c *cobra.Command, args []string) error {
if err := cfg.Finalize(); err != nil {
return err
}
log := cfg.Logger()
c.Root().SetContext(logger.NewContext(c.Context(), log))
// Set the default logger after flag parsing.
slog.SetDefault(log)
return nil
},
RunE: func(c *cobra.Command, args []string) error {
return c.Usage()
},
}
rootCmd.SetVersionTemplate("{{.Version}}\n")
rootCmd.SetOut(cfg.Stdout())
rootCmd.PersistentFlags().SortFlags = false
rootCmd.PersistentFlags().AddGoFlagSet(cfg.LogFlagSet())
// Hide the help command
rootCmd.SetHelpCommand(&cobra.Command{Hidden: true})
rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage")
rootCmd.PersistentFlags().Lookup("help").Hidden = true
// subcommands
rootCmd.AddCommand(render.New(cfg, feature))
rootCmd.AddCommand(get.New(cfg, feature))
rootCmd.AddCommand(create.New(cfg, feature))
rootCmd.AddCommand(destroy.New(cfg, feature))
rootCmd.AddCommand(preflight.New(cfg, feature))
rootCmd.AddCommand(login.New(cfg, feature))
rootCmd.AddCommand(logout.New(cfg, feature))
rootCmd.AddCommand(token.New(cfg, feature))
rootCmd.AddCommand(newInitCommand(feature))
rootCmd.AddCommand(register.New(cfg, feature))
rootCmd.AddCommand(pull.New(cfg, feature))
rootCmd.AddCommand(push.New(cfg, feature))
rootCmd.AddCommand(newOrgCmd(feature))
// Maybe not needed?
rootCmd.AddCommand(txtar.New(cfg))
// Deprecated, remove?
rootCmd.AddCommand(kv.New(cfg, feature))
// Server
rootCmd.AddCommand(server.New(cfg, feature))
// CUE
rootCmd.AddCommand(newCueCmd())
// Show
rootCmd.AddCommand(newShowCmd())
return rootCmd
}
func newOrgCmd(feature holos.Flagger) (cmd *cobra.Command) {
cmd = command.New("orgid")
cmd.Short = "print the current context org id."
cmd.Hidden = !feature.Flag(holos.ServerFeature)
cmd.RunE = func(cmd *cobra.Command, args []string) error {
ctx := cmd.Root().Context()
cc := holos.NewClientContext(ctx)
_, err := fmt.Fprintln(cmd.OutOrStdout(), cc.OrgID)
return err
}
return cmd
}
func newCueCmd() (cmd *cobra.Command) {
// Get a handle on the cue root command fields.
root, _ := cue.New([]string{})
// Copy the fields to our embedded command.
cmd = command.New("cue")
cmd.Short = root.Short
cmd.Long = root.Long
// Pass all arguments through to RunE.
cmd.DisableFlagParsing = true
cmd.Args = cobra.ArbitraryArgs
// We do it this way so we handle errors correctly.
cmd.RunE = func(cmd *cobra.Command, args []string) error {
cueRootCommand, _ := cue.New(args)
return cueRootCommand.Run(cmd.Root().Context())
}
return cmd
}