diff --git a/.golangci.yml b/.golangci.yml index 034e719..36ab636 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,9 +1,28 @@ version: "2" linters: + enable: + - revive + - misspell + - unconvert exclusions: rules: - linters: [errcheck] source: "fmt\\.Fprint" - linters: [errcheck] source: "\\.Body\\.Close\\(\\)" + # api.APIError is intentional: renaming to api.Error would be confusing + # next to the error interface. The package is internal so external callers + # are not a concern. + - linters: [revive] + path: "internal/api/apierror.go" + text: "stutters" + +# Formatters are checked by `golangci-lint run` and applied by +# `golangci-lint fmt`. Enabling gofmt here makes CI fail on formatting drift, +# which the default linter set does not catch. +formatters: + enable: + - gofmt + exclusions: + generated: lax diff --git a/cmd/auth.go b/cmd/auth.go index 3ca9aac..d7fb3fc 100644 --- a/cmd/auth.go +++ b/cmd/auth.go @@ -22,7 +22,7 @@ var authLoginCmd = &cobra.Command{ Short: "Configure credentials interactively", Example: ` namecom auth login namecom auth login --profile staging`, - RunE: runAuthLogin, + RunE: runAuthLogin, } var authStatusCmd = &cobra.Command{ @@ -30,7 +30,7 @@ var authStatusCmd = &cobra.Command{ Short: "Verify credentials by calling the API hello endpoint", Example: ` namecom auth status namecom auth status --profile staging`, - RunE: runAuthStatus, + RunE: runAuthStatus, } var authLogoutCmd = &cobra.Command{ @@ -38,7 +38,7 @@ var authLogoutCmd = &cobra.Command{ Short: "Remove credentials for the active profile", Example: ` namecom auth logout namecom auth logout --profile staging`, - RunE: runAuthLogout, + RunE: runAuthLogout, } var loginProfile string diff --git a/cmd/cmdutil/cmdutil.go b/cmd/cmdutil/cmdutil.go index 14115ea..d3c4311 100644 --- a/cmd/cmdutil/cmdutil.go +++ b/cmd/cmdutil/cmdutil.go @@ -14,8 +14,9 @@ import ( type contextKey int +// Context keys for values stored by cmd/root.go's PersistentPreRunE. const ( - KeyOutput contextKey = iota + KeyOutput contextKey = iota KeyClient KeyConfig KeyOverrides diff --git a/cmd/cmdutil/validate_test.go b/cmd/cmdutil/validate_test.go index ec26753..95619e6 100644 --- a/cmd/cmdutil/validate_test.go +++ b/cmd/cmdutil/validate_test.go @@ -65,9 +65,9 @@ func TestValidDNSAnswer(t *testing.T) { {"A", "@", "1.2.3.4", false}, {"A", "@", "256.0.0.1", true}, {"A", "@", "not-an-ip", true}, - {"A", "@", "::1", true}, // IPv6 for A record + {"A", "@", "::1", true}, // IPv6 for A record {"AAAA", "@", "::1", false}, - {"AAAA", "@", "1.2.3.4", true}, // IPv4 for AAAA record + {"AAAA", "@", "1.2.3.4", true}, // IPv4 for AAAA record {"AAAA", "@", "not-an-ip", true}, {"CNAME", "www", "target.example.com.", false}, {"CNAME", "@", "target.example.com.", true}, // apex CNAME @@ -113,7 +113,7 @@ func TestDNSAnswerWarnings(t *testing.T) { {"CNAME", "target.example.com", 0, false, "trailing dot"}, {"CNAME", "target.example.com.", 0, false, ""}, {"MX", "mail.example.com", 0, false, "priority"}, - {"MX", "mail.example.com", 0, true, ""}, // priority explicitly set + {"MX", "mail.example.com", 0, true, ""}, // priority explicitly set {"MX", "mail.example.com", 10, false, ""}, // non-zero priority {"TXT", "v=spf1 ~all", 0, false, ""}, } diff --git a/cmd/dns/dns.go b/cmd/dns/dns.go index 82c8de3..19549b0 100644 --- a/cmd/dns/dns.go +++ b/cmd/dns/dns.go @@ -39,7 +39,7 @@ var ( updateTTL int64 updatePriority int64 - exportZone bool + exportZone bool importFile string importDryRun bool ) diff --git a/cmd/dns/dns_test.go b/cmd/dns/dns_test.go index 764c060..b6f3af8 100644 --- a/cmd/dns/dns_test.go +++ b/cmd/dns/dns_test.go @@ -37,9 +37,9 @@ func cmdForCreate(t *testing.T, srv *httptest.Server) *cobra.Command { t.Fatalf("api.New: %v", err) } out := &output.Config{ - Format: output.FormatTable, - Color: output.ColorNever, - Writer: &bytes.Buffer{}, + Format: output.FormatTable, + Color: output.ColorNever, + Writer: &bytes.Buffer{}, EWriter: &bytes.Buffer{}, } cmd := &cobra.Command{} @@ -264,7 +264,7 @@ func TestDNSCreate_DomainNormalized(t *testing.T) { func recordServer(t *testing.T, records []gen.Record, nextPage int32) *httptest.Server { t.Helper() - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(gen.ListRecordsResponseSchema{ Records: records, diff --git a/cmd/dnssec/dnssec.go b/cmd/dnssec/dnssec.go index c1760ce..273e50f 100644 --- a/cmd/dnssec/dnssec.go +++ b/cmd/dnssec/dnssec.go @@ -26,36 +26,36 @@ var ( ) var listCmd = &cobra.Command{ - Use: "list ", - Short: "List DNSSEC keys for a domain", - Example: ` namecom dnssec list example.com`, + Use: "list ", + Short: "List DNSSEC keys for a domain", + Example: ` namecom dnssec list example.com`, Args: cmdutil.ExactArgs(1), RunE: runList, ValidArgsFunction: cmdutil.CompleteDomains, } var getCmd = &cobra.Command{ - Use: "get ", - Short: "Get a specific DNSSEC key", - Example: ` namecom dnssec get example.com abc123def456`, + Use: "get ", + Short: "Get a specific DNSSEC key", + Example: ` namecom dnssec get example.com abc123def456`, Args: cmdutil.ExactArgs(2), RunE: runGet, ValidArgsFunction: cmdutil.CompleteDomains, } var createCmd = &cobra.Command{ - Use: "create ", - Short: "Add a DNSSEC key", - Example: ` namecom dnssec create example.com --algorithm 8 --digest-type 2 --key-tag 12345 --digest abc123`, + Use: "create ", + Short: "Add a DNSSEC key", + Example: ` namecom dnssec create example.com --algorithm 8 --digest-type 2 --key-tag 12345 --digest abc123`, Args: cmdutil.ExactArgs(1), RunE: runCreate, ValidArgsFunction: cmdutil.CompleteDomains, } var deleteCmd = &cobra.Command{ - Use: "delete ", - Short: "Remove a DNSSEC key", - Example: ` namecom dnssec delete example.com abc123def456`, + Use: "delete ", + Short: "Remove a DNSSEC key", + Example: ` namecom dnssec delete example.com abc123def456`, Args: cmdutil.ExactArgs(2), RunE: runDelete, ValidArgsFunction: cmdutil.CompleteDomains, diff --git a/cmd/domain/domain.go b/cmd/domain/domain.go index 4b320b4..e2996aa 100644 --- a/cmd/domain/domain.go +++ b/cmd/domain/domain.go @@ -36,7 +36,6 @@ func confirm(out *output.Config, yes bool, msg string) (bool, error) { return cmdutil.Confirm(out, yes, msg) } - // derefBool dereferences a *bool, returning false for nil. func derefBool(b *bool) bool { if b == nil { diff --git a/cmd/domain/list_get.go b/cmd/domain/list_get.go index b37f079..1e1833c 100644 --- a/cmd/domain/list_get.go +++ b/cmd/domain/list_get.go @@ -206,9 +206,9 @@ func runList(cmd *cobra.Command, _ []string) error { rows = append(rows, []string{ d.DomainName, out.ExpiryDate(d.ExpireDate), - out.BoolBadge(bool(d.AutorenewEnabled)), - out.BoolBadge(bool(d.Locked)), - out.BoolBadge(bool(d.PrivacyEnabled)), + out.BoolBadge(d.AutorenewEnabled), + out.BoolBadge(d.Locked), + out.BoolBadge(d.PrivacyEnabled), }) } out.Table(headers, rows) @@ -263,9 +263,9 @@ func runGet(cmd *cobra.Command, args []string) error { {"Domain", d.DomainName}, {"Created", out.Dim(formatTime(d.CreateDate))}, {"Expires", out.ExpiryDate(d.ExpireDate)}, - {"Auto-Renew", out.BoolBadge(bool(d.AutorenewEnabled))}, - {"Locked", out.BoolBadge(bool(d.Locked))}, - {"Privacy", out.BoolBadge(bool(d.PrivacyEnabled))}, + {"Auto-Renew", out.BoolBadge(d.AutorenewEnabled)}, + {"Locked", out.BoolBadge(d.Locked)}, + {"Privacy", out.BoolBadge(d.PrivacyEnabled)}, {"Nameservers", out.Dim(formatNS(d.Nameservers))}, } out.KVTable(rows) diff --git a/cmd/domain/list_get_test.go b/cmd/domain/list_get_test.go index 813766e..a8fa41d 100644 --- a/cmd/domain/list_get_test.go +++ b/cmd/domain/list_get_test.go @@ -5,8 +5,8 @@ import ( "context" "encoding/json" "net/http" - "strconv" "net/http/httptest" + "strconv" "testing" "github.com/patramsey/namecom-cli/cmd/cmdutil" @@ -25,9 +25,9 @@ func TestFilterToWildcard(t *testing.T) { }{ {"acme", "*acme*"}, {"acme.io", "*acme.io*"}, - {"*acme", "*acme"}, // already has wildcard — leave alone - {"acme*", "acme*"}, // already has wildcard — leave alone - {"*acme*", "*acme*"}, // already has wildcard — leave alone + {"*acme", "*acme"}, // already has wildcard — leave alone + {"acme*", "acme*"}, // already has wildcard — leave alone + {"*acme*", "*acme*"}, // already has wildcard — leave alone } for _, tt := range tests { if got := filterToWildcard(tt.input); got != tt.want { diff --git a/cmd/domain/manage.go b/cmd/domain/manage.go index 683f719..c6c63db 100644 --- a/cmd/domain/manage.go +++ b/cmd/domain/manage.go @@ -76,8 +76,8 @@ var autorenewCmd = &cobra.Command{ Short: "Enable or disable automatic renewal", Example: ` namecom domain autorenew on example.com namecom domain autorenew off example.com`, - Args: cmdutil.ExactArgs(2), - RunE: runAutorenew, + Args: cmdutil.ExactArgs(2), + RunE: runAutorenew, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) == 0 { return []string{"on", "off"}, cobra.ShellCompDirectiveNoFileComp @@ -131,8 +131,8 @@ var privacyCmd = &cobra.Command{ Short: "Enable or disable WHOIS privacy", Example: ` namecom domain privacy on example.com namecom domain privacy off example.com`, - Args: cmdutil.ExactArgs(2), - RunE: runPrivacy, + Args: cmdutil.ExactArgs(2), + RunE: runPrivacy, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) == 0 { return []string{"on", "off"}, cobra.ShellCompDirectiveNoFileComp @@ -459,9 +459,9 @@ func runUpdate(cmd *cobra.Command, args []string) error { return err } - autorenew := bool(current.AutorenewEnabled) - privacy := bool(current.PrivacyEnabled) - locked := bool(current.Locked) + autorenew := current.AutorenewEnabled + privacy := current.PrivacyEnabled + locked := current.Locked body := gen.UpdateDomainJSONRequestBody{ AutorenewEnabled: &autorenew, PrivacyEnabled: &privacy, diff --git a/cmd/email/email.go b/cmd/email/email.go index e46fe8d..97c95ec 100644 --- a/cmd/email/email.go +++ b/cmd/email/email.go @@ -38,9 +38,9 @@ var listCmd = &cobra.Command{ } var getCmd = &cobra.Command{ - Use: "get ", - Short: "Get an email forwarding entry", - Example: ` namecom email get example.com info`, + Use: "get ", + Short: "Get an email forwarding entry", + Example: ` namecom email get example.com info`, Args: cmdutil.ExactArgs(2), RunE: runGet, ValidArgsFunction: cmdutil.CompleteDomains, @@ -57,18 +57,18 @@ var createCmd = &cobra.Command{ } var updateCmd = &cobra.Command{ - Use: "update ", - Short: "Update an email forwarding entry", - Example: ` namecom email update example.com info --to newemail@gmail.com`, + Use: "update ", + Short: "Update an email forwarding entry", + Example: ` namecom email update example.com info --to newemail@gmail.com`, Args: cmdutil.ExactArgs(2), RunE: runUpdate, ValidArgsFunction: cmdutil.CompleteDomains, } var deleteCmd = &cobra.Command{ - Use: "delete ", - Short: "Delete an email forwarding entry", - Example: ` namecom email delete example.com info`, + Use: "delete ", + Short: "Delete an email forwarding entry", + Example: ` namecom email delete example.com info`, Args: cmdutil.ExactArgs(2), RunE: runDelete, ValidArgsFunction: cmdutil.CompleteDomains, diff --git a/cmd/order/order.go b/cmd/order/order.go index 81510c9..ec3986d 100644 --- a/cmd/order/order.go +++ b/cmd/order/order.go @@ -48,19 +48,19 @@ var listCmd = &cobra.Command{ } var getCmd = &cobra.Command{ - Use: "get ", - Short: "Get an order by ID", + Use: "get ", + Short: "Get an order by ID", Example: ` namecom order get 12345`, - Args: cmdutil.ExactArgs(1), - RunE: runGet, + Args: cmdutil.ExactArgs(1), + RunE: runGet, } var refundCmd = &cobra.Command{ - Use: "refund", - Short: "Process a refund for order items", + Use: "refund", + Short: "Process a refund for order items", Example: ` namecom order refund --order-id 12345 --item-ids 67890 --yes`, - Args: cobra.NoArgs, - RunE: runRefund, + Args: cobra.NoArgs, + RunE: runRefund, } func init() { @@ -79,7 +79,7 @@ func init() { Cmd.AddCommand(listCmd, getCmd, refundCmd) } -func runList(cmd *cobra.Command, args []string) error { +func runList(cmd *cobra.Command, _ []string) error { out := cmdutil.Out(cmd) client := cmdutil.APIClient(cmd) @@ -217,7 +217,7 @@ func runGet(cmd *cobra.Command, args []string) error { return nil } -func runRefund(cmd *cobra.Command, args []string) error { +func runRefund(cmd *cobra.Command, _ []string) error { out := cmdutil.Out(cmd) client := cmdutil.APIClient(cmd) yes := cmdutil.IsYes(cmd) diff --git a/cmd/order/order_test.go b/cmd/order/order_test.go index 3a08d98..41b3930 100644 --- a/cmd/order/order_test.go +++ b/cmd/order/order_test.go @@ -5,8 +5,8 @@ import ( "context" "encoding/json" "net/http" - "strconv" "net/http/httptest" + "strconv" "testing" "github.com/patramsey/namecom-cli/cmd/cmdutil" @@ -155,8 +155,8 @@ func TestOrderList_StatusFilterPassedToAPI(t *testing.T) { func TestOrderList_FilterAutoPages(t *testing.T) { srv, requests := orderServer(t, [][]int32{ - {101}, // page 1 — NextPage=2 - {102}, // page 2 — no NextPage + {101}, // page 1 — NextPage=2 + {102}, // page 2 — no NextPage }) var stdout, stderr bytes.Buffer cmd := cmdForOrderList(t, srv, &stdout, &stderr) diff --git a/cmd/root.go b/cmd/root.go index c23f457..a7e73e5 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -14,8 +14,8 @@ import ( "github.com/patramsey/namecom-cli/cmd/cmdutil" configcmd "github.com/patramsey/namecom-cli/cmd/config" "github.com/patramsey/namecom-cli/cmd/dns" - "github.com/patramsey/namecom-cli/cmd/domain" "github.com/patramsey/namecom-cli/cmd/dnssec" + "github.com/patramsey/namecom-cli/cmd/domain" "github.com/patramsey/namecom-cli/cmd/email" "github.com/patramsey/namecom-cli/cmd/order" "github.com/patramsey/namecom-cli/cmd/transfer" @@ -31,21 +31,20 @@ import ( // Use the shared context keys from cmdutil so subpackages can retrieve values // without importing cmd (which would create a cycle). - // Version is set at build time via -ldflags "-X main.version=x.y.z". var Version = "dev" // globalFlags holds the parsed values of all root-level persistent flags. type globalFlags struct { - profile string - username string - token string - sandbox bool - output string - quiet bool - noHeader bool - color string - timeout time.Duration + profile string + username string + token string + sandbox bool + output string + quiet bool + noHeader bool + color string + timeout time.Duration debug bool debugFile string yes bool @@ -164,9 +163,6 @@ func init() { pf.BoolVar(&gf.dryRun, "dry-run", false, "print the API request that would be sent without executing it") pf.StringVar(&gf.idempKey, "idempotency-key", "", "idempotency key for write operations (auto-generated per invocation if not set)") - // Mark --sandbox so we can detect explicit-false vs absent. - _ = pf.Lookup("sandbox").Value // exists — no error path needed - // Apply styled help to every command in the tree. cobra.AddTemplateFunc("styleHelp", func() bool { return true }) // trigger late-bind rootCmd.SetHelpFunc(styledHelp) diff --git a/cmd/status.go b/cmd/status.go index 62a61b7..ab2c2d7 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -28,15 +28,15 @@ func init() { } type statusSummary struct { - Profile string `json:"profile"` - Endpoint string `json:"endpoint"` - DomainsTotal int `json:"domains_total"` - ExpiringCritical int `json:"expiring_critical"` // <7 days - ExpiringSoon int `json:"expiring_soon"` // 7-30 days - Unlocked int `json:"unlocked"` - PendingTransfers int `json:"pending_transfers"` - ExpiringDomains []expiryItem `json:"expiring_domains,omitempty"` - PendingDomains []string `json:"pending_transfer_domains,omitempty"` + Profile string `json:"profile"` + Endpoint string `json:"endpoint"` + DomainsTotal int `json:"domains_total"` + ExpiringCritical int `json:"expiring_critical"` // <7 days + ExpiringSoon int `json:"expiring_soon"` // 7-30 days + Unlocked int `json:"unlocked"` + PendingTransfers int `json:"pending_transfers"` + ExpiringDomains []expiryItem `json:"expiring_domains,omitempty"` + PendingDomains []string `json:"pending_transfer_domains,omitempty"` } type expiryItem struct { @@ -260,5 +260,4 @@ func renderStatus(out *output.Config, s statusSummary) { out.Hint("Run 'namecom domain list' to see all domains") } - func ptrInt32(n int32) *int32 { return &n } diff --git a/cmd/transfer/transfer.go b/cmd/transfer/transfer.go index 276ef7c..7d375fa 100644 --- a/cmd/transfer/transfer.go +++ b/cmd/transfer/transfer.go @@ -43,7 +43,7 @@ var listCmd = &cobra.Command{ var getCmd = &cobra.Command{ Use: "get ", Short: "Get a transfer's status", - Example: ` namecom transfer get example.com`, + Example: ` namecom transfer get example.com`, Args: cmdutil.ExactArgs(1), RunE: runGet, ValidArgsFunction: cmdutil.CompleteDomains, @@ -54,22 +54,22 @@ var createCmd = &cobra.Command{ Short: "Initiate a transfer in from another registrar", Example: ` namecom transfer create example.com --auth-code XXXXXX namecom transfer create example.com --auth-code XXXXXX --privacy`, - Args: cmdutil.ExactArgs(1), - RunE: runCreate, + Args: cmdutil.ExactArgs(1), + RunE: runCreate, } var internalCmd = &cobra.Command{ - Use: "internal-in ", - Short: "Move a domain between name.com accounts (no EPP wait required)", + Use: "internal-in ", + Short: "Move a domain between name.com accounts (no EPP wait required)", Example: ` namecom transfer internal-in example.com --auth-code XXXXXX`, - Args: cmdutil.ExactArgs(1), - RunE: runInternalIn, + Args: cmdutil.ExactArgs(1), + RunE: runInternalIn, } var cancelCmd = &cobra.Command{ Use: "cancel ", Short: "Cancel an in-progress transfer", - Example: ` namecom transfer cancel example.com`, + Example: ` namecom transfer cancel example.com`, Args: cmdutil.ExactArgs(1), RunE: runCancel, ValidArgsFunction: cmdutil.CompleteDomains, @@ -78,7 +78,7 @@ var cancelCmd = &cobra.Command{ var cancelOutboundCmd = &cobra.Command{ Use: "cancel-outbound ", Short: "Cancel an outbound transfer-out", - Example: ` namecom transfer cancel-outbound example.com`, + Example: ` namecom transfer cancel-outbound example.com`, Args: cmdutil.ExactArgs(1), RunE: runCancelOutbound, ValidArgsFunction: cmdutil.CompleteDomains, @@ -87,7 +87,7 @@ var cancelOutboundCmd = &cobra.Command{ var eligibilityCmd = &cobra.Command{ Use: "eligibility ", Short: "Check if a domain is eligible for transfer", - Example: ` namecom transfer eligibility example.com`, + Example: ` namecom transfer eligibility example.com`, Args: cmdutil.ExactArgs(1), RunE: runEligibility, ValidArgsFunction: cmdutil.CompleteDomains, @@ -106,7 +106,7 @@ func init() { Cmd.AddCommand(listCmd, getCmd, createCmd, internalCmd, cancelCmd, cancelOutboundCmd, eligibilityCmd) } -func runList(cmd *cobra.Command, args []string) error { +func runList(cmd *cobra.Command, _ []string) error { out := cmdutil.Out(cmd) client := cmdutil.APIClient(cmd) diff --git a/cmd/url/url.go b/cmd/url/url.go index c98e82e..707678d 100644 --- a/cmd/url/url.go +++ b/cmd/url/url.go @@ -46,9 +46,9 @@ var listCmd = &cobra.Command{ } var getCmd = &cobra.Command{ - Use: "get ", - Short: "Get a URL forwarding entry by ID", - Example: ` namecom url get example.com 12345`, + Use: "get ", + Short: "Get a URL forwarding entry by ID", + Example: ` namecom url get example.com 12345`, Args: cmdutil.ExactArgs(2), RunE: runGet, ValidArgsFunction: cmdutil.CompleteDomains, @@ -66,18 +66,18 @@ var createCmd = &cobra.Command{ } var updateCmd = &cobra.Command{ - Use: "update ", - Short: "Update a URL forwarding entry", - Example: ` namecom url update example.com 12345 --to https://other-site.com`, + Use: "update ", + Short: "Update a URL forwarding entry", + Example: ` namecom url update example.com 12345 --to https://other-site.com`, Args: cmdutil.ExactArgs(2), RunE: runUpdate, ValidArgsFunction: cmdutil.CompleteDomains, } var deleteCmd = &cobra.Command{ - Use: "delete ", - Short: "Delete a URL forwarding entry", - Example: ` namecom url delete example.com 12345`, + Use: "delete ", + Short: "Delete a URL forwarding entry", + Example: ` namecom url delete example.com 12345`, Args: cmdutil.ExactArgs(2), RunE: runDelete, ValidArgsFunction: cmdutil.CompleteDomains, diff --git a/cmd/url/url_test.go b/cmd/url/url_test.go index d7a7628..f425ae9 100644 --- a/cmd/url/url_test.go +++ b/cmd/url/url_test.go @@ -97,7 +97,7 @@ func TestURLCreate_ValidTypes(t *testing.T) { for _, fwdType := range []string{"redirect", "302", "masked"} { t.Run(fwdType, func(t *testing.T) { var called bool - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { called = true w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"host":"@","forwardsTo":"https://example.com","type":"` + fwdType + `"}`)) diff --git a/cmd/vanity/vanity.go b/cmd/vanity/vanity.go index 88272b4..9c546bd 100644 --- a/cmd/vanity/vanity.go +++ b/cmd/vanity/vanity.go @@ -36,9 +36,9 @@ var listCmd = &cobra.Command{ } var getCmd = &cobra.Command{ - Use: "get ", - Short: "Get a vanity nameserver", - Example: ` namecom vanity-ns get example.com ns1.example.com`, + Use: "get ", + Short: "Get a vanity nameserver", + Example: ` namecom vanity-ns get example.com ns1.example.com`, Args: cmdutil.ExactArgs(2), RunE: runGet, ValidArgsFunction: cmdutil.CompleteDomains, @@ -55,18 +55,18 @@ var createCmd = &cobra.Command{ } var updateCmd = &cobra.Command{ - Use: "update ", - Short: "Update vanity nameserver IPs", - Example: ` namecom vanity-ns update example.com ns1.example.com --ips 1.2.3.4,5.6.7.8`, + Use: "update ", + Short: "Update vanity nameserver IPs", + Example: ` namecom vanity-ns update example.com ns1.example.com --ips 1.2.3.4,5.6.7.8`, Args: cmdutil.ExactArgs(2), RunE: runUpdate, ValidArgsFunction: cmdutil.CompleteDomains, } var deleteCmd = &cobra.Command{ - Use: "delete ", - Short: "Delete a vanity nameserver", - Example: ` namecom vanity-ns delete example.com ns1.example.com`, + Use: "delete ", + Short: "Delete a vanity nameserver", + Example: ` namecom vanity-ns delete example.com ns1.example.com`, Args: cmdutil.ExactArgs(2), RunE: runDelete, ValidArgsFunction: cmdutil.CompleteDomains, diff --git a/go.mod b/go.mod index 870cacd..80a29fd 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/oapi-codegen/runtime v1.4.1 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 + golang.org/x/mod v0.30.0 golang.org/x/sync v0.21.0 golang.org/x/term v0.44.0 golang.org/x/time v0.15.0 @@ -56,7 +57,6 @@ require ( github.com/speakeasy-api/openapi-overlay v0.9.0 // indirect github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect - golang.org/x/mod v0.30.0 // indirect golang.org/x/sys v0.46.0 // indirect golang.org/x/text v0.32.0 // indirect golang.org/x/tools v0.39.0 // indirect diff --git a/internal/api/client.go b/internal/api/client.go index 0bcc032..b64640e 100644 --- a/internal/api/client.go +++ b/internal/api/client.go @@ -8,6 +8,7 @@ import ( "context" "encoding/base64" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -157,7 +158,7 @@ func Decode(resp *http.Response, out any) error { _, _ = io.Copy(io.Discard, resp.Body) return nil } - if err := json.NewDecoder(resp.Body).Decode(out); err != nil && err != io.EOF { + if err := json.NewDecoder(resp.Body).Decode(out); err != nil && !errors.Is(err, io.EOF) { return fmt.Errorf("decoding response: %w", err) } return nil diff --git a/internal/api/transport.go b/internal/api/transport.go index aff5fd2..70faebe 100644 --- a/internal/api/transport.go +++ b/internal/api/transport.go @@ -5,7 +5,7 @@ import ( "context" "fmt" "io" - "math/rand" + "math/rand/v2" "net/http" "strconv" "time" @@ -147,7 +147,7 @@ func (t *retryTransport) backoffDelay(attempt int, retryAfter *time.Duration) ti } const maxBackoff = 30 * time.Second base := min(unit< Checking availability…") to // stdout. Only emitted in table/interactive mode, skipped when quiet or piped. func (c *Config) Step(msg string) { @@ -737,7 +738,7 @@ func (c *Config) Amber(s string) string { } // IsStderrTTY reports whether stderr is a terminal. -func IsStderrTTY() bool { return isStderrTTY() } +func IsStderrTTY() bool { return isStderrTTY() } func isStdoutTTY() bool { return term.IsTerminal(int(os.Stdout.Fd())) } func isStdinTTY() bool { return term.IsTerminal(int(os.Stdin.Fd())) } func isStderrTTY() bool { return term.IsTerminal(int(os.Stderr.Fd())) } diff --git a/internal/update/update.go b/internal/update/update.go index c32e82d..8711e94 100644 --- a/internal/update/update.go +++ b/internal/update/update.go @@ -10,14 +10,15 @@ import ( "net/http" "os" "path/filepath" - "strconv" "strings" "time" + + "golang.org/x/mod/semver" ) const ( - releaseURL = "https://api.github.com/repos/patramsey/namecom-cli/releases/latest" - cacheTTL = 24 * time.Hour + releaseURL = "https://api.github.com/repos/patramsey/namecom-cli/releases/latest" + cacheTTL = 24 * time.Hour httpTimeout = 2 * time.Second ) @@ -119,29 +120,13 @@ func writeCache(version string) { } // isNewer returns true if candidate is a strictly higher semver than current. +// Both values are expected without a leading "v"; this function adds it for +// golang.org/x/mod/semver which requires canonical "vX.Y.Z" form. func isNewer(candidate, current string) bool { - c := parseSemver(candidate) - cur := parseSemver(current) - for i := 0; i < len(c); i++ { - if i >= len(cur) { - return c[i] > 0 - } - if c[i] > cur[i] { - return true - } - if c[i] < cur[i] { - return false - } - } - return false -} - -func parseSemver(v string) []int { - parts := strings.Split(v, ".") - out := make([]int, len(parts)) - for i, p := range parts { - n, _ := strconv.Atoi(p) - out[i] = n - } - return out + c := "v" + candidate + cur := "v" + current + if !semver.IsValid(c) || !semver.IsValid(cur) { + return false + } + return semver.Compare(c, cur) > 0 } diff --git a/main.go b/main.go index dbddb21..133e903 100644 --- a/main.go +++ b/main.go @@ -1,3 +1,4 @@ +// Package main is the entry point for the namecom CLI. package main import "github.com/patramsey/namecom-cli/cmd"