Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package kubectl-view-secret for 
openSUSE:Factory checked in at 2026-11-10 14:41:55
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/kubectl-view-secret (Old)
 and      /work/SRC/openSUSE:Factory/.kubectl-view-secret.new.3759290 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "kubectl-view-secret"

Tue Nov 10 14:41:55 2026 rev:9 rq:1396443 version:0.17.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/kubectl-view-secret/kubectl-view-secret.changes  
2026-04-23 17:13:37.414925437 +0200
+++ 
/work/SRC/openSUSE:Factory/.kubectl-view-secret.new.3759290/kubectl-view-secret.changes
     2026-11-10 14:42:49.028590044 +0100
@@ -1,0 +2,16 @@
+Tue Nov 10 06:10:34 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- Update to version 0.17.0:
+  * Update changelog for v0.17.0 release
+  * Document that text output is not shell safe (#101)
+  * Replace goccy/go-json with encoding/json/v2 (#100)
+  * Limit the decompressed size of Helm releases (#99)
+  * List secret names without downloading their data (#98)
+  * Reject invalid --output values (#97)
+  * Fail on an unknown key for secrets with a single key (#96)
+  * Forward context, kubeconfig and impersonation flags in
+    completion (#95)
+  * Support multiple `--as-group` flags (#94)
+  * Remove unused wrapper and duplicate helpers (#93)
+
+-------------------------------------------------------------------

Old:
----
  kubectl-view-secret-0.16.0.obscpio

New:
----
  kubectl-view-secret-0.17.0.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ kubectl-view-secret.spec ++++++
--- /var/tmp/diff_new_pack.6TVpC4/_old  2026-11-10 14:42:50.647657870 +0100
+++ /var/tmp/diff_new_pack.6TVpC4/_new  2026-11-10 14:42:50.648657912 +0100
@@ -21,14 +21,14 @@
 %define executable_name kubectl-view_secret
 
 Name:           kubectl-view-secret
-Version:        0.16.0
+Version:        0.17.0
 Release:        0
 Summary:        Kubernetes CLI plugin to decode Kubernetes secrets
 License:        MIT
 URL:            https://github.com/elsesiy/kubectl-view-secret
 Source:         %{name}-%{version}.tar.gz
 Source1:        vendor.tar.gz
-BuildRequires:  go1.26 >= 1.26.1
+BuildRequires:  go1.27 >= 1.27.3
 
 %description
 This plugin allows for easy secret decoding. Useful if you want to see what's

++++++ _service ++++++
--- /var/tmp/diff_new_pack.6TVpC4/_old  2026-11-10 14:42:50.682659336 +0100
+++ /var/tmp/diff_new_pack.6TVpC4/_new  2026-11-10 14:42:50.685659462 +0100
@@ -1,9 +1,9 @@
 <services>
   <service name="obs_scm" mode="manual">
-    <param name="url">https://github.com/elsesiy/kubectl-view-secret</param>
+    <param 
name="url">https://github.com/elsesiy/kubectl-view-secret.git</param>
     <param name="scm">git</param>
     <param name="exclude">.git</param>
-    <param name="revision">v0.16.0</param>
+    <param name="revision">refs/tags/v0.17.0</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="changesgenerate">enable</param>
     <param name="versionrewrite-pattern">v(.*)</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.6TVpC4/_old  2026-11-10 14:42:50.708660425 +0100
+++ /var/tmp/diff_new_pack.6TVpC4/_new  2026-11-10 14:42:50.710660509 +0100
@@ -1,6 +1,8 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://github.com/elsesiy/kubectl-view-secret</param>
-              <param 
name="changesrevision">2e33057ad8e86bb12251fd363816491151c16818</param></service></servicedata>
+              <param 
name="changesrevision">2e33057ad8e86bb12251fd363816491151c16818</param></service><service
 name="tar_scm">
+                <param 
name="url">https://github.com/elsesiy/kubectl-view-secret.git</param>
+              <param 
name="changesrevision">3ca5d45233f382008389af09555b332843867c73</param></service></servicedata>
 (No newline at EOF)
 

++++++ kubectl-view-secret-0.16.0.obscpio -> kubectl-view-secret-0.17.0.obscpio 
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-view-secret-0.16.0/CHANGELOG.md 
new/kubectl-view-secret-0.17.0/CHANGELOG.md
--- old/kubectl-view-secret-0.16.0/CHANGELOG.md 2026-04-23 07:19:16.000000000 
+0200
+++ new/kubectl-view-secret-0.17.0/CHANGELOG.md 2026-11-09 07:33:51.000000000 
+0100
@@ -1,5 +1,43 @@
 # Changelog
 
+## [v0.17.0](https://github.com/elsesiy/kubectl-view-secret/tree/v0.17.0) 
(2026-11-09)
+
+[Full 
Changelog](https://github.com/elsesiy/kubectl-view-secret/compare/v0.16.0...v0.17.0)
+
+**Implemented enhancements:**
+
+- Replace goccy/go-json with encoding/json/v2 
[\#90](https://github.com/elsesiy/kubectl-view-secret/issues/90)
+- Selecting a secret interactively downloads the data of all secrets 
[\#88](https://github.com/elsesiy/kubectl-view-secret/issues/88)
+
+**Fixed bugs:**
+
+- Helm release decoding has no size limit 
[\#89](https://github.com/elsesiy/kubectl-view-secret/issues/89)
+- Invalid --output values are silently treated as text 
[\#87](https://github.com/elsesiy/kubectl-view-secret/issues/87)
+- Unknown key is ignored for secrets with a single key 
[\#86](https://github.com/elsesiy/kubectl-view-secret/issues/86)
+- Shell completion ignores --context, --kubeconfig and impersonation flags 
[\#85](https://github.com/elsesiy/kubectl-view-secret/issues/85)
+- `--as-group` cannot impersonate more than one group 
[\#84](https://github.com/elsesiy/kubectl-view-secret/issues/84)
+
+**Closed issues:**
+
+- Document that text output is not shell safe 
[\#91](https://github.com/elsesiy/kubectl-view-secret/issues/91)
+- Remove unused wrapper and duplicate helpers 
[\#83](https://github.com/elsesiy/kubectl-view-secret/issues/83)
+- Command tests do not compare the expected output 
[\#82](https://github.com/elsesiy/kubectl-view-secret/issues/82)
+- Update to go1.27 
[\#80](https://github.com/elsesiy/kubectl-view-secret/issues/80)
+
+**Merged pull requests:**
+
+- Document that text output is not shell safe 
[\#101](https://github.com/elsesiy/kubectl-view-secret/pull/101) 
([elsesiy](https://github.com/elsesiy))
+- Replace goccy/go-json with encoding/json/v2 
[\#100](https://github.com/elsesiy/kubectl-view-secret/pull/100) 
([elsesiy](https://github.com/elsesiy))
+- Limit the decompressed size of Helm releases 
[\#99](https://github.com/elsesiy/kubectl-view-secret/pull/99) 
([elsesiy](https://github.com/elsesiy))
+- List secret names without downloading their data 
[\#98](https://github.com/elsesiy/kubectl-view-secret/pull/98) 
([elsesiy](https://github.com/elsesiy))
+- Reject invalid --output values 
[\#97](https://github.com/elsesiy/kubectl-view-secret/pull/97) 
([elsesiy](https://github.com/elsesiy))
+- Fail on an unknown key for secrets with a single key 
[\#96](https://github.com/elsesiy/kubectl-view-secret/pull/96) 
([elsesiy](https://github.com/elsesiy))
+- Forward context, kubeconfig and impersonation flags in completion 
[\#95](https://github.com/elsesiy/kubectl-view-secret/pull/95) 
([elsesiy](https://github.com/elsesiy))
+- Support multiple `--as-group` flags 
[\#94](https://github.com/elsesiy/kubectl-view-secret/pull/94) 
([elsesiy](https://github.com/elsesiy))
+- Remove unused wrapper and duplicate helpers 
[\#93](https://github.com/elsesiy/kubectl-view-secret/pull/93) 
([elsesiy](https://github.com/elsesiy))
+- Assert command output in tests 
[\#92](https://github.com/elsesiy/kubectl-view-secret/pull/92) 
([elsesiy](https://github.com/elsesiy))
+- Bump to go1.27 & update dependencies 
[\#81](https://github.com/elsesiy/kubectl-view-secret/pull/81) 
([elsesiy](https://github.com/elsesiy))
+
 ## [v0.16.0](https://github.com/elsesiy/kubectl-view-secret/tree/v0.16.0) 
(2026-04-23)
 
 [Full 
Changelog](https://github.com/elsesiy/kubectl-view-secret/compare/v0.15.1...v0.16.0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-view-secret-0.16.0/README.md 
new/kubectl-view-secret-0.17.0/README.md
--- old/kubectl-view-secret-0.16.0/README.md    2026-04-23 07:19:16.000000000 
+0200
+++ new/kubectl-view-secret-0.17.0/README.md    2026-11-09 07:33:51.000000000 
+0100
@@ -43,6 +43,9 @@
     # output in YAML format
     kubectl view-secret <secret> -o yaml
 
+    # impersonate a user and groups (repeat --as-group for each group)
+    kubectl view-secret <secret> --as <user> --as-group <group1> --as-group 
<group2>
+
 ## Bash Completion
 
 This plugin supports bash completion for kubectl versions 1.26 and later. To 
enable completion:
@@ -85,10 +88,12 @@
 - Secret keys: `kubectl view-secret <secret> <TAB>`
 - Namespaces: `kubectl view-secret <secret> -n <TAB>`
 
+Completion honors `-n`, `-c`, `-k`, `--as` and `--as-group`.
+
 ## Features
 
 ### Output Formats
-- **Text**: Default human-readable format
+- **Text**: Default human-readable format. Values are printed as `key='value'` 
without escaping, so do not feed it to a shell. Use JSON or YAML for scripts.
 - **JSON**: Structured output for automation and scripting
 - **YAML**: Alternative structured format
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-view-secret-0.16.0/go.mod 
new/kubectl-view-secret-0.17.0/go.mod
--- old/kubectl-view-secret-0.16.0/go.mod       2026-04-23 07:19:16.000000000 
+0200
+++ new/kubectl-view-secret-0.17.0/go.mod       2026-11-09 07:33:51.000000000 
+0100
@@ -1,45 +1,45 @@
 module github.com/elsesiy/kubectl-view-secret
 
-go 1.26.1
+go 1.27.3
 
 require (
-       github.com/charmbracelet/bubbletea v1.3.10
-       github.com/charmbracelet/huh v1.0.0
-       github.com/goccy/go-json v0.10.6
+       charm.land/bubbletea/v2 v2.1.1
+       charm.land/huh/v2 v2.0.3
        github.com/spf13/cobra v1.10.2
-       github.com/stretchr/testify v1.11.1
+       github.com/stretchr/testify v1.12.1
        gopkg.in/yaml.v3 v3.0.1
 )
 
 require (
+       charm.land/bubbles/v2 v2.2.1 // indirect
+       charm.land/lipgloss/v2 v2.0.6 // indirect
        github.com/atotto/clipboard v0.1.4 // indirect
-       github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
        github.com/catppuccin/go v0.3.0 // indirect
-       github.com/charmbracelet/bubbles v1.0.0 // indirect
        github.com/charmbracelet/colorprofile v0.4.3 // indirect
-       github.com/charmbracelet/lipgloss v1.1.0 // indirect
-       github.com/charmbracelet/x/ansi v0.11.7 // indirect
-       github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
+       github.com/charmbracelet/ultraviolet v0.0.0-20261023060817-a45b7c86333f 
// indirect
+       github.com/charmbracelet/x/ansi v0.11.9 // indirect
+       github.com/charmbracelet/x/exp/golden v0.1.0 // indirect
+       github.com/charmbracelet/x/exp/ordered v0.1.0 // indirect
        github.com/charmbracelet/x/exp/strings v0.1.0 // indirect
        github.com/charmbracelet/x/term v0.2.2 // indirect
+       github.com/charmbracelet/x/termios v0.1.1 // indirect
+       github.com/charmbracelet/x/windows v0.2.2 // indirect
+       github.com/charmbracelet/x/xpty v0.1.4 // indirect
        github.com/clipperhouse/displaywidth v0.11.0 // indirect
        github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
-       github.com/davecgh/go-spew v1.1.1 // indirect
-       github.com/dustin/go-humanize v1.0.1 // indirect
-       github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // 
indirect
+       github.com/dustin/go-humanize v1.1.0 // indirect
        github.com/inconshreveable/mousetrap v1.1.0 // indirect
-       github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
-       github.com/mattn/go-isatty v0.0.21 // indirect
-       github.com/mattn/go-localereader v0.0.1 // indirect
-       github.com/mattn/go-runewidth v0.0.23 // indirect
+       github.com/kr/pretty v0.3.1 // indirect
+       github.com/lucasb-eyer/go-colorful v1.4.1 // indirect
+       github.com/mattn/go-runewidth v0.0.31 // indirect
        github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
-       github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
        github.com/muesli/cancelreader v0.2.2 // indirect
-       github.com/muesli/termenv v0.16.0 // indirect
-       github.com/pmezard/go-difflib v1.0.0 // indirect
        github.com/rivo/uniseg v0.4.7 // indirect
-       github.com/spf13/pflag v1.0.10 // indirect
-       github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
-       golang.org/x/sys v0.43.0 // indirect
-       golang.org/x/text v0.36.0 // indirect
+       github.com/rogpeppe/go-internal v1.16.0 // indirect
+       github.com/spf13/pflag v1.1.0 // indirect
+       github.com/xo/terminfo v1.3.1 // indirect
+       go.yaml.in/yaml/v3 v3.0.6 // indirect
+       golang.org/x/sync v0.24.0 // indirect
+       golang.org/x/sys v0.49.0 // indirect
+       gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-view-secret-0.16.0/go.sum 
new/kubectl-view-secret-0.17.0/go.sum
--- old/kubectl-view-secret-0.16.0/go.sum       2026-04-23 07:19:16.000000000 
+0200
+++ new/kubectl-view-secret-0.17.0/go.sum       2026-11-09 07:33:51.000000000 
+0100
@@ -1,97 +1,93 @@
+charm.land/bubbles/v2 v2.2.1 h1:Fq1+qm5hV6GkvzLQDhCBpXXE5tLgvh1PRriCLwSvIQU=
+charm.land/bubbles/v2 v2.2.1/go.mod 
h1:wdMgn+sje1KNXdwFizIWjbf328fIUBxqEmJ/vYPo8yc=
+charm.land/bubbletea/v2 v2.1.1 h1:S1S9l37pCZGrj1JexBcr9x4Nr3MVeM42InqCYM/tDpU=
+charm.land/bubbletea/v2 v2.1.1/go.mod 
h1:xWHeyxIX9/wiFU2kgPW04ojOZyTzrWf+8lGjUCaftiU=
+charm.land/huh/v2 v2.0.3 h1:2cJsMqEPwSywGHvdlKsJyQKPtSJLVnFKyFbsYZTlLkU=
+charm.land/huh/v2 v2.0.3/go.mod h1:93eEveeeqn47MwiC3tf+2atZ2l7Is88rAtmZNZ8x9Wc=
+charm.land/lipgloss/v2 v2.0.6 h1:EaGKeuA8FvF+v2BT5VmZd2LoYLaMZJXA5n34th8nCIQ=
+charm.land/lipgloss/v2 v2.0.6/go.mod 
h1:ipDDJNSGa1hlwDtSfW1s2/xR8Vdhbut4PXh2zEKZd0Q=
 github.com/MakeNowJust/heredoc v1.0.0 
h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
 github.com/MakeNowJust/heredoc v1.0.0/go.mod 
h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
 github.com/atotto/clipboard v0.1.4 
h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
 github.com/atotto/clipboard v0.1.4/go.mod 
h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
-github.com/aymanbagabas/go-osc52/v2 v2.0.1 
h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
-github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod 
h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
-github.com/aymanbagabas/go-udiff v0.3.1 
h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY=
-github.com/aymanbagabas/go-udiff v0.3.1/go.mod 
h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=
+github.com/aymanbagabas/go-udiff v0.4.1 
h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
+github.com/aymanbagabas/go-udiff v0.4.1/go.mod 
h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
 github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=
 github.com/catppuccin/go v0.3.0/go.mod 
h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
-github.com/charmbracelet/bubbles v1.0.0 
h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc=
-github.com/charmbracelet/bubbles v1.0.0/go.mod 
h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E=
-github.com/charmbracelet/bubbletea v1.3.10 
h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
-github.com/charmbracelet/bubbletea v1.3.10/go.mod 
h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
 github.com/charmbracelet/colorprofile v0.4.3 
h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
 github.com/charmbracelet/colorprofile v0.4.3/go.mod 
h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
-github.com/charmbracelet/huh v1.0.0 
h1:wOnedH8G4qzJbmhftTqrpppyqHakl/zbbNdXIWJyIxw=
-github.com/charmbracelet/huh v1.0.0/go.mod 
h1:5YVc+SlZ1IhQALxRPpkGwwEKftN/+OlJlnJYlDRFqN4=
-github.com/charmbracelet/lipgloss v1.1.0 
h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
-github.com/charmbracelet/lipgloss v1.1.0/go.mod 
h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
-github.com/charmbracelet/x/ansi v0.11.7 
h1:kzv1kJvjg2S3r9KHo8hDdHFQLEqn4RBCb39dAYC84jI=
-github.com/charmbracelet/x/ansi v0.11.7/go.mod 
h1:9qGpnAVYz+8ACONkZBUWPtL7lulP9No6p1epAihUZwQ=
-github.com/charmbracelet/x/cellbuf v0.0.15 
h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI=
-github.com/charmbracelet/x/cellbuf v0.0.15/go.mod 
h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q=
-github.com/charmbracelet/x/conpty v0.1.0 
h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U=
-github.com/charmbracelet/x/conpty v0.1.0/go.mod 
h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ=
-github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 
h1:JSt3B+U9iqk37QUU2Rvb6DSBYRLtWqFqfxf8l5hOZUA=
-github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86/go.mod 
h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=
-github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 
h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
-github.com/charmbracelet/x/exp/golden 
v0.0.0-20241011142426-46044092ad91/go.mod 
h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
+github.com/charmbracelet/ultraviolet v0.0.0-20261023060817-a45b7c86333f 
h1:D2JpB65eehsnkEwEG401dMBSNYtTDgvKXZZSl7ZCxWI=
+github.com/charmbracelet/ultraviolet v0.0.0-20261023060817-a45b7c86333f/go.mod 
h1:aJDVk+WOVM1K04rdc/71PMPoUGfjxjhvXfP3Pjz1Zio=
+github.com/charmbracelet/x/ansi v0.11.9 
h1:kIrqLBxtTVbLyyHO2Evoi93EjQo1mylWmpNEsfPxXAM=
+github.com/charmbracelet/x/ansi v0.11.9/go.mod 
h1:DG/hpOnaI2QJ/KMMqLL2h0KVhluuJLazf8973P+Vy5w=
+github.com/charmbracelet/x/conpty v0.2.0 
h1:eKtA2hm34qNfgJCDp/M6Dc0gLy7e07YEK4qAdNGOvVY=
+github.com/charmbracelet/x/conpty v0.2.0/go.mod 
h1:fexgUnVrZgw8scD49f6VSi0Ggj9GWYIrpedRthAwW/8=
+github.com/charmbracelet/x/exp/golden v0.1.0 
h1:qgUxGaHX4I8Fg077vD+54+qFmDWrgO19lTCQLblHWko=
+github.com/charmbracelet/x/exp/golden v0.1.0/go.mod 
h1:6fMpcW6iwN/kX+xJ52eqVWsDiBTe0UJD24JLoHFe+P0=
+github.com/charmbracelet/x/exp/ordered v0.1.0 
h1:55/qLwjIh0gL0Vni+QAWk7T/qRVP6sBf+2agPBgnOFE=
+github.com/charmbracelet/x/exp/ordered v0.1.0/go.mod 
h1:5UHwmG+is5THxMyCJHNPCn2/ecI07aKNrW+LcResjJ8=
 github.com/charmbracelet/x/exp/strings v0.1.0 
h1:i69S2XI7uG1u4NLGeJPSYU++Nmjvpo9nwd6aoEm7gkA=
 github.com/charmbracelet/x/exp/strings v0.1.0/go.mod 
h1:/ehtMPNh9K4odGFkqYJKpIYyePhdp1hLBRvyY4bWkH8=
 github.com/charmbracelet/x/term v0.2.2 
h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
 github.com/charmbracelet/x/term v0.2.2/go.mod 
h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
 github.com/charmbracelet/x/termios v0.1.1 
h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
 github.com/charmbracelet/x/termios v0.1.1/go.mod 
h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
-github.com/charmbracelet/x/xpty v0.1.2 
h1:Pqmu4TEJ8KeA9uSkISKMU3f+C1F6OGBn8ABuGlqCbtI=
-github.com/charmbracelet/x/xpty v0.1.2/go.mod 
h1:XK2Z0id5rtLWcpeNiMYBccNNBrP2IJnzHI0Lq13Xzq4=
+github.com/charmbracelet/x/windows v0.2.2 
h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM=
+github.com/charmbracelet/x/windows v0.2.2/go.mod 
h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k=
+github.com/charmbracelet/x/xpty v0.1.4 
h1:4jaW7u+8AHQMxesiVc+zUMsspu7GyDwtJO+gy/tFtW4=
+github.com/charmbracelet/x/xpty v0.1.4/go.mod 
h1:7t8P7BpPiolHJ1pLzz7/4ujDbD+sUxI9yA3CBOLOIcU=
 github.com/clipperhouse/displaywidth v0.11.0 
h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
 github.com/clipperhouse/displaywidth v0.11.0/go.mod 
h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
 github.com/clipperhouse/uax29/v2 v2.7.0 
h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
 github.com/clipperhouse/uax29/v2 v2.7.0/go.mod 
h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
 github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod 
h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
+github.com/creack/pty v1.1.9/go.mod 
h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
 github.com/creack/pty v1.1.24/go.mod 
h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
-github.com/davecgh/go-spew v1.1.1 
h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dustin/go-humanize v1.0.1 
h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
-github.com/dustin/go-humanize v1.0.1/go.mod 
h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
-github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f 
h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
-github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod 
h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
-github.com/goccy/go-json v0.10.6 
h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
-github.com/goccy/go-json v0.10.6/go.mod 
h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+github.com/dustin/go-humanize v1.1.0 
h1:dbKTrvD0klcbBV/h4AWJdMuZogJACoMlvWIWZ5b2xWg=
+github.com/dustin/go-humanize v1.1.0/go.mod 
h1:hc1CvRkJMsgxqjmjMQF3QNRAZBwY8AXBAzKYoSX9sFI=
 github.com/inconshreveable/mousetrap v1.1.0 
h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod 
h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/lucasb-eyer/go-colorful v1.4.0 
h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4=
-github.com/lucasb-eyer/go-colorful v1.4.0/go.mod 
h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
-github.com/mattn/go-isatty v0.0.21 
h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs=
-github.com/mattn/go-isatty v0.0.21/go.mod 
h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
-github.com/mattn/go-localereader v0.0.1 
h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
-github.com/mattn/go-localereader v0.0.1/go.mod 
h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
-github.com/mattn/go-runewidth v0.0.23 
h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
-github.com/mattn/go-runewidth v0.0.23/go.mod 
h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
+github.com/kr/pretty v0.2.1/go.mod 
h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod 
h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod 
h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod 
h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/lucasb-eyer/go-colorful v1.4.1 
h1:1EO+WB73+EH8EVbzlrG3KLAfEypQWVHIBqlTf+2hNss=
+github.com/lucasb-eyer/go-colorful v1.4.1/go.mod 
h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
+github.com/mattn/go-runewidth v0.0.31 
h1:1oCV4ReTlosDBc6rYRw5cL8S/4y8iv5oRl4SJ5IBgwo=
+github.com/mattn/go-runewidth v0.0.31/go.mod 
h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8=
 github.com/mitchellh/hashstructure/v2 v2.0.2 
h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
 github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod 
h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
-github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 
h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
-github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod 
h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
 github.com/muesli/cancelreader v0.2.2 
h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
 github.com/muesli/cancelreader v0.2.2/go.mod 
h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
-github.com/muesli/termenv v0.16.0 
h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
-github.com/muesli/termenv v0.16.0/go.mod 
h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
-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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod 
h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
 github.com/rivo/uniseg v0.4.7/go.mod 
h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/rogpeppe/go-internal v1.9.0/go.mod 
h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
+github.com/rogpeppe/go-internal v1.16.0 
h1:O9DK+vNMDVGLr2BeZqmpLeMjiMNkuXfcqntWbZV6S5g=
+github.com/rogpeppe/go-internal v1.16.0/go.mod 
h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod 
h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
 github.com/spf13/cobra v1.10.2/go.mod 
h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
 github.com/spf13/pflag v1.0.9/go.mod 
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
-github.com/spf13/pflag v1.0.10/go.mod 
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/stretchr/testify v1.11.1 
h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
-github.com/stretchr/testify v1.11.1/go.mod 
h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e 
h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
-github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod 
h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
+github.com/spf13/pflag v1.1.0 h1:6W6MbVUrnMb8PEhjkqwsVLY7ryuzRfFHF4mQd7L94ag=
+github.com/spf13/pflag v1.1.0/go.mod 
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stretchr/testify v1.12.1 
h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
+github.com/stretchr/testify v1.12.1/go.mod 
h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
+github.com/xo/terminfo v1.3.1 h1:4ZwPCuu4PEFw0PkNp3Hv/x3gqvgLCzYhurszsj101cM=
+github.com/xo/terminfo v1.3.1/go.mod 
h1:lGzkSo8Fe7IRh/w+Gqz7n5mDog4FVXCj3gy/DYTBqio=
 go.yaml.in/yaml/v3 v3.0.4/go.mod 
h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d 
h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod 
h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
-golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
-golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
-golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
-golang.org/x/text v0.36.0/go.mod 
h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 
h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+go.yaml.in/yaml/v3 v3.0.6 h1:zcDfvjXEqTQmpjniUBLc91xYJMB2c4QOFxEF/LCEckM=
+go.yaml.in/yaml/v3 v3.0.6/go.mod 
h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
+golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
+golang.org/x/sync v0.24.0/go.mod 
h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
+golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA=
+golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c 
h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod 
h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-view-secret-0.16.0/pkg/cmd/completion.go 
new/kubectl-view-secret-0.17.0/pkg/cmd/completion.go
--- old/kubectl-view-secret-0.16.0/pkg/cmd/completion.go        2026-04-23 
07:19:16.000000000 +0200
+++ new/kubectl-view-secret-0.17.0/pkg/cmd/completion.go        2026-11-09 
07:33:51.000000000 +0100
@@ -1,56 +1,23 @@
 package cmd
 
 import (
-       "bytes"
-       "os/exec"
-       "sort"
+       "encoding/json/v2"
+       "maps"
+       "slices"
 
-       "github.com/goccy/go-json"
        "github.com/spf13/cobra"
 )
 
 // getNamespaces returns a list of namespaces for shell completion
 func getNamespaces(cmd *cobra.Command, _ []string, _ string) ([]string, 
cobra.ShellCompDirective) {
-       var res bytes.Buffer
-
-       kubectlCmd := exec.Command("kubectl", "get", "namespaces", "-o", 
"jsonpath={.items[*].metadata.name}")
-       kubectlCmd.Stdout = &res
-       if err := kubectlCmd.Run(); err != nil {
-               return nil, cobra.ShellCompDirectiveNoFileComp
-       }
-
-       namespaces := []string{}
-       for ns := range bytes.SplitSeq(res.Bytes(), []byte(" ")) {
-               if len(ns) > 0 {
-                       namespaces = append(namespaces, string(ns))
-               }
-       }
+       namespaces, _ := kubectlNames("namespaces", kubectlFlags(cmd)...)
 
        return namespaces, cobra.ShellCompDirectiveNoFileComp
 }
 
 // getSecrets returns a list of secrets for shell completion
 func getSecrets(cmd *cobra.Command, _ []string, _ string) ([]string, 
cobra.ShellCompDirective) {
-       var res bytes.Buffer
-
-       ns, _ := cmd.Flags().GetString("namespace")
-       kubectlArgs := []string{"get", "secrets", "-o", 
"jsonpath={.items[*].metadata.name}"}
-       if ns != "" {
-               kubectlArgs = append(kubectlArgs, "-n", ns)
-       }
-
-       kubectlCmd := exec.Command("kubectl", kubectlArgs...)
-       kubectlCmd.Stdout = &res
-       if err := kubectlCmd.Run(); err != nil {
-               return nil, cobra.ShellCompDirectiveNoFileComp
-       }
-
-       secrets := []string{}
-       for secret := range bytes.SplitSeq(res.Bytes(), []byte(" ")) {
-               if len(secret) > 0 {
-                       secrets = append(secrets, string(secret))
-               }
-       }
+       secrets, _ := kubectlNames("secrets", kubectlFlags(cmd)...)
 
        return secrets, cobra.ShellCompDirectiveNoFileComp
 }
@@ -61,32 +28,16 @@
                return nil, cobra.ShellCompDirectiveNoFileComp
        }
 
-       secretName := args[0]
-       var res bytes.Buffer
-
-       ns, _ := cmd.Flags().GetString("namespace")
-       kubectlArgs := []string{"get", "secret", secretName, "-o", 
"jsonpath={.data}"}
-       if ns != "" {
-               kubectlArgs = append(kubectlArgs, "-n", ns)
-       }
-
-       kubectlCmd := exec.Command("kubectl", kubectlArgs...)
-       kubectlCmd.Stdout = &res
-       if err := kubectlCmd.Run(); err != nil {
+       out, err := runKubectl(append([]string{"get", "secret", args[0], "-o", 
"jsonpath={.data}"}, kubectlFlags(cmd)...)...)
+       if err != nil {
                return nil, cobra.ShellCompDirectiveNoFileComp
        }
 
        // Parse the JSON output to extract keys
        var data map[string]string
-       if err := json.Unmarshal(res.Bytes(), &data); err != nil {
+       if err := json.Unmarshal(out, &data); err != nil {
                return nil, cobra.ShellCompDirectiveNoFileComp
        }
 
-       keys := make([]string, 0, len(data))
-       for k := range data {
-               keys = append(keys, k)
-       }
-       sort.Strings(keys)
-
-       return keys, cobra.ShellCompDirectiveNoFileComp
+       return slices.Sorted(maps.Keys(data)), 
cobra.ShellCompDirectiveNoFileComp
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kubectl-view-secret-0.16.0/pkg/cmd/completion_test.go 
new/kubectl-view-secret-0.17.0/pkg/cmd/completion_test.go
--- old/kubectl-view-secret-0.16.0/pkg/cmd/completion_test.go   2026-04-23 
07:19:16.000000000 +0200
+++ new/kubectl-view-secret-0.17.0/pkg/cmd/completion_test.go   2026-11-09 
07:33:51.000000000 +0100
@@ -1,56 +1,79 @@
 package cmd
 
 import (
+       "os"
+       "path/filepath"
+       "runtime"
+       "strconv"
+       "strings"
        "testing"
 
-       "github.com/spf13/cobra"
        "github.com/stretchr/testify/assert"
 )
 
-// Test completion function logic without kubectl dependency
-func TestCompletionFunctionLogic(t *testing.T) {
-       t.Run("getNamespaces returns correct directive", func(t *testing.T) {
-               cmd := &cobra.Command{}
-               _, directive := getNamespaces(cmd, nil, "")
-               assert.Equal(t, cobra.ShellCompDirectiveNoFileComp, directive)
-       })
-
-       t.Run("getSecrets returns correct directive", func(t *testing.T) {
-               cmd := &cobra.Command{}
-               _, directive := getSecrets(cmd, nil, "")
-               assert.Equal(t, cobra.ShellCompDirectiveNoFileComp, directive)
-       })
-
-       t.Run("getDataKeys with no args returns nil", func(t *testing.T) {
-               cmd := &cobra.Command{}
-               result, directive := getDataKeys(cmd, []string{}, "")
-               assert.Nil(t, result)
-               assert.Equal(t, cobra.ShellCompDirectiveNoFileComp, directive)
-       })
-
-       t.Run("getDataKeys with args returns correct directive", func(t 
*testing.T) {
-               cmd := &cobra.Command{}
-               _, directive := getDataKeys(cmd, []string{"test-secret"}, "")
-               assert.Equal(t, cobra.ShellCompDirectiveNoFileComp, directive)
-       })
-}
+// fakeKubectl puts a kubectl on PATH that records its arguments, prints 
stdout and exits with code
+func fakeKubectl(t *testing.T, stdout string, code int) (argsFile string) {
+       t.Helper()
+       if runtime.GOOS == "windows" {
+               t.Skip("fake kubectl is a shell script")
+       }
+
+       dir := t.TempDir()
+       argsFile = filepath.Join(dir, "args")
+       script := "#!/bin/sh\nprintf '%s\\n' \"$@\" > '" + argsFile + 
"'\nprintf '%s' '" + stdout + "'\nexit " + strconv.Itoa(code) + "\n"
+       assert.NoError(t, os.WriteFile(filepath.Join(dir, "kubectl"), 
[]byte(script), 0o755))
+       t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))
 
-func TestValidArgsFunctionLogic(t *testing.T) {
-       cmd := NewCmdViewSecret()
+       return argsFile
+}
 
-       t.Run("no args returns secrets directive", func(t *testing.T) {
-               _, directive := cmd.ValidArgsFunction(cmd, []string{}, "")
-               assert.Equal(t, cobra.ShellCompDirectiveNoFileComp, directive)
-       })
-
-       t.Run("one arg returns keys directive", func(t *testing.T) {
-               _, directive := cmd.ValidArgsFunction(cmd, []string{"secret"}, 
"")
-               assert.Equal(t, cobra.ShellCompDirectiveNoFileComp, directive)
-       })
-
-       t.Run("two args returns no completion", func(t *testing.T) {
-               result, directive := cmd.ValidArgsFunction(cmd, 
[]string{"secret", "key"}, "")
-               assert.Nil(t, result)
-               assert.Equal(t, cobra.ShellCompDirectiveNoFileComp, directive)
-       })
+func TestCompletion(t *testing.T) {
+       flags := []string{"-n", "ns", "-c", "ctx", "--as", "bob", "--as-group", 
"g1", "--as-group", "g2"}
+       wantFlags := "--namespace ns --context ctx --as bob --as-group g1 
--as-group g2"
+
+       tests := map[string]struct {
+               args     []string
+               stdout   string
+               exitCode int
+               want     []string
+               wantArgs string
+       }{
+               "secrets": {
+                       args:     nil,
+                       stdout:   "alpha   Opaque   2   5d\nbeta   
helm.sh/release.v1   1   2h\ngamma   Opaque   1   1m\n",
+                       want:     []string{"alpha", "beta", "gamma"},
+                       wantArgs: "get secrets --no-headers " + wantFlags,
+               },
+               "keys of a secret are sorted": {
+                       args:     []string{"mysecret"},
+                       stdout:   `{"b":"x","a":"y"}`,
+                       want:     []string{"a", "b"},
+                       wantArgs: "get secret mysecret -o jsonpath={.data} " + 
wantFlags,
+               },
+               "kubectl failure": {
+                       args:     nil,
+                       exitCode: 1,
+                       want:     nil,
+                       wantArgs: "get secrets --no-headers " + wantFlags,
+               },
+               "no completion after secret and key": {
+                       args: []string{"mysecret", "key"},
+                       want: nil,
+               },
+       }
+
+       for name, tt := range tests {
+               t.Run(name, func(t *testing.T) {
+                       argsFile := fakeKubectl(t, tt.stdout, tt.exitCode)
+
+                       cmd := NewCmdViewSecret()
+                       assert.NoError(t, cmd.ParseFlags(flags))
+
+                       got, _ := cmd.ValidArgsFunction(cmd, tt.args, "")
+                       assert.Equal(t, tt.want, got)
+
+                       recorded, _ := os.ReadFile(argsFile)
+                       assert.Equal(t, tt.wantArgs, 
strings.Join(strings.Fields(string(recorded)), " "))
+               })
+       }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-view-secret-0.16.0/pkg/cmd/decode.go 
new/kubectl-view-secret-0.17.0/pkg/cmd/decode.go
--- old/kubectl-view-secret-0.16.0/pkg/cmd/decode.go    2026-04-23 
07:19:16.000000000 +0200
+++ new/kubectl-view-secret-0.17.0/pkg/cmd/decode.go    2026-11-09 
07:33:51.000000000 +0100
@@ -1,14 +1,18 @@
 package cmd
 
 import (
+       "bytes"
        "compress/gzip"
        "encoding/base64"
-       "encoding/json"
+       "encoding/json/jsontext"
+       "encoding/json/v2"
        "fmt"
        "io"
-       "strings"
 )
 
+// maxHelmReleaseSize caps the decompressed size of a Helm release, so a 
crafted secret can't exhaust memory
+const maxHelmReleaseSize = 64 << 20
+
 // SecretDecoder is an interface for decoding various kubernetes secret 
resources
 type SecretDecoder interface {
        Decode(input string) (string, error)
@@ -62,16 +66,19 @@
                return "", err
        }
 
-       gz, err := gzip.NewReader(strings.NewReader(string(b64dhelm)))
+       gz, err := gzip.NewReader(bytes.NewReader(b64dhelm))
        if err != nil {
                return "", fmt.Errorf("failed to create gzip reader: %w", err)
        }
        defer func() { _ = gz.Close() }()
 
-       data, err := io.ReadAll(gz)
+       data, err := io.ReadAll(io.LimitReader(gz, maxHelmReleaseSize+1))
        if err != nil {
                return "", err
        }
+       if len(data) > maxHelmReleaseSize {
+               return "", fmt.Errorf("helm release is larger than %d bytes 
when decompressed", maxHelmReleaseSize)
+       }
 
        return string(data), nil
 }
@@ -86,7 +93,7 @@
        // Try to pretty-print JSON if it's valid
        var jsonData any
        if json.Unmarshal(b64d, &jsonData) == nil {
-               prettyJSON, err := json.MarshalIndent(jsonData, "", "  ")
+               prettyJSON, err := json.Marshal(jsonData, jsontext.WithIndent(" 
 "), json.Deterministic(true))
                if err == nil {
                        return string(prettyJSON), nil
                }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-view-secret-0.16.0/pkg/cmd/decode_test.go 
new/kubectl-view-secret-0.17.0/pkg/cmd/decode_test.go
--- old/kubectl-view-secret-0.16.0/pkg/cmd/decode_test.go       2026-04-23 
07:19:16.000000000 +0200
+++ new/kubectl-view-secret-0.17.0/pkg/cmd/decode_test.go       2026-11-09 
07:33:51.000000000 +0100
@@ -194,3 +194,19 @@
                })
        }
 }
+
+func TestDecodeHelmSizeLimit(t *testing.T) {
+       // A few KB of gzip that expand beyond the limit, as a crafted release 
secret would
+       var gz bytes.Buffer
+       w := gzip.NewWriter(&gz)
+       zeros := make([]byte, 1<<20)
+       for range maxHelmReleaseSize/len(zeros) + 1 {
+               _, err := w.Write(zeros)
+               assert.NoError(t, err)
+       }
+       assert.NoError(t, w.Close())
+       input := 
base64.StdEncoding.EncodeToString([]byte(base64.StdEncoding.EncodeToString(gz.Bytes())))
+
+       _, err := Secret{Type: Helm}.Decode(input)
+       assert.ErrorContains(t, err, "larger than")
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-view-secret-0.16.0/pkg/cmd/types_test.go 
new/kubectl-view-secret-0.17.0/pkg/cmd/types_test.go
--- old/kubectl-view-secret-0.16.0/pkg/cmd/types_test.go        2026-04-23 
07:19:16.000000000 +0200
+++ new/kubectl-view-secret-0.17.0/pkg/cmd/types_test.go        2026-11-09 
07:33:51.000000000 +0100
@@ -1,9 +1,7 @@
 package cmd
 
 import (
-       "encoding/json"
-       "errors"
-       "reflect"
+       "encoding/json/v2"
        "testing"
 
        "github.com/stretchr/testify/assert"
@@ -59,7 +57,7 @@
        tests := map[string]struct {
                input   string
                want    Secret
-               wantErr error
+               wantErr bool
        }{
                "empty opaque secret": {
                        input: invalidSecretJSON,
@@ -70,7 +68,7 @@
                                },
                                Type: Opaque,
                        },
-                       wantErr: errors.New("invalid character '}' looking for 
beginning of object key string"),
+                       wantErr: true,
                },
                "valid opaque secret": {
                        input: validSecretJSON,
@@ -107,21 +105,13 @@
 
                        var got Secret
                        err := json.Unmarshal([]byte(tt.input), &got)
-                       if err != nil {
-                               if tt.wantErr == nil {
-                                       assert.Fail(t, "unexpected error", err)
-                               } else if err.Error() != tt.wantErr.Error() {
-                                       assert.Equal(t, tt.wantErr, err)
-                               }
-                               return
-                       } else if tt.wantErr != nil {
-                               assert.Fail(t, "expected error, got nil", 
tt.wantErr)
+                       if tt.wantErr {
+                               assert.Error(t, err)
                                return
                        }
 
-                       if !reflect.DeepEqual(got, tt.want) {
-                               t.Fatal(err)
-                       }
+                       assert.NoError(t, err)
+                       assert.Equal(t, tt.want, got)
                })
        }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-view-secret-0.16.0/pkg/cmd/view-secret.go 
new/kubectl-view-secret-0.17.0/pkg/cmd/view-secret.go
--- old/kubectl-view-secret-0.16.0/pkg/cmd/view-secret.go       2026-04-23 
07:19:16.000000000 +0200
+++ new/kubectl-view-secret-0.17.0/pkg/cmd/view-secret.go       2026-11-09 
07:33:51.000000000 +0100
@@ -2,15 +2,18 @@
 
 import (
        "bytes"
+       "encoding/json/jsontext"
+       "encoding/json/v2"
        "errors"
        "fmt"
        "io"
+       "maps"
        "os/exec"
-       "sort"
+       "slices"
+       "strings"
 
-       tea "github.com/charmbracelet/bubbletea"
-       "github.com/charmbracelet/huh"
-       "github.com/goccy/go-json"
+       tea "charm.land/bubbletea/v2"
+       "charm.land/huh/v2"
        "github.com/spf13/cobra"
        "gopkg.in/yaml.v3"
 )
@@ -46,6 +49,9 @@
 
        # output in json (or yaml) instead of text
        %[1]s view-secret <secret> -o/--output json
+
+       # impersonate a user and groups (repeat --as-group for each group)
+       %[1]s view-secret <secret> --as <user> --as-group <group1> --as-group 
<group2>
 `
 
        secretDescription     = "Found %d keys in secret %q. Choose one or 
select 'all' to view."
@@ -55,6 +61,9 @@
        singleKeyDescription  = "Viewing only available key: %[1]s"
 )
 
+// outputFormats lists the values accepted by the --output flag
+var outputFormats = []string{"text", "json", "yaml"}
+
 var (
        // ErrNoSecretFound is thrown when no secret name was provided but we 
didn't find any secrets
        ErrNoSecretFound = errors.New("no secrets found")
@@ -68,16 +77,11 @@
 
 // CommandOpts is the struct holding common properties
 type CommandOpts struct {
-       customContext       string
-       customNamespace     string
-       decodeAll           bool
-       impersonateAs       string
-       impersonateAsGroups string
-       kubeConfig          string
-       outputFormat        string
-       quiet               bool
-       secretKey           string
-       secretName          string
+       decodeAll    bool
+       outputFormat string
+       quiet        bool
+       secretKey    string
+       secretName   string
 }
 
 // NewCmdViewSecret creates the cobra command to be executed
@@ -94,25 +98,26 @@
                SilenceUsage: true,
                Use:          "view-secret [secret-name] [secret-key]",
                RunE: func(c *cobra.Command, args []string) error {
-                       res.ParseArgs(args)
-                       if err := res.Retrieve(c); err != nil {
-                               return err
+                       if !slices.Contains(outputFormats, res.outputFormat) {
+                               return fmt.Errorf("invalid output format %q: 
must be one of %s", res.outputFormat, strings.Join(outputFormats, ", "))
                        }
 
-                       return nil
+                       res.ParseArgs(args)
+
+                       return res.Retrieve(c)
                },
        }
 
        cmd.Flags().
                BoolVarP(&res.decodeAll, "all", "a", res.decodeAll, "if true, 
decodes all secrets without specifying the individual secret keys")
        cmd.Flags().BoolVarP(&res.quiet, "quiet", "q", res.quiet, "if true, 
suppresses info output")
-       cmd.Flags().
-               StringVarP(&res.customNamespace, "namespace", "n", 
res.customNamespace, "override the namespace defined in the current context")
-       cmd.Flags().StringVarP(&res.customContext, "context", "c", 
res.customContext, "override the current context")
-       cmd.Flags().StringVarP(&res.kubeConfig, "kubeconfig", "k", 
res.kubeConfig, "explicitly provide the kubeconfig to use")
-       cmd.Flags().StringVar(&res.impersonateAs, "as", res.impersonateAs, 
"Username to impersonate for the operation. User could be a regular user or a 
service account in a namespace.")
-       cmd.Flags().StringVar(&res.impersonateAsGroups, "as-group", 
res.impersonateAsGroups, "Groups to impersonate for the operation. Multipe 
groups can be specified by comma separated.")
-       cmd.Flags().StringVarP(&res.outputFormat, "output", "o", "text", 
"output format: text, json, yaml")
+       // These flags are only forwarded to kubectl, see kubectlFlags
+       cmd.Flags().StringP("namespace", "n", "", "override the namespace 
defined in the current context")
+       cmd.Flags().StringP("context", "c", "", "override the current context")
+       cmd.Flags().StringP("kubeconfig", "k", "", "explicitly provide the 
kubeconfig to use")
+       cmd.Flags().String("as", "", "Username to impersonate for the 
operation. User could be a regular user or a service account in a namespace.")
+       cmd.Flags().StringArray("as-group", nil, "Group to impersonate for the 
operation. Repeat the flag to specify multiple groups.")
+       cmd.Flags().StringVarP(&res.outputFormat, "output", "o", "text", 
"output format: "+strings.Join(outputFormats, ", "))
 
        // Add shell completion functions
        _ = cmd.RegisterFlagCompletionFunc("namespace", getNamespaces)
@@ -148,65 +153,54 @@
 // for secret selection, and outputs the decoded content in the
 // specified format.
 func (c *CommandOpts) Retrieve(cmd *cobra.Command) error {
-       commandArgs := c.buildKubectlCommand(cmd)
-       output, err := c.executeKubectlCommand(commandArgs)
-       if err != nil {
-               return err
+       flags := kubectlFlags(cmd)
+
+       if c.secretName == "" {
+               if err := c.selectSecret(cmd, flags); err != nil {
+                       return err
+               }
        }
 
-       secret, err := c.parseSecretResponse(output, cmd)
+       output, err := runKubectl(append([]string{"get", "secret", 
c.secretName, "-o", "json"}, flags...)...)
        if err != nil {
                return err
        }
 
-       if c.quiet {
-               return ProcessSecretWithOptions(cmd.OutOrStdout(), io.Discard, 
cmd.InOrStdin(), secret, c.secretKey, c.decodeAll, c.outputFormat)
-       }
-
-       return ProcessSecretWithOptions(cmd.OutOrStdout(), cmd.OutOrStderr(), 
cmd.InOrStdin(), secret, c.secretKey, c.decodeAll, c.outputFormat)
-}
-
-// buildKubectlCommand builds the kubectl command arguments
-func (c *CommandOpts) buildKubectlCommand(cmd *cobra.Command) []string {
-       nsOverride, _ := cmd.Flags().GetString("namespace")
-       ctxOverride, _ := cmd.Flags().GetString("context")
-       kubeConfigOverride, _ := cmd.Flags().GetString("kubeconfig")
-       impersonateOverride, _ := cmd.Flags().GetString("as")
-       impersonateGroupOverride, _ := cmd.Flags().GetString("as-group")
-
-       commandArgs := []string{"get", "secret", "-o", "json"}
-       if c.secretName != "" {
-               commandArgs = []string{"get", "secret", c.secretName, "-o", 
"json"}
-       }
-
-       if nsOverride != "" {
-               commandArgs = append(commandArgs, "-n", nsOverride)
+       var secret Secret
+       if err := json.Unmarshal(output, &secret); err != nil {
+               return fmt.Errorf("failed to parse kubectl output as secret: 
%w", err)
        }
 
-       if ctxOverride != "" {
-               commandArgs = append(commandArgs, "--context", ctxOverride)
+       errWriter := cmd.OutOrStderr()
+       if c.quiet {
+               errWriter = io.Discard
        }
 
-       if kubeConfigOverride != "" {
-               commandArgs = append(commandArgs, "--kubeconfig", 
kubeConfigOverride)
-       }
+       return ProcessSecretWithOptions(cmd.OutOrStdout(), errWriter, 
cmd.InOrStdin(), secret, c.secretKey, c.decodeAll, c.outputFormat)
+}
 
-       if impersonateOverride != "" {
-               commandArgs = append(commandArgs, "--as", impersonateOverride)
+// kubectlFlags returns the flags that select the cluster, namespace and 
identity kubectl talks to
+func kubectlFlags(cmd *cobra.Command) []string {
+       var args []string
+       for _, name := range []string{"namespace", "context", "kubeconfig", 
"as"} {
+               if v, _ := cmd.Flags().GetString(name); v != "" {
+                       args = append(args, "--"+name, v)
+               }
        }
 
-       if impersonateGroupOverride != "" {
-               commandArgs = append(commandArgs, "--as-group", 
impersonateGroupOverride)
+       groups, _ := cmd.Flags().GetStringArray("as-group")
+       for _, g := range groups {
+               args = append(args, "--as-group", g)
        }
 
-       return commandArgs
+       return args
 }
 
-// executeKubectlCommand executes the kubectl command and returns the output
-func (c *CommandOpts) executeKubectlCommand(commandArgs []string) ([]byte, 
error) {
+// runKubectl executes kubectl and returns its output
+func runKubectl(args ...string) ([]byte, error) {
        var res, cmdErr bytes.Buffer
 
-       out := exec.Command("kubectl", commandArgs...)
+       out := exec.Command("kubectl", args...)
        out.Stdout = &res
        out.Stderr = &cmdErr
        err := out.Run()
@@ -220,71 +214,59 @@
        return res.Bytes(), nil
 }
 
-// parseSecretResponse parses the kubectl output and handles secret selection
-func (c *CommandOpts) parseSecretResponse(output []byte, cmd *cobra.Command) 
(Secret, error) {
-       var secret Secret
-       if c.secretName == "" {
-               return c.handleSecretSelection(output, cmd)
+// kubectlNames returns the names of all resources of the given kind
+//
+// Unlike -o json or -o jsonpath, the default output makes kubectl ask the API 
server for a
+// table of metadata, so no secret data is downloaded just to list names.
+func kubectlNames(kind string, flags ...string) ([]string, error) {
+       out, err := runKubectl(append([]string{"get", kind, "--no-headers"}, 
flags...)...)
+       if err != nil {
+               return nil, err
        }
 
-       if err := json.Unmarshal(output, &secret); err != nil {
-               return secret, fmt.Errorf("failed to parse kubectl output as 
secret: %w", err)
+       var names []string
+       for line := range strings.Lines(string(out)) {
+               if fields := strings.Fields(line); len(fields) > 0 {
+                       names = append(names, fields[0]) // the first column is 
always the name
+               }
        }
 
-       return secret, nil
+       return names, nil
 }
 
-// handleSecretSelection handles the interactive selection of a secret from a 
list
-func (c *CommandOpts) handleSecretSelection(output []byte, cmd *cobra.Command) 
(Secret, error) {
-       var secretList SecretList
-       if err := json.Unmarshal(output, &secretList); err != nil {
-               return Secret{}, fmt.Errorf("failed to parse kubectl output as 
secret list: %w", err)
+// selectSecret lets the user pick one of the available secrets and stores the 
choice
+func (c *CommandOpts) selectSecret(cmd *cobra.Command, flags []string) error {
+       names, err := kubectlNames("secrets", flags...)
+       if err != nil {
+               return err
        }
 
        // Since we don't query valid namespaces, we'll avoid prompting the 
user to select a secret if we didn't retrieve any secrets
-       if len(secretList.Items) == 0 {
-               return Secret{}, ErrNoSecretFound
+       if len(names) == 0 {
+               return ErrNoSecretFound
        }
 
-       opts := []string{}
-       secretMap := map[string]Secret{}
-       for _, v := range secretList.Items {
-               opts = append(opts, v.Metadata.Name)
-               secretMap[v.Metadata.Name] = v
-       }
-
-       err := huh.NewForm(
+       err = huh.NewForm(
                huh.NewGroup(
                        huh.NewSelect[string]().
                                Title(secretListTitle).
-                               Description(fmt.Sprintf(secretListDescription, 
len(secretList.Items))).
-                               Options(huh.NewOptions(opts...)...).
+                               Description(fmt.Sprintf(secretListDescription, 
len(names))).
+                               Options(huh.NewOptions(names...)...).
                                Value(&c.secretName),
                ),
        ).WithProgramOptions(tea.WithInput(cmd.InOrStdin()), 
tea.WithOutput(cmd.OutOrStdout())).Run()
        if err != nil {
-               return Secret{}, fmt.Errorf("failed to get user selection: %w", 
err)
+               return fmt.Errorf("failed to get user selection: %w", err)
        }
 
-       return secretMap[c.secretName], nil
-}
-
-// ProcessSecret takes the secret and user input to determine the output
-func ProcessSecret(outWriter, errWriter io.Writer, inputReader io.Reader, 
secret Secret, secretKey string, decodeAll bool) error {
-       return ProcessSecretWithOptions(outWriter, errWriter, inputReader, 
secret, secretKey, decodeAll, "text")
+       return nil
 }
 
-// decodeAllData decodes all data in the secret and returns sorted key-value 
pairs
-func decodeAllData(secret Secret, data SecretData) ([]KeyValue, error) {
-       var keys []string
-       for k := range data {
-               keys = append(keys, k)
-       }
-       sort.Strings(keys)
+// decodeAllData decodes the given keys of the secret and returns them as 
key-value pairs
+func decodeAllData(secret Secret, keys []string) ([]KeyValue, error) {
        var decodedData []KeyValue
        for _, k := range keys {
-               v := data[k]
-               s, err := secret.Decode(v)
+               s, err := secret.Decode(secret.Data[k])
                if err != nil {
                        return nil, fmt.Errorf("failed to decode key %s: %w", 
k, err)
                }
@@ -300,61 +282,32 @@
                return ErrSecretEmpty
        }
 
-       var keys []string
-       for k := range data {
-               keys = append(keys, k)
-       }
-       sort.Strings(keys)
+       keys := slices.Sorted(maps.Keys(data))
 
-       if decodeAll {
-               decodedData, err := decodeAllData(secret, data)
+       switch {
+       case decodeAll:
+       case secretKey != "":
+               v, ok := data[secretKey]
+               if !ok {
+                       return ErrSecretKeyNotFound
+               }
+               s, err := secret.Decode(v)
                if err != nil {
-                       return err
+                       return fmt.Errorf("failed to decode key %s: %w", 
secretKey, err)
                }
-               return outputFormattedSecret(outWriter, secret, decodedData, 
outputFormat)
-       } else if len(data) == 1 {
+               return outputFormattedSecret(outWriter, secret, 
[]KeyValue{{Key: secretKey, Value: s}}, outputFormat)
+       case len(keys) == 1:
                if _, err := fmt.Fprintf(errWriter, singleKeyDescription+"\n", 
keys[0]); err != nil {
                        return fmt.Errorf("failed to write to stderr: %w", err)
                }
-               decodedData, err := decodeAllData(secret, data)
-               if err != nil {
-                       return err
-               }
-               return outputFormattedSecret(outWriter, secret, decodedData, 
outputFormat)
-       } else if secretKey != "" {
-               if v, ok := data[secretKey]; ok {
-                       s, err := secret.Decode(v)
-                       if err != nil {
-                               return fmt.Errorf("failed to decode key %s: 
%w", secretKey, err)
-                       }
-                       if outputFormat == "text" {
-                               if _, err := fmt.Fprintf(outWriter, "%s\n", s); 
err != nil {
-                                       return fmt.Errorf("failed to write 
output: %w", err)
-                               }
-                       } else {
-                               decodedData := []KeyValue{
-                                       {
-                                               Key:   secretKey,
-                                               Value: s,
-                                       },
-                               }
-                               return outputFormattedSecret(outWriter, secret, 
decodedData, outputFormat)
-                       }
-               } else {
-                       return ErrSecretKeyNotFound
-               }
-       } else {
-               opts := make([]string, len(keys)+1)
-               opts[0] = "all"
-               copy(opts[1:], keys)
-
+       default:
                var selection string
                err := huh.NewForm(
                        huh.NewGroup(
                                huh.NewSelect[string]().
                                        Title(secretTitle).
                                        
Description(fmt.Sprintf(secretDescription, len(data), secret.Metadata.Name)).
-                                       Options(huh.NewOptions(opts...)...).
+                                       
Options(huh.NewOptions(append([]string{"all"}, keys...)...)...).
                                        Value(&selection),
                        ),
                ).WithProgramOptions(tea.WithInput(inputReader), 
tea.WithOutput(outWriter)).Run()
@@ -362,14 +315,14 @@
                        return err
                }
 
-               if selection == "all" {
-                       decodeAll = true
-               }
-
-               return ProcessSecretWithOptions(outWriter, errWriter, 
inputReader, secret, selection, decodeAll, outputFormat)
+               return ProcessSecretWithOptions(outWriter, errWriter, 
inputReader, secret, selection, selection == "all", outputFormat)
        }
 
-       return nil
+       decodedData, err := decodeAllData(secret, keys)
+       if err != nil {
+               return err
+       }
+       return outputFormattedSecret(outWriter, secret, decodedData, 
outputFormat)
 }
 
 // buildOutputMap builds the common output structure for JSON/YAML formats
@@ -396,10 +349,13 @@
 
 // outputJSON outputs secret data as JSON
 func outputJSON(outWriter io.Writer, secret Secret, decodedData []KeyValue) 
error {
-       output := buildOutputMap(secret, decodedData)
-       encoder := json.NewEncoder(outWriter)
-       encoder.SetIndent("", "  ")
-       return encoder.Encode(output)
+       // Deterministic sorts the keys of the output map
+       out, err := json.Marshal(buildOutputMap(secret, decodedData), 
jsontext.WithIndent("  "), json.Deterministic(true))
+       if err != nil {
+               return err
+       }
+       _, err = fmt.Fprintf(outWriter, "%s\n", out)
+       return err
 }
 
 // outputYAML outputs secret data as YAML
@@ -410,22 +366,12 @@
 
 // outputText outputs secret data as plain text
 func outputText(outWriter io.Writer, sortedData []KeyValue) error {
-       var format string
-
-       if len(sortedData) == 1 {
-               format = "%s\n"
-       } else {
-               format = "%s='%s'\n"
-       }
-
        for _, kv := range sortedData {
-               var args []any
-               if len(sortedData) == 1 {
-                       args = []any{kv.Value}
-               } else {
-                       args = []any{kv.Key, kv.Value}
+               line := kv.Value + "\n"
+               if len(sortedData) > 1 {
+                       line = fmt.Sprintf("%s='%s'\n", kv.Key, kv.Value)
                }
-               if _, err := fmt.Fprintf(outWriter, format, args...); err != 
nil {
+               if _, err := io.WriteString(outWriter, line); err != nil {
                        return fmt.Errorf("failed to write output: %w", err)
                }
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kubectl-view-secret-0.16.0/pkg/cmd/view-secret_test.go 
new/kubectl-view-secret-0.17.0/pkg/cmd/view-secret_test.go
--- old/kubectl-view-secret-0.16.0/pkg/cmd/view-secret_test.go  2026-04-23 
07:19:16.000000000 +0200
+++ new/kubectl-view-secret-0.17.0/pkg/cmd/view-secret_test.go  2026-11-09 
07:33:51.000000000 +0100
@@ -4,7 +4,6 @@
        "bytes"
        "errors"
        "fmt"
-       "io"
        "strings"
        "testing"
 
@@ -61,20 +60,22 @@
                want     string
                wantErr  error
        }{
-               "all":                        {args: []string{"test", "--all"}, 
want: `key1='value1'\nkey2='value2'`},
+               "all":                        {args: []string{"test", "--all"}, 
want: "key1='value1\n'\nkey2='value2\n'\n"},
                "custom ctx":                 {args: []string{"test", 
"--context", "gotest"}, wantErr: errors.New("Error in configuration: context 
was not found for specified context: gotest\nError: kubectl command failed: 
exit status 1")},
                "custom kubecfg":             {args: []string{"test", 
"--kubeconfig", "cfg"}, wantErr: errors.New("error: stat cfg: no such file or 
directory\nError: kubectl command failed: exit status 1")},
                "custom ns (does not exist)": {args: []string{"test", 
"--namespace", "bob"}, wantErr: errors.New("Error from server (NotFound): 
namespaces \"bob\" not found\nError: kubectl command failed: exit status 1")},
                "custom ns (no secret)":      {args: []string{"test", 
"--namespace", "another"}, wantErr: errors.New("Error from server (NotFound): 
secrets \"test\" not found\nError: kubectl command failed: exit status 1")},
-               "custom ns (valid secret)":   {args: []string{"gopher", 
"--namespace", "another"}, want: `Viewing only available key: foo\nbar`},
-               "helm":                       {args: []string{"test3", 
"--namespace", "helm"}, want: `Viewing only available key: release\nhelm-test`},
-               "impersonate group":          {args: []string{"test", "--all", 
"--as", "gopher"}, want: `key1='value1'\nkey2='value2'`},
-               "impersonate user & group":   {args: []string{"test", "--all", 
"--as", "gopher", "--as-group", "golovers"}, want: 
`key1='value1'\nkey2='value2'`},
-               // make bootstrap sources 2 test secrets in the default 
namespace, select the first one and print all values
-               "interactive":                       {args: []string{"--all"}, 
feedkeys: "\r", want: `key1='value1'\nkey2='value2'`},
+               "custom ns (valid secret)":   {args: []string{"gopher", 
"--namespace", "another"}, want: "Viewing only available key: foo\nbar\n"},
+               "helm":                       {args: []string{"test3", 
"--namespace", "helm"}, want: "Viewing only available key: 
release\nhelm-test\n"},
+               "impersonate group":          {args: []string{"test", "--all", 
"--as", "gopher"}, want: "key1='value1\n'\nkey2='value2\n'\n"},
+               "impersonate user & group":   {args: []string{"test", "--all", 
"--as", "gopher", "--as-group", "golovers"}, want: 
"key1='value1\n'\nkey2='value2\n'\n"},
+               // make bootstrap sources several secrets in the default 
namespace, select the first one (basic-auth-secret) and print all values
+               "interactive":                       {args: []string{"--all"}, 
feedkeys: "\r", want: "password='secret123\n'\nusername='admin'\n"},
                "interactive custom ns (no secret)": {args: 
[]string{"--namespace", "empty"}, wantErr: ErrNoSecretFound},
                "invalid arg count":                 {args: []string{"a", "b", 
"c"}, wantErr: errors.New("accepts between 0 and 2 arg(s), received 3")},
-               "quiet":                             {args: []string{"test2", 
"--quiet"}, want: `value1`},
+               "invalid output format":             {args: []string{"test", 
"-o", "xml"}, wantErr: errors.New(`invalid output format "xml": must be one of 
text, json, yaml`)},
+               "single-key secret, unknown key":    {args: []string{"test2", 
"typo"}, wantErr: ErrSecretKeyNotFound},
+               "quiet":                             {args: []string{"test2", 
"--quiet"}, want: "value1\n"},
                "unknown flag":                      {args: 
[]string{"--version"}, wantErr: errors.New("unknown flag: --version")},
        }
 
@@ -106,15 +107,12 @@
                                return
                        }
 
-                       _, err = io.ReadAll(outBuf)
-                       if err != nil {
-                               t.Fatal(err)
-                       }
+                       assert.Contains(t, outBuf.String(), tt.want)
                })
        }
 }
 
-func TestProcessSecret(t *testing.T) {
+func TestProcessSecretWithOptions(t *testing.T) {
        tests := map[string]struct {
                secretData SecretData
                secretType SecretType
@@ -193,6 +191,16 @@
                        ErrSecretKeyNotFound,
                        "",
                },
+               "view-secret <secret-single-key> NONE": {
+                       secretSingle,
+                       Opaque,
+                       nil,
+                       nil,
+                       "NONE",
+                       false,
+                       ErrSecretKeyNotFound,
+                       "",
+               },
                "view-secret <secret-empty>": {
                        secretEmpty,
                        Opaque,
@@ -227,11 +235,13 @@
                                readBuf = *strings.NewReader(test.feedkeys)
                        }
 
-                       err := ProcessSecret(&stdOutBuf, &stdErrBuf, &readBuf, 
Secret{Data: test.secretData, Type: test.secretType}, test.secretKey, 
test.decodeAll)
+                       err := ProcessSecretWithOptions(&stdOutBuf, &stdErrBuf, 
&readBuf, Secret{Data: test.secretData, Type: test.secretType}, test.secretKey, 
test.decodeAll, "text")
 
                        if test.err != nil {
                                assert.Equal(t, err, test.err)
                        } else {
+                               assert.NoError(t, err)
+
                                gotStdOut := stdOutBuf.String()
                                gotStdErr := stdErrBuf.String()
 
@@ -251,37 +261,6 @@
        }
 }
 
-func TestErrorHandling(t *testing.T) {
-       tests := map[string]struct {
-               name        string
-               setupCmd    func(*CommandOpts)
-               expectError bool
-       }{
-               "invalid namespace": {
-                       name: "invalid namespace",
-                       setupCmd: func(opts *CommandOpts) {
-                               opts.secretName = "test"
-                               opts.customNamespace = "invalid-namespace"
-                       },
-                       expectError: true,
-               },
-               "nonexistent secret": {
-                       name: "nonexistent secret",
-                       setupCmd: func(opts *CommandOpts) {
-                               opts.secretName = "nonexistent"
-                       },
-                       expectError: true,
-               },
-       }
-
-       for name, tt := range tests {
-               t.Run(name, func(t *testing.T) {
-                       opts := &CommandOpts{}
-                       tt.setupCmd(opts)
-               })
-       }
-}
-
 func TestOutputText(t *testing.T) {
        tests := map[string]struct {
                sortedData []KeyValue
@@ -404,6 +383,31 @@
                })
        }
 }
+
+func TestKubectlFlags(t *testing.T) {
+       tests := map[string]struct {
+               args []string
+               want []string
+       }{
+               "none": {nil, nil},
+               "all flags": {
+                       []string{"-n", "ns", "-c", "ctx", "-k", "cfg", "--as", 
"bob"},
+                       []string{"--namespace", "ns", "--context", "ctx", 
"--kubeconfig", "cfg", "--as", "bob"},
+               },
+               "every group is forwarded": {
+                       []string{"--as", "bob", "--as-group", "a", 
"--as-group", "b"},
+                       []string{"--as", "bob", "--as-group", "a", 
"--as-group", "b"},
+               },
+       }
+
+       for name, tt := range tests {
+               t.Run(name, func(t *testing.T) {
+                       cmd := NewCmdViewSecret()
+                       assert.NoError(t, cmd.ParseFlags(tt.args))
+                       assert.Equal(t, tt.want, kubectlFlags(cmd))
+               })
+       }
+}
 
 func TestCompletionFlagRegistration(t *testing.T) {
        cmd := NewCmdViewSecret()

++++++ kubectl-view-secret.obsinfo ++++++
--- /var/tmp/diff_new_pack.6TVpC4/_old  2026-11-10 14:42:50.823665243 +0100
+++ /var/tmp/diff_new_pack.6TVpC4/_new  2026-11-10 14:42:50.826665369 +0100
@@ -1,5 +1,5 @@
 name: kubectl-view-secret
-version: 0.16.0
-mtime: 1776921556
-commit: 2e33057ad8e86bb12251fd363816491151c16818
+version: 0.17.0
+mtime: 1794206031
+commit: 3ca5d45233f382008389af09555b332843867c73
 

++++++ vendor.tar.gz ++++++
++++ 147308 lines of diff (skipped)

Reply via email to