check / check (push) Successful in 54s
REPO_POLICIES.md sets the Go module root to sneak.berlin/go/<name>. go.mod, every import of the old path and the -X path in the Makefile LDFLAGS now use sneak.berlin/go/keyfunc; make fmt moved those imports to their new place in the sort order. The old path keeps no alias. The README gives the go install line next to the build from a clone and drops this issue from its TODO list. Model: opus-5-5
98 lines
2.6 KiB
Go
98 lines
2.6 KiB
Go
// Package cli builds the command tree and runs it.
|
|
package cli
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"os/signal"
|
|
"runtime/debug"
|
|
"syscall"
|
|
|
|
"github.com/spf13/cobra"
|
|
"sneak.berlin/go/keyfunc/internal/cli/age"
|
|
"sneak.berlin/go/keyfunc/internal/cli/mnemonic"
|
|
"sneak.berlin/go/keyfunc/internal/cli/options"
|
|
"sneak.berlin/go/keyfunc/internal/cli/ssh"
|
|
)
|
|
|
|
// devVersion is what Version holds until a build stamps a real one.
|
|
const devVersion = "dev"
|
|
|
|
// Version is what --version prints. make build stamps it with -ldflags.
|
|
//
|
|
//nolint:gochecknoglobals // set at build time with -ldflags
|
|
var Version = devVersion
|
|
|
|
// resolveVersion chooses what --version reports. A value stamped at
|
|
// build time wins. Otherwise, for a binary from go install, the module
|
|
// version recorded in the build info is used, unless that is empty or
|
|
// the "(devel)" of a local build. When neither names a version, the
|
|
// "dev" fallback stays.
|
|
func resolveVersion(stamped string, info *debug.BuildInfo) string {
|
|
if stamped != devVersion {
|
|
return stamped
|
|
}
|
|
|
|
if info != nil && info.Main.Version != "" &&
|
|
info.Main.Version != "(devel)" {
|
|
return info.Main.Version
|
|
}
|
|
|
|
return devVersion
|
|
}
|
|
|
|
// Root returns the whole command tree.
|
|
func Root() *cobra.Command {
|
|
info, _ := debug.ReadBuildInfo()
|
|
|
|
root := &cobra.Command{
|
|
Use: "keyfunc",
|
|
Short: "derive key pairs from a BIP-39 mnemonic",
|
|
Long: "keyfunc turns a BIP-39 mnemonic into key pairs that can " +
|
|
"be recreated from that mnemonic at any time. The same " +
|
|
"mnemonic, key type and index always give the same key.",
|
|
Version: resolveVersion(Version, info),
|
|
SilenceUsage: true,
|
|
SilenceErrors: true,
|
|
}
|
|
|
|
options.Add(root)
|
|
root.AddCommand(ssh.Command(), age.Command(), mnemonic.Command())
|
|
|
|
return root
|
|
}
|
|
|
|
// Main runs the tool and returns the status the process should exit
|
|
// with. An error ends the tool with status 1, except when it carries a
|
|
// status of its own, which "ssh to" uses to hand on the status ssh
|
|
// ended with. ssh has already said whatever it had to say in that
|
|
// case, so nothing more is printed.
|
|
//
|
|
// SIGINT, SIGTERM and SIGHUP cancel the command's context instead of
|
|
// killing the process outright, so the child ssh or sftp ends and the
|
|
// deferred cleanup that removes the agent socket and the install
|
|
// working directory still runs.
|
|
func Main() int {
|
|
ctx, stop := signal.NotifyContext(
|
|
context.Background(),
|
|
syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP,
|
|
)
|
|
defer stop()
|
|
|
|
err := Root().ExecuteContext(ctx)
|
|
if err == nil {
|
|
return 0
|
|
}
|
|
|
|
var passed ssh.StatusError
|
|
if errors.As(err, &passed) {
|
|
return passed.Status
|
|
}
|
|
|
|
fmt.Fprintln(os.Stderr, "keyfunc: "+err.Error())
|
|
|
|
return 1
|
|
}
|