Unify some CLI error reporting
This unifies some of the CLI error reporting logic. It's still not perfect, but this tidies up some minor issues that were starting to annoy me (e.g., inconsistencies in message formatting, message colorization, and exit code handling).
This commit is contained in:
parent
49f5f3debc
commit
f93e093ab3
10 changed files with 46 additions and 52 deletions
|
@ -4,10 +4,15 @@ package cmd
|
|||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/pulumi/coconut/pkg/compiler/core"
|
||||
"github.com/pulumi/coconut/pkg/diag"
|
||||
)
|
||||
|
||||
func NewCoconutCmd() *cobra.Command {
|
||||
|
@ -45,3 +50,27 @@ func NewCoconutCmd() *cobra.Command {
|
|||
|
||||
return cmd
|
||||
}
|
||||
|
||||
var snk diag.Sink
|
||||
|
||||
// sink lazily allocates a sink to be used if we can't create a compiler.
|
||||
func sink() diag.Sink {
|
||||
if snk == nil {
|
||||
snk = core.DefaultSink("")
|
||||
}
|
||||
return snk
|
||||
}
|
||||
|
||||
// exitErrorPrefix is auto-appended to any abrupt command exit.
|
||||
const exitErrorPrefix = "fatal: "
|
||||
|
||||
// exitError issues an error and exits with a standard error exit code.
|
||||
func exitError(msg string, args ...interface{}) {
|
||||
exitErrorCode(-1, msg, args...)
|
||||
}
|
||||
|
||||
// exitErrorCode issues an error and exists with the given error exit code.
|
||||
func exitErrorCode(code int, msg string, args ...interface{}) {
|
||||
sink().Errorf(diag.Message(exitErrorPrefix + fmt.Sprintf(msg, args...)))
|
||||
os.Exit(code)
|
||||
}
|
||||
|
|
|
@ -42,8 +42,7 @@ func newDescribeCmd() *cobra.Command {
|
|||
pwd, _ := os.Getwd()
|
||||
pkgpath, err := workspace.DetectPackage(pwd, sink())
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "fatal: could not find a nut: %v", err)
|
||||
os.Exit(-1)
|
||||
exitError("could not locate a nut to load: %v", err)
|
||||
}
|
||||
|
||||
if pkg := cmdutil.ReadPackage(pkgpath); pkg != nil {
|
||||
|
|
|
@ -38,8 +38,7 @@ func newEvalCmd() *cobra.Command {
|
|||
if dotOutput {
|
||||
// Convert the output to a DOT file.
|
||||
if err := dotconv.Print(g, os.Stdout); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: failed to write DOT file to output: %v\n", err)
|
||||
os.Exit(-1)
|
||||
exitError("failed to write DOT file to output: %v", err)
|
||||
}
|
||||
} else {
|
||||
// Just print a very basic, yet (hopefully) aesthetically pleasinge, ascii-ization of the graph.
|
||||
|
|
18
cmd/husk.go
18
cmd/husk.go
|
@ -53,23 +53,13 @@ func newHuskCmd() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
var snk diag.Sink
|
||||
|
||||
// sink lazily allocates a sink to be used if we can't create a compiler.
|
||||
func sink() diag.Sink {
|
||||
if snk == nil {
|
||||
snk = core.DefaultSink("")
|
||||
}
|
||||
return snk
|
||||
}
|
||||
|
||||
func initHuskCmd(cmd *cobra.Command, args []string) (*huskCmdInfo, error) {
|
||||
func initHuskCmd(cmd *cobra.Command, args []string) *huskCmdInfo {
|
||||
// Create a new context for the plan operations.
|
||||
ctx := resource.NewContext(sink())
|
||||
|
||||
// Read in the name of the husk to use.
|
||||
if len(args) == 0 {
|
||||
return nil, fmt.Errorf("missing required husk name")
|
||||
exitError("missing required husk name")
|
||||
}
|
||||
|
||||
// Read in the deployment information, bailing if an IO error occurs.
|
||||
|
@ -77,7 +67,7 @@ func initHuskCmd(cmd *cobra.Command, args []string) (*huskCmdInfo, error) {
|
|||
huskfile, husk, old := readHusk(ctx, name)
|
||||
if husk == nil {
|
||||
contract.Assert(!ctx.Diag.Success())
|
||||
return nil, fmt.Errorf("failed to read huskfile") // failure reading the husk information.
|
||||
exitError("could not read huskfile required to proceed") // failure reading the husk information.
|
||||
}
|
||||
return &huskCmdInfo{
|
||||
Ctx: ctx,
|
||||
|
@ -86,7 +76,7 @@ func initHuskCmd(cmd *cobra.Command, args []string) (*huskCmdInfo, error) {
|
|||
Old: old,
|
||||
Args: args[1:],
|
||||
Orig: args,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
type huskCmdInfo struct {
|
||||
|
|
|
@ -26,11 +26,8 @@ func newHuskDeployCmd() *cobra.Command {
|
|||
"\n" +
|
||||
"By default, the Nut to execute is loaded from the current directory. Optionally, an\n" +
|
||||
"explicit path can be provided using the [nut-file] argument.",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
info, err := initHuskCmd(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
info := initHuskCmd(cmd, args)
|
||||
apply(cmd, info, applyOptions{
|
||||
Delete: false,
|
||||
DryRun: dryRun,
|
||||
|
@ -39,7 +36,6 @@ func newHuskDeployCmd() *cobra.Command {
|
|||
Summary: summary,
|
||||
Output: output,
|
||||
})
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -21,11 +21,8 @@ func newHuskDestroyCmd() *cobra.Command {
|
|||
"\n" +
|
||||
"Warning: although old snapshots can be used to recreate an environment, this command\n" +
|
||||
"is generally irreversable and should be used with great care.",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
info, err := initHuskCmd(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
info := initHuskCmd(cmd, args)
|
||||
if dryRun || yes ||
|
||||
confirmPrompt("This will permanently destroy all resources in the '%v' husk!", info.Husk.Name) {
|
||||
apply(cmd, info, applyOptions{
|
||||
|
@ -34,7 +31,6 @@ func newHuskDestroyCmd() *cobra.Command {
|
|||
Summary: summary,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/pulumi/coconut/pkg/tokens"
|
||||
|
@ -22,8 +19,7 @@ func newHuskInitCmd() *cobra.Command {
|
|||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Read in the name of the husk to use.
|
||||
if len(args) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "fatal: missing required husk name\n")
|
||||
os.Exit(-1)
|
||||
exitError("missing required husk name")
|
||||
}
|
||||
|
||||
husk := tokens.QName(args[0])
|
||||
|
|
|
@ -27,8 +27,7 @@ func newHuskLsCmd() *cobra.Command {
|
|||
path := workspace.HuskPath("")
|
||||
files, err := ioutil.ReadDir(path)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
fmt.Fprintf(os.Stderr, "fatal: could not read husks: %v\n", err)
|
||||
os.Exit(-1)
|
||||
exitError("could not read husks: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("%-20s %-48s %-12s\n", "NAME", "LAST DEPLOYMENT", "RESOURCE COUNT")
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
@ -20,20 +18,16 @@ func newHuskRmCmd() *cobra.Command {
|
|||
"`destroy` command for removing a husk's resources, as it is a distinct operation.\n" +
|
||||
"\n" +
|
||||
"After this command completes, the husk will no longer be available for deployments.",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
info, err := initHuskCmd(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
info := initHuskCmd(cmd, args)
|
||||
if !force && info.Old != nil && len(info.Old.Resources()) > 0 {
|
||||
return fmt.Errorf(
|
||||
"Husk '%v' still has resources; removal rejected; pass --force to override\n", info.Husk.Name)
|
||||
exitError(
|
||||
"'%v' still has resources; removal rejected; pass --force to override", info.Husk.Name)
|
||||
}
|
||||
if yes ||
|
||||
confirmPrompt("This will permanently remove the '%v' husk!", info.Husk.Name) {
|
||||
remove(info.Husk)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
@ -25,8 +22,7 @@ func newVerifyCmd() *cobra.Command {
|
|||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Create a compiler object and perform the verification.
|
||||
if !verify(cmd, args) {
|
||||
fmt.Printf("fatal: Nut verification failed\n")
|
||||
os.Exit(-1)
|
||||
exitError("Nut verification failed")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue