Skip to content

Commit 70f39be

Browse files
committed
feat(ophis) change selector helper functions for more clear and common use cases
1 parent 09b01e0 commit 70f39be

File tree

10 files changed

+247
-49
lines changed

10 files changed

+247
-49
lines changed

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ Different commands can have different flag rules and execution hooks:
9090
config := &ophis.Config{
9191
Selectors: []ophis.Selector{
9292
{
93-
// Select read-only commands
94-
CmdSelector: ophis.AllowCmd("get", "helm repo list", "logs"),
93+
// Select all get commands
94+
CmdSelector: ophis.AllowCmdsContaining("get"),
9595

9696
// LocalFlagSelector is nil, selecting all local flags
9797

@@ -106,11 +106,11 @@ config := &ophis.Config{
106106
},
107107
},
108108
{
109-
// Select write commands
110-
CmdSelector: ophis.AllowCmd("helm repo add", "apply"),
109+
// Select exactly these write commands
110+
CmdSelector: ophis.AllowCmds("mycli create", "mycli apply"),
111111

112112
// Exclude dangerous flags
113-
LocalFlagSelector: ophis.ExcludeFlag("force", "token", "insecure"),
113+
LocalFlagSelector: ophis.ExcludeFlags("force", "token", "insecure"),
114114

115115
// Exclude all inherited flags
116116
InheritedFlagSelector: ophis.NoFlags,
@@ -119,7 +119,7 @@ config := &ophis.Config{
119119
// CmdSelector is nil, selecting all remaining commands
120120

121121
// Exclude common, dangerous local flags
122-
LocalFlagSelector: ophis.ExcludeFlag("token", "insecure"),
122+
LocalFlagSelector: ophis.ExcludeFlags("token", "insecure"),
123123

124124
// Exclude all inherited flags
125125
InheritedFlagSelector: ophis.NoFlags,

examples/argocd/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func rootCmd() *cobra.Command {
2222
command.AddCommand(ophis.Command(&ophis.Config{
2323
Selectors: []ophis.Selector{
2424
{
25-
CmdSelector: ophis.AllowCmd(
25+
CmdSelector: ophis.AllowCmds(
2626
"argocd app get",
2727
"argocd app list",
2828
"argocd app diff",

examples/helm/config.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ func config() *ophis.Config {
1111
func selectors() []ophis.Selector {
1212
return []ophis.Selector{
1313
{
14-
CmdSelector: ophis.AllowCmd(
14+
CmdSelector: ophis.AllowCmdsContaining(
1515
// helm get [all, hooks, manifest, metadata, notes, values]
1616
"helm get",
1717
"helm history",
@@ -22,7 +22,8 @@ func selectors() []ophis.Selector {
2222
"helm show",
2323
"helm status",
2424
),
25-
InheritedFlagSelector: ophis.AllowFlag("namespace"),
25+
26+
InheritedFlagSelector: ophis.AllowFlags("namespace"),
2627
},
2728
}
2829
}

examples/helm/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ require (
113113
golang.org/x/term v0.35.0 // indirect
114114
golang.org/x/text v0.29.0 // indirect
115115
golang.org/x/time v0.13.0 // indirect
116-
google.golang.org/protobuf v1.36.9 // indirect
116+
google.golang.org/protobuf v1.36.10 // indirect
117117
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
118118
gopkg.in/inf.v0 v0.9.1 // indirect
119119
gopkg.in/yaml.v3 v3.0.1 // indirect
@@ -126,7 +126,7 @@ require (
126126
k8s.io/klog/v2 v2.130.1 // indirect
127127
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect
128128
k8s.io/kubectl v0.34.1 // indirect
129-
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d // indirect
129+
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect
130130
oras.land/oras-go/v2 v2.6.0 // indirect
131131
sigs.k8s.io/controller-runtime v0.22.1 // indirect
132132
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect

examples/helm/go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -412,8 +412,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:
412412
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
413413
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
414414
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
415-
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
416-
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
415+
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
416+
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
417417
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
418418
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
419419
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -448,8 +448,8 @@ k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZ
448448
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
449449
k8s.io/kubectl v0.34.1 h1:1qP1oqT5Xc93K+H8J7ecpBjaz511gan89KO9Vbsh/OI=
450450
k8s.io/kubectl v0.34.1/go.mod h1:JRYlhJpGPyk3dEmJ+BuBiOB9/dAvnrALJEiY/C5qa6A=
451-
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0=
452-
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
451+
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck=
452+
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
453453
oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc=
454454
oras.land/oras-go/v2 v2.6.0/go.mod h1:magiQDfG6H1O9APp+rOsvCPcW1GD2MM7vgnKY0Y+u1o=
455455
sigs.k8s.io/controller-runtime v0.22.1 h1:Ah1T7I+0A7ize291nJZdS1CabF/lB4E++WizgV24Eqg=

examples/kubectl/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ require (
8686
golang.org/x/term v0.35.0 // indirect
8787
golang.org/x/text v0.29.0 // indirect
8888
golang.org/x/time v0.13.0 // indirect
89-
google.golang.org/protobuf v1.36.9 // indirect
89+
google.golang.org/protobuf v1.36.10 // indirect
9090
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
9191
gopkg.in/inf.v0 v0.9.1 // indirect
9292
k8s.io/api v0.34.1 // indirect
@@ -96,7 +96,7 @@ require (
9696
k8s.io/klog/v2 v2.130.1 // indirect
9797
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect
9898
k8s.io/metrics v0.34.1 // indirect
99-
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d // indirect
99+
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect
100100
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
101101
sigs.k8s.io/kustomize/api v0.20.1 // indirect
102102
sigs.k8s.io/kustomize/kustomize/v5 v5.7.1 // indirect

examples/kubectl/go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
228228
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
229229
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
230230
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
231-
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
232-
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
231+
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
232+
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
233233
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
234234
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
235235
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -260,8 +260,8 @@ k8s.io/kubectl v0.34.1 h1:1qP1oqT5Xc93K+H8J7ecpBjaz511gan89KO9Vbsh/OI=
260260
k8s.io/kubectl v0.34.1/go.mod h1:JRYlhJpGPyk3dEmJ+BuBiOB9/dAvnrALJEiY/C5qa6A=
261261
k8s.io/metrics v0.34.1 h1:374Rexmp1xxgRt64Bi0TsjAM8cA/Y8skwCoPdjtIslE=
262262
k8s.io/metrics v0.34.1/go.mod h1:Drf5kPfk2NJrlpcNdSiAAHn/7Y9KqxpRNagByM7Ei80=
263-
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0=
264-
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
263+
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck=
264+
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
265265
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
266266
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
267267
sigs.k8s.io/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I=

examples/kubectl/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func rootCmd() *cobra.Command {
1919
command.AddCommand(ophis.Command(&ophis.Config{
2020
Selectors: []ophis.Selector{
2121
{
22-
CmdSelector: ophis.AllowCmd(
22+
CmdSelector: ophis.AllowCmds(
2323
"kubectl get",
2424
"kubectl describe",
2525
"kubectl logs",
@@ -28,7 +28,7 @@ func rootCmd() *cobra.Command {
2828
"kubectl explain",
2929
),
3030

31-
InheritedFlagSelector: ophis.AllowFlag(
31+
InheritedFlagSelector: ophis.AllowFlags(
3232
"namespace",
3333
"context",
3434
"output",

select.go

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -70,50 +70,66 @@ type Selector struct {
7070
PostRun PostRunFunc
7171
}
7272

73-
// ExcludeCmd creates a selector that rejects commands whose path contains any listed phrase.
74-
// Example: ExcludeCmd("kubectl delete", "admin") excludes "kubectl delete" and "cli admin user".
75-
func ExcludeCmd(cmds ...string) CmdSelector {
73+
// AllowCmdsContaining creates a selector that only accepts commands whose path contains a listed phrase.
74+
// Example: AllowCmdsContaining("get", "helm list") includes "kubectl get pods" and "helm list".
75+
func AllowCmdsContaining(substrings ...string) CmdSelector {
7676
return func(cmd *cobra.Command) bool {
77-
for _, phrase := range cmds {
78-
if strings.Contains(cmd.CommandPath(), phrase) {
79-
return false
77+
for _, s := range substrings {
78+
if strings.Contains(cmd.CommandPath(), s) {
79+
return true
8080
}
8181
}
8282

83-
return true
83+
return false
8484
}
8585
}
8686

87-
// AllowCmd creates a selector that only accepts commands whose path contains a listed phrase.
88-
// Example: AllowCmd("get", "helm list") includes "kubectl get pods" and "helm list".
89-
func AllowCmd(cmds ...string) CmdSelector {
87+
// ExcludeCmdsContaining creates a selector that rejects commands whose path contains any listed phrase.
88+
// Example: ExcludeCmdsContaining("kubectl delete", "admin") excludes "kubectl delete" and "cli admin user".
89+
func ExcludeCmdsContaining(substrings ...string) CmdSelector {
9090
return func(cmd *cobra.Command) bool {
91-
for _, phrase := range cmds {
92-
if strings.Contains(cmd.CommandPath(), phrase) {
93-
return true
91+
for _, s := range substrings {
92+
if strings.Contains(cmd.CommandPath(), s) {
93+
return false
9494
}
9595
}
9696

97-
return false
97+
return true
9898
}
9999
}
100100

101-
// ExcludeFlag creates a selector that rejects flags whose name is listed.
102-
// Example: ExcludeFlag("color", "kubeconfig") excludes flags named "color" and "kubeconfig".
103-
func ExcludeFlag(names ...string) FlagSelector {
104-
return func(flag *pflag.Flag) bool {
105-
return !slices.Contains(names, flag.Name)
101+
// AllowCmds creates a selector that only accepts commands whose path is listed.
102+
// Example: AllowCmds("kubectl get", "helm list") includes only those exact commands.
103+
func AllowCmds(cmds ...string) CmdSelector {
104+
return func(cmd *cobra.Command) bool {
105+
return slices.Contains(cmds, cmd.CommandPath())
106+
}
107+
}
108+
109+
// ExcludeCmds creates a selector that rejects commands whose path is listed.
110+
// Example: ExcludeCmds("kubectl delete", "helm uninstall") excludes those exact commands.
111+
func ExcludeCmds(cmds ...string) CmdSelector {
112+
return func(cmd *cobra.Command) bool {
113+
return !slices.Contains(cmds, cmd.CommandPath())
106114
}
107115
}
108116

109-
// AllowFlag creates a selector that only accepts flags whose name is listed.
110-
// Example: AllowFlag("namespace", "output") includes only flags named "namespace" and "output".
111-
func AllowFlag(names ...string) FlagSelector {
117+
// AllowFlags creates a selector that only accepts flags whose name is listed.
118+
// Example: AllowFlags("namespace", "output") includes only flags named "namespace" and "output".
119+
func AllowFlags(names ...string) FlagSelector {
112120
return func(flag *pflag.Flag) bool {
113121
return slices.Contains(names, flag.Name)
114122
}
115123
}
116124

125+
// ExcludeFlags creates a selector that rejects flags whose name is listed.
126+
// Example: ExcludeFlags("color", "kubeconfig") excludes flags named "color" and "kubeconfig".
127+
func ExcludeFlags(names ...string) FlagSelector {
128+
return func(flag *pflag.Flag) bool {
129+
return !slices.Contains(names, flag.Name)
130+
}
131+
}
132+
117133
// NoFlags is a FlagSelector that excludes all flags.
118134
func NoFlags(_ *pflag.Flag) bool {
119135
return false

0 commit comments

Comments
 (0)