Skip to content

Commit 35361bb

Browse files
authored
Rel v0.40.10 (#3206)
* [HOTFIX] Broke plugins ;( * add support for cust cols on alias+ns * rel notes
1 parent 78c7787 commit 35361bb

File tree

9 files changed

+109
-42
lines changed

9 files changed

+109
-42
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ DATE ?= $(shell TZ=UTC date -j -f "%s" ${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:
1111
else
1212
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
1313
endif
14-
VERSION ?= v0.40.9
14+
VERSION ?= v0.40.10
1515
IMG_NAME := derailed/k9s
1616
IMAGE := ${IMG_NAME}:${VERSION}
1717

change_logs/release_v0.40.10.md

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
2+
3+
# Release v0.40.10
4+
5+
## Notes
6+
7+
Thank you to all that contributed with flushing out issues and enhancements for K9s!
8+
I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev
9+
and see if we're happier with some of the fixes!
10+
If you've filed an issue please help me verify and close.
11+
12+
Your support, kindness and awesome suggestions to make K9s better are, as ever, very much noted and appreciated!
13+
Also big thanks to all that have allocated their own time to help others on both slack and on this repo!!
14+
15+
As you may know, K9s is not pimped out by corps with deep pockets, thus if you feel K9s is helping your Kubernetes journey,
16+
please consider joining our [sponsorship program](https://github.com/sponsors/derailed) and/or make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
17+
18+
On Slack? Please join us [K9slackers](https://join.slack.com/t/k9sers/shared_invite/enQtOTA5MDEyNzI5MTU0LWQ1ZGI3MzliYzZhZWEyNzYxYzA3NjE0YTk1YmFmNzViZjIyNzhkZGI0MmJjYzhlNjdlMGJhYzE2ZGU1NjkyNTM)
19+
20+
## Maintenance Release!
21+
22+
Sounds like I did hose plugins after all... With feelings!
23+
24+
* Refactored plugins implementation, hopefully we didn't hose them 😳
25+
* Updated plugins docs
26+
* Apparently when it comes to icons, I've chosen... poorly 🙀
27+
Updated `write` icon 🔓->✍️, hopefully for the better 👀??
28+
29+
## Videos Are In The Can!
30+
31+
Please dial [K9s Channel](https://www.youtube.com/channel/UC897uwPygni4QIjkPCpgjmw) for up coming content...
32+
33+
* [K9s v0.40.0 -Column Blow- Sneak peek](https://youtu.be/iy6RDozAM4A)
34+
* [K9s v0.31.0 Configs+Sneak peek](https://youtu.be/X3444KfjguE)
35+
* [K9s v0.30.0 Sneak peek](https://youtu.be/mVBc1XneRJ4)
36+
* [Vulnerability Scans](https://youtu.be/ULkl0MsaidU)
37+
38+
---
39+
40+
## Resolved Issues
41+
42+
* [#3202](https://github.com/derailed/k9s/issues/3202) 0.40.8 breaks plugins loading
43+
44+
---
45+
46+
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2025 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)

internal/config/json/schemas/plugin-multi.json

+19-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,24 @@
33
"title": "K9s plugin-multi schema",
44
"type": "object",
55
"additionalProperties": {
6-
"$ref": "file://internal/config/json/schemas/plugin.json",
7-
"additionalProperties": false
6+
"properties": {
7+
"shortCut": { "type": "string" },
8+
"override": { "type": "boolean" },
9+
"description": { "type": "string" },
10+
"confirm": { "type": "boolean" },
11+
"dangerous": { "type": "boolean" },
12+
"scopes": {
13+
"type": "array",
14+
"items": { "type": "string" }
15+
},
16+
"command": { "type": "string" },
17+
"background": { "type": "boolean" },
18+
"overwriteOutput": { "type": "boolean" },
19+
"args": {
20+
"type": "array",
21+
"items": { "type": ["string", "number"] }
22+
}
23+
},
24+
"required": ["shortCut", "description", "scopes", "command"]
825
}
926
}

internal/config/json/schemas/plugins.json

+19-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,25 @@
77
"plugins": {
88
"type": "object",
99
"additionalProperties": {
10-
"$ref": "file://internal/config/json/schemas/plugin.json",
11-
"additionalProperties": false
10+
"properties": {
11+
"shortCut": { "type": "string" },
12+
"override": { "type": "boolean" },
13+
"description": { "type": "string" },
14+
"confirm": { "type": "boolean" },
15+
"dangerous": { "type": "boolean" },
16+
"scopes": {
17+
"type": "array",
18+
"items": { "type": "string" }
19+
},
20+
"command": { "type": "string" },
21+
"background": { "type": "boolean" },
22+
"overwriteOutput": { "type": "boolean" },
23+
"args": {
24+
"type": "array",
25+
"items": { "type": ["string", "number"] }
26+
}
27+
},
28+
"required": ["shortCut", "description", "scopes", "command"]
1229
},
1330
"required": []
1431
}

internal/config/json/validator_test.go

+1-7
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func TestValidatePlugins(t *testing.T) {
3535
"toast": {
3636
path: "testdata/plugins/toast.yaml",
3737
schema: json.PluginsSchema,
38-
err: "Additional property shortCuts is not allowed\nscopes is required\nshortCut is required",
38+
err: "scopes is required\nshortCut is required",
3939
},
4040
"cool-snippet": {
4141
path: "testdata/plugins/snippet.yaml",
@@ -52,13 +52,10 @@ func TestValidatePlugins(t *testing.T) {
5252
t.Run(k, func(t *testing.T) {
5353
bb, err := os.ReadFile(u.path)
5454
assert.NoError(t, err)
55-
dir, _ := os.Getwd()
56-
assert.NoError(t, os.Chdir("../../.."))
5755
v := json.NewValidator()
5856
if err := v.Validate(u.schema, bb); err != nil {
5957
assert.Equal(t, u.err, err.Error())
6058
}
61-
assert.NoError(t, os.Chdir(dir))
6259
})
6360
}
6461
}
@@ -80,11 +77,8 @@ func TestValidatePluginDir(t *testing.T) {
8077
bb, err := os.ReadFile(filepath.Join(plugDir, e.Name()))
8178
assert.NoError(t, err)
8279

83-
dir, _ := os.Getwd()
84-
assert.NoError(t, os.Chdir("../../.."))
8580
p := json.NewValidator()
8681
assert.NoError(t, p.Validate(json.PluginsSchema, bb), e.Name())
87-
assert.NoError(t, os.Chdir(dir))
8882
}
8983
}
9084

internal/config/plugin_test.go

+7-27
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44
package config
55

66
import (
7-
"os"
8-
"path"
9-
"strings"
107
"testing"
118

129
"github.com/stretchr/testify/assert"
@@ -79,22 +76,16 @@ func TestPluginLoad(t *testing.T) {
7976
"toast-invalid": {
8077
path: "testdata/plugins/plugins-toast.yaml",
8178
ee: NewPlugins(),
82-
err: "Additional property scoped is not allowed\nscopes is required\nAdditional property plugins is not allowed\ncommand is required\ndescription is required\nscopes is required\nshortCut is required\nAdditional property blah is not allowed\ncommand is required\ndescription is required\nscopes is required\nshortCut is required",
79+
err: "plugin validation failed for testdata/plugins/plugins-toast.yaml: scopes is required\nAdditional property plugins is not allowed\ncommand is required\ndescription is required\nscopes is required\nshortCut is required\ncommand is required\ndescription is required\nscopes is required\nshortCut is required",
8380
},
8481
}
8582

86-
dir, _ := os.Getwd()
87-
assert.NoError(t, os.Chdir("../.."))
88-
defer func() {
89-
assert.NoError(t, os.Chdir(dir))
90-
}()
9183
for k, u := range uu {
9284
t.Run(k, func(t *testing.T) {
9385
p := NewPlugins()
94-
err := p.Load(path.Join(dir, u.path), false)
86+
err := p.Load(u.path, false)
9587
if err != nil {
96-
idx := strings.Index(err.Error(), ":")
97-
assert.Equal(t, u.err, err.Error()[idx+2:])
88+
assert.Equal(t, u.err, err.Error())
9889
}
9990
assert.Equal(t, u.ee, p)
10091
})
@@ -111,15 +102,9 @@ func TestSinglePluginFileLoad(t *testing.T) {
111102
Confirm: true,
112103
}
113104

114-
dir, _ := os.Getwd()
115-
assert.NoError(t, os.Chdir("../.."))
116-
defer func() {
117-
assert.NoError(t, os.Chdir(dir))
118-
}()
119-
120105
p := NewPlugins()
121-
assert.NoError(t, p.load(path.Join(dir, "testdata/plugins/plugins.yaml")))
122-
assert.NoError(t, p.loadDir(path.Join(dir, "/random/dir/not/exist")))
106+
assert.NoError(t, p.load("testdata/plugins/plugins.yaml"))
107+
assert.NoError(t, p.loadDir("/random/dir/not/exist"))
123108

124109
assert.Equal(t, 1, len(p.Plugins))
125110
v, ok := p.Plugins["blah"]
@@ -135,8 +120,8 @@ func TestMultiplePluginFilesLoad(t *testing.T) {
135120
ee Plugins
136121
}{
137122
"empty": {
138-
path: "internal/config/testdata/plugins/plugins.yaml",
139-
dir: "internal/config/testdata/plugins/dir",
123+
path: "testdata/plugins/plugins.yaml",
124+
dir: "testdata/plugins/dir",
140125
ee: Plugins{
141126
Plugins: plugins{
142127
"blah": {
@@ -181,11 +166,6 @@ func TestMultiplePluginFilesLoad(t *testing.T) {
181166
},
182167
}
183168

184-
dir, _ := os.Getwd()
185-
assert.NoError(t, os.Chdir("../.."))
186-
defer func() {
187-
assert.NoError(t, os.Chdir(dir))
188-
}()
189169
for k, u := range uu {
190170
t.Run(k, func(t *testing.T) {
191171
p := NewPlugins()

internal/config/views.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ func (v *CustomView) getVS(gvr, ns string) *ViewSetting {
185185
})
186186
slices.Reverse(kk)
187187
for _, key := range kk {
188-
if !strings.HasPrefix(key, gvr) {
188+
if !strings.HasPrefix(key, gvr) && !strings.HasPrefix(gvr, key) {
189189
continue
190190
}
191191

@@ -203,10 +203,23 @@ func (v *CustomView) getVS(gvr, ns string) *ViewSetting {
203203
vs := v.Views[key]
204204
return &vs
205205
}
206+
case strings.HasPrefix(k, key):
207+
kk := strings.Fields(k)
208+
if len(kk) == 2 {
209+
if v, ok := v.Views[kk[0]+"@"+kk[1]]; ok {
210+
return &v
211+
}
212+
if key == kk[0] {
213+
vs := v.Views[key]
214+
return &vs
215+
}
216+
}
217+
fallthrough
206218
case key == k:
207219
vs := v.Views[key]
208220
return &vs
209221
}
222+
210223
}
211224

212225
return nil

internal/render/cust_cols.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ func hydrate(o runtime.Object, cc ColumnSpecs, parsers []*jsonpath.JSONPath, rh
151151
Header: cc[idx].Header,
152152
Value: NAValue,
153153
}
154-
slog.Warn("Unable to find column %s", slogs.Name, cc[idx].Header.Name)
154+
slog.Warn("Unable to find custom column", slogs.Name, cc[idx].Header.Name)
155155
continue
156156
}
157157
var v string

snap/snapcraft.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: k9s
22
base: core22
3-
version: 'v0.40.9'
3+
version: 'v0.40.10'
44
summary: K9s is a CLI to view and manage your Kubernetes clusters.
55
description: |
66
K9s is a CLI to view and manage your Kubernetes clusters. By leveraging a terminal UI, you can easily traverse Kubernetes resources and view the state of your clusters in a single powerful session.

0 commit comments

Comments
 (0)