Skip to content

Commit 5e742fe

Browse files
authored
refactor: replace cobra with coder/serpent (#158)
Closes #45 - Migrate from spf13/cobra to coder/serpent CLI framework - Use serpent's native environment variable support - Use serpent's native default value support - Improved help output formatting
1 parent 3999bae commit 5e742fe

4 files changed

Lines changed: 56 additions & 27 deletions

File tree

go.mod

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ require (
1010
github.com/breml/rootcerts v0.3.1
1111
github.com/coder/coder/v2 v2.29.3
1212
github.com/coder/quartz v0.3.0
13+
github.com/coder/serpent v0.13.0
1314
github.com/coder/websocket v1.8.14
1415
github.com/fatih/color v1.18.0
1516
github.com/go-chi/chi/v5 v5.2.2
1617
github.com/google/uuid v1.6.0
1718
github.com/hashicorp/yamux v0.1.2
18-
github.com/spf13/cobra v1.10.1
1919
github.com/stretchr/testify v1.11.1
2020
google.golang.org/protobuf v1.36.10
2121
k8s.io/api v0.34.1
@@ -25,6 +25,7 @@ require (
2525
)
2626

2727
require (
28+
cdr.dev/slog/v3 v3.0.0-rc1 // indirect
2829
cloud.google.com/go/compute/metadata v0.9.0 // indirect
2930
filippo.io/edwards25519 v1.1.0 // indirect
3031
github.com/DataDog/appsec-internal-go v1.11.2 // indirect
@@ -85,7 +86,6 @@ require (
8586
github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect
8687
github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 // indirect
8788
github.com/coder/retry v1.5.1 // indirect
88-
github.com/coder/serpent v0.12.0 // indirect
8989
github.com/coder/terraform-provider-coder/v2 v2.13.1 // indirect
9090
github.com/coreos/go-iptables v0.6.0 // indirect
9191
github.com/coreos/go-oidc/v3 v3.17.0 // indirect
@@ -134,7 +134,6 @@ require (
134134
github.com/hdevalence/ed25519consensus v0.1.0 // indirect
135135
github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 // indirect
136136
github.com/illarion/gonotify v1.0.1 // indirect
137-
github.com/inconshreveable/mousetrap v1.1.0 // indirect
138137
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 // indirect
139138
github.com/josharian/intern v1.0.0 // indirect
140139
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect

go.sum

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
cdr.dev/slog v1.6.2-0.20250703074222-9df5e0a6c145 h1:Mk4axSLxKw3hjkf3PffBLQYta7nPVIWObuKCPDWgQLc=
22
cdr.dev/slog v1.6.2-0.20250703074222-9df5e0a6c145/go.mod h1:NaoTA7KwopCrnaSb0JXTC0PTp/O/Y83Lndnq0OEV3ZQ=
3+
cdr.dev/slog/v3 v3.0.0-rc1 h1:EN7Zim6GvTpAeHQjI0ERDEfqKbTyXRvgH4UhlzLpvWM=
4+
cdr.dev/slog/v3 v3.0.0-rc1/go.mod h1:iO/OALX1VxlI03mkodCGdVP7pXzd2bRMvu3ePvlJ9ak=
35
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
46
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
57
cloud.google.com/go v0.121.4 h1:cVvUiY0sX0xwyxPwdSU2KsF9knOVmtRyAMt8xou0iTs=
@@ -230,8 +232,8 @@ github.com/coder/quartz v0.3.0 h1:bUoSEJ77NBfKtUqv6CPSC0AS8dsjqAqqAv7bN02m1mg=
230232
github.com/coder/quartz v0.3.0/go.mod h1:BgE7DOj/8NfvRgvKw0jPLDQH/2Lya2kxcTaNJ8X0rZk=
231233
github.com/coder/retry v1.5.1 h1:iWu8YnD8YqHs3XwqrqsjoBTAVqT9ml6z9ViJ2wlMiqc=
232234
github.com/coder/retry v1.5.1/go.mod h1:blHMk9vs6LkoRT9ZHyuZo360cufXEhrxqvEzeMtRGoY=
233-
github.com/coder/serpent v0.12.0 h1:fUu3qVjeRvVy3DB/C2EFFvOctm+f2HKyckyfA86O63Q=
234-
github.com/coder/serpent v0.12.0/go.mod h1:mPEpD8Cq106E0glBs5ROAAGoALLtD5HAAMVZmjf4zO0=
235+
github.com/coder/serpent v0.13.0 h1:6EoWjpEypkb8cS6i0eCF4qoAv9vrEVaX26RW+3FMMvo=
236+
github.com/coder/serpent v0.13.0/go.mod h1:7OIvFBYMd+OqarMy5einBl8AtRr8LliopVU7pyrwucY=
235237
github.com/coder/tailscale v1.1.1-0.20250829055706-6eafe0f9199e h1:9RKGKzGLHtTvVBQublzDGtCtal3cXP13diCHoAIGPeI=
236238
github.com/coder/tailscale v1.1.1-0.20250829055706-6eafe0f9199e/go.mod h1:jU9T1vEs+DOs8NtGp1F2PT0/TOGVwtg/JCCKYRgvMOs=
237239
github.com/coder/terraform-provider-coder/v2 v2.13.1 h1:dtPaJUvueFm+XwBPUMWQCc5Z1QUQBW4B4RNyzX4h4y8=
@@ -244,7 +246,6 @@ github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo
244246
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
245247
github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=
246248
github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
247-
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
248249
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
249250
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
250251
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -488,8 +489,6 @@ github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJ
488489
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
489490
github.com/illarion/gonotify v1.0.1 h1:F1d+0Fgbq/sDWjj/r66ekjDG+IDeecQKUFH4wNwsoio=
490491
github.com/illarion/gonotify v1.0.1/go.mod h1:zt5pmDofZpU1f8aqlK0+95eQhoEAn/d4G4B/FjVW4jE=
491-
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
492-
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
493492
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
494493
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
495494
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
@@ -680,7 +679,6 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
680679
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
681680
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
682681
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
683-
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
684682
github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
685683
github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
686684
github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc=
@@ -698,9 +696,6 @@ github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
698696
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
699697
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
700698
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
701-
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
702-
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
703-
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
704699
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
705700
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
706701
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=

main.go

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,72 @@ import (
99

1010
"cdr.dev/slog"
1111
"cdr.dev/slog/sloggers/sloghuman"
12-
"github.com/spf13/cobra"
12+
"github.com/coder/serpent"
1313
"k8s.io/client-go/kubernetes"
1414
restclient "k8s.io/client-go/rest"
1515
"k8s.io/client-go/tools/clientcmd"
1616
)
1717

1818
func main() {
1919
cmd := root()
20-
err := cmd.Execute()
20+
err := cmd.Invoke().WithOS().Run()
2121
if err != nil {
2222
os.Exit(1)
2323
}
2424
}
2525

26-
func root() *cobra.Command {
26+
func root() *serpent.Command {
2727
var (
2828
coderURL string
2929
fieldSelector string
3030
kubeConfig string
3131
namespacesStr string
3232
labelSelector string
3333
)
34-
cmd := &cobra.Command{
34+
cmd := &serpent.Command{
3535
Use: "coder-logstream-kube",
3636
Short: "Stream Kubernetes Pod events to the Coder startup logs.",
37-
RunE: func(cmd *cobra.Command, _ []string) error {
37+
Options: serpent.OptionSet{
38+
{
39+
Name: "coder-url",
40+
Flag: "coder-url",
41+
FlagShorthand: "u",
42+
Env: "CODER_URL",
43+
Value: serpent.StringOf(&coderURL),
44+
Description: "URL of the Coder instance.",
45+
},
46+
{
47+
Name: "kubeconfig",
48+
Flag: "kubeconfig",
49+
FlagShorthand: "k",
50+
Default: "~/.kube/config",
51+
Value: serpent.StringOf(&kubeConfig),
52+
Description: "Path to the kubeconfig file.",
53+
},
54+
{
55+
Name: "namespaces",
56+
Flag: "namespaces",
57+
FlagShorthand: "n",
58+
Env: "CODER_NAMESPACES",
59+
Value: serpent.StringOf(&namespacesStr),
60+
Description: "List of namespaces to use when listing pods.",
61+
},
62+
{
63+
Name: "field-selector",
64+
Flag: "field-selector",
65+
FlagShorthand: "f",
66+
Value: serpent.StringOf(&fieldSelector),
67+
Description: "Field selector to use when listing pods.",
68+
},
69+
{
70+
Name: "label-selector",
71+
Flag: "label-selector",
72+
FlagShorthand: "l",
73+
Value: serpent.StringOf(&labelSelector),
74+
Description: "Label selector to use when listing pods.",
75+
},
76+
},
77+
Handler: func(inv *serpent.Invocation) error {
3878
if coderURL == "" {
3979
return fmt.Errorf("--coder-url is required")
4080
}
@@ -75,13 +115,13 @@ func root() *cobra.Command {
75115
}
76116
}
77117

78-
reporter, err := newPodEventLogger(cmd.Context(), podEventLoggerOptions{
118+
reporter, err := newPodEventLogger(inv.Context(), podEventLoggerOptions{
79119
coderURL: parsedURL,
80120
client: client,
81121
namespaces: namespaces,
82122
fieldSelector: fieldSelector,
83123
labelSelector: labelSelector,
84-
logger: slog.Make(sloghuman.Sink(cmd.ErrOrStderr())).Leveled(slog.LevelDebug),
124+
logger: slog.Make(sloghuman.Sink(inv.Stderr)).Leveled(slog.LevelDebug),
85125
maxRetries: 15, // 15 retries is the default max retries for a log send failure.
86126
})
87127
if err != nil {
@@ -93,16 +133,11 @@ func root() *cobra.Command {
93133
select {
94134
case err := <-reporter.errChan:
95135
return fmt.Errorf("pod event reporter: %w", err)
96-
case <-cmd.Context().Done():
136+
case <-inv.Context().Done():
97137
}
98138
return nil
99139
},
100140
}
101-
cmd.Flags().StringVarP(&coderURL, "coder-url", "u", os.Getenv("CODER_URL"), "URL of the Coder instance")
102-
cmd.Flags().StringVarP(&kubeConfig, "kubeconfig", "k", "~/.kube/config", "Path to the kubeconfig file")
103-
cmd.Flags().StringVarP(&namespacesStr, "namespaces", "n", os.Getenv("CODER_NAMESPACES"), "List of namespaces to use when listing pods")
104-
cmd.Flags().StringVarP(&fieldSelector, "field-selector", "f", "", "Field selector to use when listing pods")
105-
cmd.Flags().StringVarP(&labelSelector, "label-selector", "l", "", "Label selector to use when listing pods")
106141

107142
return cmd
108143
}

main_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ func TestRootCommand_URLValidation(t *testing.T) {
3636
for _, tt := range tests {
3737
t.Run(tt.name, func(t *testing.T) {
3838
cmd := root()
39-
cmd.SetArgs([]string{"--coder-url", tt.coderURL})
39+
inv := cmd.Invoke("--coder-url", tt.coderURL)
4040

41-
err := cmd.Execute()
41+
err := inv.Run()
4242

4343
if err == nil {
4444
t.Errorf("expected error containing %q, got nil", tt.wantError)

0 commit comments

Comments
 (0)