1
0
mirror of https://github.com/smallstep/cli.git synced 2026-10-29 00:17:23 +03:00
Files
step-ca-cli/internal/cmd/root.go
2026-02-24 13:51:06 +01:00

195 lines
5.2 KiB
Go

package cmd
import (
"errors"
"fmt"
"io"
"os"
"reflect"
"regexp"
"strings"
"time"
"github.com/urfave/cli"
"github.com/smallstep/cli-utils/command"
"github.com/smallstep/cli-utils/fileutil"
"github.com/smallstep/cli-utils/step"
"github.com/smallstep/cli-utils/ui"
"github.com/smallstep/cli-utils/usage"
"github.com/smallstep/cli/command/version"
"github.com/smallstep/cli/internal/plugin"
"go.step.sm/crypto/jose"
"go.step.sm/crypto/pemutil"
// Enabled cas interfaces.
_ "github.com/smallstep/certificates/cas/cloudcas"
_ "github.com/smallstep/certificates/cas/softcas"
_ "github.com/smallstep/certificates/cas/stepcas"
// Enabled commands
_ "github.com/smallstep/cli/command/api"
_ "github.com/smallstep/cli/command/base64"
_ "github.com/smallstep/cli/command/beta"
_ "github.com/smallstep/cli/command/ca"
_ "github.com/smallstep/cli/command/certificate"
_ "github.com/smallstep/cli/command/completion"
_ "github.com/smallstep/cli/command/context"
_ "github.com/smallstep/cli/command/crl"
_ "github.com/smallstep/cli/command/crypto"
_ "github.com/smallstep/cli/command/fileserver"
_ "github.com/smallstep/cli/command/oauth"
_ "github.com/smallstep/cli/command/path"
_ "github.com/smallstep/cli/command/ssh"
)
func Run() {
os.Exit(run())
}
func run() int {
defer panicHandler()
// initialize step environment.
if err := step.Init(); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
return 1
}
// create new instance of app
app := newApp(os.Stdout, os.Stderr)
if err := app.Run(os.Args); err != nil {
var messenger interface {
Message() string
}
if errors.As(err, &messenger) {
if os.Getenv("STEPDEBUG") == "1" {
fmt.Fprintf(os.Stderr, "%+v\n\n%s", err, messenger.Message())
} else {
fmt.Fprintln(os.Stderr, messenger.Message())
fmt.Fprintln(os.Stderr, "Re-run with STEPDEBUG=1 for more info.")
}
} else {
if os.Getenv("STEPDEBUG") == "1" {
fmt.Fprintf(os.Stderr, "%+v\n", err)
} else {
fmt.Fprintln(os.Stderr, err)
}
}
return 1
}
return 0
}
var stepAppName = "step"
func SetName(appName string) {
if appName != "" {
stepAppName = appName
}
}
func newApp(stdout, stderr io.Writer) *cli.App {
// Define default file writers and prompters for go.step.sm/crypto
pemutil.WriteFile = fileutil.WriteFile
pemutil.PromptPassword = func(msg string) ([]byte, error) {
return ui.PromptPassword(msg)
}
jose.PromptPassword = func(msg string) ([]byte, error) {
return ui.PromptPassword(msg)
}
// Override global framework components
cli.VersionPrinter = func(c *cli.Context) {
version.Command(c)
}
cli.AppHelpTemplate = usage.AppHelpTemplate
cli.SubcommandHelpTemplate = usage.SubcommandHelpTemplate
cli.CommandHelpTemplate = usage.CommandHelpTemplate
cli.HelpPrinter = usage.HelpPrinter
cli.FlagNamePrefixer = usage.FlagNamePrefixer
cli.FlagStringer = stringifyFlag
// Configure cli app
app := cli.NewApp()
app.Name = stepAppName // "step" by default
app.HelpName = stepAppName // "step" by default
app.Usage = "plumbing for distributed systems"
app.Version = step.Version()
app.Commands = command.Retrieve()
app.Flags = append(app.Flags, cli.HelpFlag)
app.EnableBashCompletion = true
app.Copyright = fmt.Sprintf("(c) 2018-%d Smallstep Labs, Inc.", time.Now().Year())
// Flag of custom configuration flag
app.Flags = append(app.Flags, cli.StringFlag{
Name: "config",
Usage: "path to the config file to use for CLI flags",
})
// Action runs on `step` or `step <command>` if the command is not enabled.
app.Action = func(ctx *cli.Context) error {
args := ctx.Args()
if name := args.First(); name != "" {
if file, err := plugin.LookPath(name); err == nil {
return plugin.Run(ctx, file)
}
if u := plugin.GetURL(name); u != "" {
//nolint:staticcheck // this is a top level error - capitalization is ok
return fmt.Errorf("The plugin %q was not found on this system.\nDownload it from %s", name, u)
}
return cli.ShowCommandHelp(ctx, name)
}
return cli.ShowAppHelp(ctx)
}
// All non-successful output should be written to stderr
app.Writer = stdout
app.ErrWriter = stderr
return app
}
func panicHandler() {
if r := recover(); r != nil {
if os.Getenv("STEPDEBUG") == "1" {
fmt.Fprintf(os.Stderr, "%s\n", step.Version())
fmt.Fprintf(os.Stderr, "Release Date: %s\n\n", step.ReleaseDate())
panic(r)
}
fmt.Fprintln(os.Stderr, "Something unexpected happened.")
fmt.Fprintln(os.Stderr, "If you want to help us debug the problem, please run:")
fmt.Fprintf(os.Stderr, "STEPDEBUG=1 %q\n", strings.Join(os.Args, " ")) // #nosec G705 -- terminal output
fmt.Fprintln(os.Stderr, "and send the output to info@smallstep.com")
os.Exit(2)
}
}
func flagValue(f cli.Flag) reflect.Value {
fv := reflect.ValueOf(f)
for fv.Kind() == reflect.Ptr {
fv = reflect.Indirect(fv)
}
return fv
}
var placeholderString = regexp.MustCompile(`<.*?>`)
func stringifyFlag(f cli.Flag) string {
fv := flagValue(f)
usg := fv.FieldByName("Usage").String()
placeholder := placeholderString.FindString(usg)
if placeholder == "" {
switch f.(type) {
case cli.BoolFlag, cli.BoolTFlag:
default:
placeholder = "<value>"
}
}
return cli.FlagNamePrefixer(fv.FieldByName("Name").String(), placeholder) + "\t" + usg
}