diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 747b712..c17095e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: go-version-file: go.mod - name: Check docs are up to date run: | - cd docs && go generate + go generate ./docs/ git diff --exit-code docs/ integration-test: runs-on: ubuntu-latest diff --git a/.golangci.yml b/.golangci.yml index 7b1475e..4f5c792 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,19 +1,21 @@ version: "2" linters: - default: standard + default: none enable: - errcheck - govet - staticcheck - unused - - gosimple - ineffassign - - revive -formatters: - enable: - - gofmt -issues: - exclude-rules: - - path: "_test.go" - linters: - - errcheck + settings: + errcheck: + exclude-functions: + - fmt.Fprintln + - fmt.Fprintf + - fmt.Fprint + - (io.Closer).Close + exclusions: + rules: + - path: "_test.go" + linters: + - errcheck diff --git a/cmd/auth/status.go b/cmd/auth/status.go index 4e27eec..8aedfae 100644 --- a/cmd/auth/status.go +++ b/cmd/auth/status.go @@ -61,7 +61,7 @@ func NewStatusCmd(f *cmdutil.Factory) *cobra.Command { } if resolved.Method == internalauth.AuthMethodNone { - return errors.New("Not authenticated. Run 'kh auth login' to sign in.") + return errors.New("not authenticated, run 'kh auth login' to sign in") } info, err := FetchTokenInfoFunc(host, resolved.Token) diff --git a/cmd/completion/completion.go b/cmd/completion/completion.go index 0f1a40a..2f4a8bd 100644 --- a/cmd/completion/completion.go +++ b/cmd/completion/completion.go @@ -8,7 +8,7 @@ func NewCompletionCmd() *cobra.Command { cmd := &cobra.Command{ Use: "completion ", Short: "Generate shell completion scripts", - Args: cobra.ExactValidArgs(1), + Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, Long: `Generate shell completion scripts for kh. Source the output in your shell profile to enable tab completion for all kh commands and flags. diff --git a/cmd/config/get.go b/cmd/config/get.go index 383aa72..8a2f73d 100644 --- a/cmd/config/get.go +++ b/cmd/config/get.go @@ -27,11 +27,11 @@ func NewGetCmd(f *cmdutil.Factory) *cobra.Command { case "default_host": value = cfg.DefaultHost default: - return fmt.Errorf("X Config key not set: %s", key) + return fmt.Errorf("config key not set: %s", key) } if value == "" { - return fmt.Errorf("X Config key not set: %s", key) + return fmt.Errorf("config key not set: %s", key) } fmt.Fprintln(f.IOStreams.Out, value) diff --git a/cmd/config/set.go b/cmd/config/set.go index 6d1a2bc..9c43cc1 100644 --- a/cmd/config/set.go +++ b/cmd/config/set.go @@ -8,8 +8,6 @@ import ( "github.com/spf13/cobra" ) -var validConfigKeys = []string{"default_host"} - func NewSetCmd(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "set ", @@ -38,7 +36,7 @@ See also: kh config list, kh config get`, case "default_host": cfg.DefaultHost = value default: - return fmt.Errorf("X Unknown config key: %s\nHint: use 'kh config list' to see available keys", key) + return fmt.Errorf("unknown config key: %s\nhint: use 'kh config list' to see available keys", key) } if err := internalconfig.WriteConfig(cfg); err != nil { diff --git a/cmd/workflow/go_live.go b/cmd/workflow/go_live.go index 3af799e..b79351a 100644 --- a/cmd/workflow/go_live.go +++ b/cmd/workflow/go_live.go @@ -81,7 +81,7 @@ func NewGoLiveCmd(f *cmdutil.Factory) *cobra.Command { defer resp.Body.Close() if resp.StatusCode == http.StatusUnauthorized { - return fmt.Errorf("HTTP 401: Unauthorized. This command requires interactive login. Run 'kh auth login' first.") + return fmt.Errorf("HTTP 401: unauthorized, this command requires interactive login, run 'kh auth login' first") } if resp.StatusCode != http.StatusOK { return khhttp.NewAPIError(resp) diff --git a/cmd/workflow/pause_test.go b/cmd/workflow/pause_test.go index 8297aa7..17a8901 100644 --- a/cmd/workflow/pause_test.go +++ b/cmd/workflow/pause_test.go @@ -168,6 +168,7 @@ func TestPauseHasYesFlag(t *testing.T) { if flag == nil { flag = pauseCmd.InheritedFlags().Lookup("yes") } + _ = flag // The test just verifies pause command exists and is wired correctly assert.NotNil(t, pauseCmd) } diff --git a/docs/kh.md b/docs/kh.md index 000a93a..1b9283b 100644 --- a/docs/kh.md +++ b/docs/kh.md @@ -29,6 +29,7 @@ KeeperHub CLI * [kh serve](kh_serve.md) - Start a server * [kh tag](kh_tag.md) - Manage tags * [kh template](kh_template.md) - Manage workflow templates +* [kh update](kh_update.md) - Update kh to the latest version * [kh version](kh_version.md) - Show CLI version * [kh wallet](kh_wallet.md) - Manage wallets * [kh workflow](kh_workflow.md) - Manage workflows diff --git a/docs/kh_update.md b/docs/kh_update.md new file mode 100644 index 0000000..114a14d --- /dev/null +++ b/docs/kh_update.md @@ -0,0 +1,43 @@ +## kh update + +Update kh to the latest version + +### Synopsis + +Update kh to the latest version by downloading the newest release from GitHub. + +If kh was installed via Homebrew, this command will print the appropriate +brew command to use instead of replacing the binary directly. Homebrew manages +its own binary lifecycle and must be used to keep the installation consistent. + +``` +kh update [flags] +``` + +### Examples + +``` + # Check for and install the latest version + kh update +``` + +### Options + +``` + -h, --help help for update +``` + +### Options inherited from parent commands + +``` + -H, --host string KeeperHub host (default: app.keeperhub.io) + --jq string Filter JSON output with a jq expression + --json Output as JSON + --no-color Disable color output + -y, --yes Skip confirmation prompts +``` + +### SEE ALSO + +* [kh](kh.md) - KeeperHub CLI + diff --git a/go.mod b/go.mod index 3a44412..91fb393 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/clipperhouse/stringish v0.1.1 // indirect github.com/clipperhouse/uax29/v2 v2.3.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/danieljoos/wincred v1.2.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/davidmz/go-pageant v1.0.2 // indirect @@ -39,6 +40,7 @@ require ( github.com/mattn/go-runewidth v0.0.19 // indirect github.com/mtibben/percent v0.2.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/segmentio/asm v1.1.3 // indirect github.com/segmentio/encoding v0.5.3 // indirect github.com/spf13/pflag v1.0.8 // indirect diff --git a/go.sum b/go.sum index b3a6d65..f36136d 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,7 @@ github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfa github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creativeprojects/go-selfupdate v1.5.2 h1:3KR3JLrq70oplb9yZzbmJ89qRP78D1AN/9u+l3k0LJ4= github.com/creativeprojects/go-selfupdate v1.5.2/go.mod h1:BCOuwIl1dRRCmPNRPH0amULeZqayhKyY2mH/h4va7Dk= @@ -75,6 +76,7 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc= github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= diff --git a/internal/auth/device.go b/internal/auth/device.go index ba75b61..03bb1ab 100644 --- a/internal/auth/device.go +++ b/internal/auth/device.go @@ -112,9 +112,9 @@ func pollDeviceToken(ctx context.Context, host, deviceCode string, interval time case "slow_down": interval += 5 * time.Second case "expired_token": - return "", errors.New("Device code expired. Run 'kh auth login --no-browser' again.") + return "", errors.New("device code expired, run 'kh auth login --no-browser' again") case "access_denied": - return "", errors.New("Authentication denied.") + return "", errors.New("authentication denied") default: return "", fmt.Errorf("unexpected device token error: %s", tokenResp.Error) } diff --git a/internal/config/config.go b/internal/config/config.go index 9b70225..826065c 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -37,7 +37,7 @@ func ReadConfig() (Config, error) { var cfg Config if err := yaml.Unmarshal(data, &cfg); err != nil { - return Config{}, fmt.Errorf("Config file is invalid. Run 'kh auth login' to reset it.") + return Config{}, fmt.Errorf("config file is invalid, run 'kh auth login' to reset it") } return cfg, nil diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 3a47516..39cee41 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -73,7 +73,7 @@ func TestReadConfigInvalidYAML(t *testing.T) { if err == nil { t.Fatal("expected error for invalid YAML, got nil") } - if err.Error() != "Config file is invalid. Run 'kh auth login' to reset it." { + if err.Error() != "config file is invalid, run 'kh auth login' to reset it" { t.Errorf("unexpected error message: %q", err.Error()) } }