Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package kubectl-switch for openSUSE:Factory 
checked in at 2025-10-13 15:35:40
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/kubectl-switch (Old)
 and      /work/SRC/openSUSE:Factory/.kubectl-switch.new.18484 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "kubectl-switch"

Mon Oct 13 15:35:40 2025 rev:8 rq:1311029 version:2.3.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/kubectl-switch/kubectl-switch.changes    
2025-09-15 19:55:43.486202649 +0200
+++ /work/SRC/openSUSE:Factory/.kubectl-switch.new.18484/kubectl-switch.changes 
2025-10-13 15:37:00.873448887 +0200
@@ -1,0 +2,70 @@
+Mon Oct 13 06:53:03 UTC 2025 - Johannes Kastl 
<[email protected]>
+
+- Update to version 2.3.0:
+  * New Features
+    - use viper for configuration management (#213) (c173c57)
+  * Documentation
+    - update defaults in config section (1339ef9)
+  * CI/CD
+    - add PR metadata workflow for title validation and labeling
+      (034c01b)
+    - merge all lint jobs in a single workflow (5480be7)
+    - remove label-sync (da92db2)
+
+-------------------------------------------------------------------
+Mon Oct 13 06:44:34 UTC 2025 - Johannes Kastl 
<[email protected]>
+
+- Update to version 2.2.17:
+  * Bug Fixes
+    - remove file extension check. we assume all files in the
+      kubeconfig_dir are kubeconfigs (3b56de5)
+  * CI/CD
+    - github-action: Update github/codeql-action ( v3.30.7 →
+      v4.30.7 ) (#210) (ab77e20)
+    - github-action: update renovatebot/github-action ( v43.0.15 →
+      v43.0.16 ) (7d54339)
+    - github-action: update github/codeql-action ( v3.30.6 →
+      v3.30.7 ) (75fe234)
+    - github-action: update jdx/mise-action ( v3.3.0 → v3.3.1 )
+      (c951441)
+    - github-action: update renovatebot/github-action ( v43.0.14 →
+      v43.0.15 ) (66731bc)
+    - github-action: update jdx/mise-action ( v3.2.0 → v3.3.0 )
+      (9ee7740)
+    - github-action: update github/codeql-action ( v3.30.5 →
+      v3.30.6 ) (c678380)
+    - github-action: update docker/login-action ( v3.5.0 → v3.6.0 )
+      (b78b245)
+    - github-action: update renovatebot/github-action ( v43.0.13 →
+      v43.0.14 ) (26acafa)
+    - github-action: update github/codeql-action ( v3.30.4 →
+      v3.30.5 ) (d9c5ef5)
+    - github-action: update github/codeql-action ( v3.30.3 →
+      v3.30.4 ) (a457268)
+    - github-action: update renovatebot/github-action ( v43.0.12 →
+      v43.0.13 ) (568adf2)
+    - github-action: update renovatebot/github-action ( v43.0.11 →
+      v43.0.12 ) (b09e93d)
+    - github-action: update actions/create-github-app-token (
+      v2.1.2 → v2.1.4 ) (2d27a35)
+    - github-action: update actions/create-github-app-token (
+      v2.1.1 → v2.1.2 ) (f31c63a)
+  * Chores
+    - cleanup spaghetti (#211) (e6e647e)
+    - mise: update go ( 1.25.1 → 1.25.2 ) (41bdb64)
+    - mise: update aqua:goreleaser/goreleaser ( 2.12.4 → 2.12.5 )
+      (ba65d9e)
+    - mise: update aqua:goreleaser/goreleaser ( 2.12.3 → 2.12.4 )
+      (471f138)
+    - mise: Update npm:@commitlint/cli ( 19.8.1 → 20.0.0 ) (#209)
+      (48acdbe)
+    - mise: update aqua:goreleaser/goreleaser ( 2.12.2 → 2.12.3 )
+      (4954cf9)
+    - mise: update aqua:golangci/golangci-lint ( 2.4.0 → 2.5.0 )
+      (350b550)
+    - mise: update aqua:goreleaser/goreleaser ( 2.12.1 → 2.12.2 )
+      (0e46b49)
+    - mise: update aqua:goreleaser/goreleaser ( 2.12.0 → 2.12.1 )
+      (8ed7db6)
+
+-------------------------------------------------------------------

Old:
----
  kubectl-switch-2.2.16.obscpio

New:
----
  kubectl-switch-2.3.0.obscpio

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

Other differences:
------------------
++++++ kubectl-switch.spec ++++++
--- /var/tmp/diff_new_pack.JWGj7P/_old  2025-10-13 15:37:01.625480497 +0200
+++ /var/tmp/diff_new_pack.JWGj7P/_new  2025-10-13 15:37:01.641481169 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           kubectl-switch
-Version:        2.2.16
+Version:        2.3.0
 Release:        0
 Summary:        Simple tool to help manage multiple kubeconfig files
 License:        MIT
@@ -84,6 +84,8 @@
 # Install the binary.
 install -D -m 0755 %{name} %{buildroot}/%{_bindir}/%{name}
 
+mkdir -p /home/abuild/.kube/configs
+
 # create the bash completion file
 mkdir -p %{buildroot}%{_datarootdir}/bash-completion/completions/
 %{buildroot}/%{_bindir}/%{name} completion bash > 
%{buildroot}%{_datarootdir}/bash-completion/completions/%{name}

++++++ _service ++++++
--- /var/tmp/diff_new_pack.JWGj7P/_old  2025-10-13 15:37:02.021497142 +0200
+++ /var/tmp/diff_new_pack.JWGj7P/_new  2025-10-13 15:37:02.085499833 +0200
@@ -3,7 +3,7 @@
     <param name="url">https://github.com/mirceanton/kubectl-switch</param>
     <param name="scm">git</param>
     <param name="exclude">.git</param>
-    <param name="revision">v2.2.16</param>
+    <param name="revision">v2.3.0</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="versionrewrite-pattern">v(.*)</param>
     <param name="changesgenerate">enable</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.JWGj7P/_old  2025-10-13 15:37:02.489516815 +0200
+++ /var/tmp/diff_new_pack.JWGj7P/_new  2025-10-13 15:37:02.529518496 +0200
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://github.com/mirceanton/kubectl-switch</param>
-              <param 
name="changesrevision">6bb6ac390fbf54d2f17de8dea1de44928fe632bf</param></service></servicedata>
+              <param 
name="changesrevision">1339ef9aaa2e74313d2f67988d4ac6ec08de852a</param></service></servicedata>
 (No newline at EOF)
 

++++++ kubectl-switch-2.2.16.obscpio -> kubectl-switch-2.3.0.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-switch-2.2.16/.envrc 
new/kubectl-switch-2.3.0/.envrc
--- old/kubectl-switch-2.2.16/.envrc    1970-01-01 01:00:00.000000000 +0100
+++ new/kubectl-switch-2.3.0/.envrc     2025-10-13 00:17:55.000000000 +0200
@@ -0,0 +1,4 @@
+export KUBECONFIG_DIR="./test/configs/"
+export KUBECONFIG="./test/config"
+export LOG_LEVEL="error"
+export LOG_FORMAT="text"
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-switch-2.2.16/.mise.toml 
new/kubectl-switch-2.3.0/.mise.toml
--- old/kubectl-switch-2.2.16/.mise.toml        2025-09-13 20:04:28.000000000 
+0200
+++ new/kubectl-switch-2.3.0/.mise.toml 2025-10-13 00:17:55.000000000 +0200
@@ -2,9 +2,9 @@
 # 
=================================================================================================
 # Project Tools -> pinned versions
 # 
=================================================================================================
-go = "1.25.1"
-"aqua:golangci/golangci-lint" = "2.4.0"
-"aqua:goreleaser/goreleaser" = "2.12.0"
+go = "1.25.2"
+"aqua:golangci/golangci-lint" = "2.5.0"
+"aqua:goreleaser/goreleaser" = "2.12.5"
 "aqua:kubernetes/kubectl" = "1.33.1"
 
 
@@ -14,10 +14,27 @@
 "aqua:kubernetes/minikube" = "latest"
 "aqua:helm/helm" = "latest"
 "aqua:derailed/k9s" = "latest"
+direnv = "latest"
 
 
 # 
=================================================================================================
 # CI Tools
 # 
=================================================================================================
-"npm:@commitlint/cli" = "19"
+"npm:@commitlint/cli" = "20"
 "aqua:rhysd/actionlint" = "1"
+"shellcheck" = "0.11"
+
+# 
=================================================================================================
+# TASKS -> GO
+# 
=================================================================================================
+[tasks."release"]
+description = "Run goreleaser in snapshot mode."
+run = "goreleaser release --snapshot --clean"
+
+[tasks."lint"]
+description = "Run golangci-lint in fix mode."
+run = "golangci-lint run --fix"
+
+[tasks."test"]
+description = "Run e2e tests."
+run = "bash scripts/test.sh"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-switch-2.2.16/README.md 
new/kubectl-switch-2.3.0/README.md
--- old/kubectl-switch-2.2.16/README.md 2025-09-13 20:04:28.000000000 +0200
+++ new/kubectl-switch-2.3.0/README.md  2025-10-13 00:17:55.000000000 +0200
@@ -6,15 +6,9 @@
 
 ## Features
 
-- **Multiple kubeconfig files**: Manage multiple kubeconfig files with one or 
more contexts each, all present in a single directory. You don't have to 
generate a global kubeconfig to hold all of your contexts
-- **Context Switching**: Easily switch between different Kubernetes contexts 
from multiple config files.
-- **Namespace Switching**: Quickly change the current namespace in the active 
context.
-- **Previous Config**: Switch back to the previous configuration with `kubectl 
switch -`.
-- **Non-Interactive Mode**: Specify the desired context or namespace directly 
as an argument.
-- **Interactive Mode**: Run the program with no arguments to get an 
interactive list of available namespaces or contexts to choose from.
-- **Persistent Configuration**: Changes persist across different shell 
sessions.
-- **Shell Completions**: Provides tab completion for available contexts and 
namespaces.
-- **Non-destructive**: `kubectl-switch` will never edit those. It will only 
work with copies of them.
+- **Multiple kubeconfig files**: Manage multiple kubeconfig files in a single 
directory without merging them
+- **Context & namespace switching**: Switch between contexts and namespaces 
from multiple config files
+- **Interactive & non-interactive modes**: Select from a list or specify 
directly as an argument (with tab completion support!)
 
 ## Why `kubectl-switch`?
 
@@ -48,29 +42,29 @@
 1. Download the appropriate binary for your system and extract the archive.
 2. Make the extracted binary executable:
 
-    ```bash
-    chmod +x kubectl-switch
-    ```
+   ```bash
+   chmod +x kubectl-switch
+   ```
 
 3. Move the binary to a directory in your PATH:
 
-    ```bash
-    mv kubectl-switch /usr/local/bin/kubectl-switch
-    ```
+   ```bash
+   mv kubectl-switch /usr/local/bin/kubectl-switch
+   ```
 
 ### Install via Homebrew
 
 1. Add the tap
 
-    ```bash
-    brew tap mirceanton/taps
-    ```
+   ```bash
+   brew tap mirceanton/taps
+   ```
 
 2. Install `kubectl-switch`
 
-    ```bash
-    brew install kubectl-switch
-    ```
+   ```bash
+   brew install kubectl-switch
+   ```
 
 ### Running via Docker
 
@@ -92,22 +86,22 @@
 
 1. Clone the repository:
 
-    ```bash
-    git clone https://github.com/mirceanton/kubectl-switch
-    cd kubectl-switch
-    ```
+   ```bash
+   git clone https://github.com/mirceanton/kubectl-switch
+   cd kubectl-switch
+   ```
 
 2. Build the tool:
 
-    ```bash
-    go build -o kubectl-switch
-    ```
+   ```bash
+   go build -o kubectl-switch
+   ```
 
 3. Move the binary to your PATH:
 
-    ```bash
-    mv kubectl-switch /usr/local/bin/kubectl-switch
-    ```
+   ```bash
+   mv kubectl-switch /usr/local/bin/kubectl-switch
+   ```
 
 ## Usage
 
@@ -155,12 +149,16 @@
 
 ## Configuration
 
-You can configure `kubectl-switch` using environment variables or CLI flags:
+`kubectl-switch` uses Viper for configuration management, allowing you to 
configure the tool via command-line flags or environment variables, with flags 
taking precedence.
 
-|  Environment Variable  |     CLI Flag      |                   Description   
                 |        Default Value         |
-| :--------------------: | :---------------: | 
:----------------------------------------------: | :--------------------------: 
|
-|    `KUBECONFIG_DIR`    | `--kubeconfig-dir` | Directory containing your 
kubeconfig files.      |            None              |
-|     `KUBECONFIG`       |       N/A         | Path where the active 
kubeconfig will be stored. | `~/.kube/config`             |
+### Configuration Options
+
+| Option               | Flag               | Environment Variable | Default   
         | Description                                                       |
+| -------------------- | ------------------ | -------------------- | 
------------------ | 
----------------------------------------------------------------- |
+| Kubeconfig Directory | `--kubeconfig-dir` | `KUBECONFIG_DIR`     | 
`~/.kube/configs/` | Directory containing your kubeconfig files                 
       |
+| Kubeconfig           | `--kubeconfig`     | `KUBECONFIG`         | 
`~/.kube/config`   | Path to the currently active kubeconfig file.              
       |
+| Log Level            | `--log-level`      | `LOG_LEVEL`          | `info`    
         | Logging verbosity (trace, debug, info, warn, error, fatal, panic) |
+| Log Format           | `--log-format`     | `LOG_FORMAT`         | `text`    
         | Log output format (text, json)                                    |
 
 ## Shell Completion
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-switch-2.2.16/cmd/context.go 
new/kubectl-switch-2.3.0/cmd/context.go
--- old/kubectl-switch-2.2.16/cmd/context.go    2025-09-13 20:04:28.000000000 
+0200
+++ new/kubectl-switch-2.3.0/cmd/context.go     2025-10-13 00:17:55.000000000 
+0200
@@ -2,13 +2,10 @@
 
 import (
        "github.com/AlecAivazis/survey/v2"
-       "github.com/mirceanton/kubectl-switch/v2/pkg/kubeconfig"
        log "github.com/sirupsen/logrus"
        "github.com/spf13/cobra"
 )
 
-var contextManager = kubeconfig.NewContextManager(manager)
-
 var contextCmd = &cobra.Command{
        Use:               "context",
        Aliases:           []string{"ctx"},
@@ -16,42 +13,25 @@
        ValidArgsFunction: getContextCompletions,
        Args:              cobra.MaximumNArgs(1),
        Run: func(cmd *cobra.Command, args []string) {
-               // Validate and get the config directory
-               validConfigDir, err := 
contextManager.ValidateConfigDir(configDir)
-               if err != nil {
-                       log.Fatal(err)
-               }
-
-               // Get all contexts from kubeconfig files
-               contextMap, contextNames, err := 
contextManager.GetContextsFromDir(validConfigDir)
-               if err != nil {
-                       log.Fatalf("Failed to read kubeconfig files: %v", err)
+               contextNames := configManager.GetAllContexts()
+               if len(contextNames) == 0 {
+                       log.Fatal("No kubernetes contexts found in the provided 
directory")
                }
 
-               if len(contextMap) == 0 {
-                       log.Fatal("No kubernetes contexts found in the provided 
directory: ", validConfigDir)
-               }
-
-               // Determine the target context
                var selectedContext string
                if len(args) == 1 {
                        selectedContext = args[0]
-                       if _, exists := contextMap[selectedContext]; !exists {
-                               log.Fatalf("Context '%s' not found", 
selectedContext)
-                       }
                } else {
                        prompt := &survey.Select{
                                Message: "Choose a context:",
                                Options: contextNames,
                        }
-                       err = survey.AskOne(prompt, &selectedContext)
-                       if err != nil {
+                       if err := survey.AskOne(prompt, &selectedContext); err 
!= nil {
                                log.Fatalf("Failed to get user input: %v", err)
                        }
                }
 
-               // Switch to the selected context
-               if err := 
contextManager.SwitchContext(contextMap[selectedContext], selectedContext); err 
!= nil {
+               if err := configManager.SwitchToContext(selectedContext); err 
!= nil {
                        log.Fatalf("Failed to switch context: %v", err)
                }
 
@@ -63,18 +43,6 @@
        rootCmd.AddCommand(contextCmd)
 }
 
-// getContextCompletions provides bash completion for available contexts
 func getContextCompletions(cmd *cobra.Command, args []string, toComplete 
string) ([]string, cobra.ShellCompDirective) {
-       configDirFlag, _ := cmd.Flags().GetString("kubeconfig-dir")
-       validConfigDir, err := contextManager.ValidateConfigDir(configDirFlag)
-       if err != nil {
-               return nil, cobra.ShellCompDirectiveNoFileComp
-       }
-
-       _, contextNames, err := 
contextManager.GetContextsFromDir(validConfigDir)
-       if err != nil {
-               return nil, cobra.ShellCompDirectiveNoFileComp
-       }
-
-       return contextNames, cobra.ShellCompDirectiveNoFileComp
+       return configManager.GetAllContexts(), 
cobra.ShellCompDirectiveNoFileComp
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-switch-2.2.16/cmd/namespace.go 
new/kubectl-switch-2.3.0/cmd/namespace.go
--- old/kubectl-switch-2.2.16/cmd/namespace.go  2025-09-13 20:04:28.000000000 
+0200
+++ new/kubectl-switch-2.3.0/cmd/namespace.go   2025-10-13 00:17:55.000000000 
+0200
@@ -2,13 +2,10 @@
 
 import (
        "github.com/AlecAivazis/survey/v2"
-       "github.com/mirceanton/kubectl-switch/v2/pkg/kubeconfig"
        log "github.com/sirupsen/logrus"
        "github.com/spf13/cobra"
 )
 
-var namespaceManager = kubeconfig.NewNamespaceManager(manager)
-
 var namespaceCmd = &cobra.Command{
        Use:               "namespace",
        Aliases:           []string{"ns"},
@@ -16,13 +13,11 @@
        Args:              cobra.MaximumNArgs(1),
        ValidArgsFunction: getNamespaceCompletions,
        Run: func(cmd *cobra.Command, args []string) {
-               // Get all namespaces from the current context
-               namespaceNames, err := namespaceManager.GetNamespaces()
-               if err != nil {
-                       log.Fatalf("Error listing namespaces: %v", err)
+               namespaceNames := configManager.GetAllNamespaces()
+               if len(namespaceNames) == 0 {
+                       log.Fatal("No kubernetes namespaces found in the 
current cluster")
                }
 
-               // Determine the target namespace
                var selectedNamespace string
                if len(args) == 1 {
                        selectedNamespace = args[0]
@@ -31,14 +26,12 @@
                                Message: "Choose a namespace:",
                                Options: namespaceNames,
                        }
-                       err = survey.AskOne(prompt, &selectedNamespace)
-                       if err != nil {
+                       if err := survey.AskOne(prompt, &selectedNamespace); 
err != nil {
                                log.Fatalf("Failed to get user input: %v", err)
                        }
                }
 
-               // Switch to the selected namespace
-               if err := namespaceManager.SwitchNamespace(selectedNamespace); 
err != nil {
+               if err := configManager.SwitchToNamespace(selectedNamespace); 
err != nil {
                        log.Fatalf("Failed to switch namespace: %v", err)
                }
 
@@ -50,12 +43,6 @@
        rootCmd.AddCommand(namespaceCmd)
 }
 
-// getNamespaceCompletions provides bash completion for available namespaces
 func getNamespaceCompletions(cmd *cobra.Command, args []string, toComplete 
string) ([]string, cobra.ShellCompDirective) {
-       namespaceNames, err := namespaceManager.GetNamespaces()
-       if err != nil {
-               return nil, cobra.ShellCompDirectiveNoFileComp
-       }
-
-       return namespaceNames, cobra.ShellCompDirectiveNoFileComp
+       return configManager.GetAllNamespaces(), 
cobra.ShellCompDirectiveNoFileComp
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-switch-2.2.16/cmd/root.go 
new/kubectl-switch-2.3.0/cmd/root.go
--- old/kubectl-switch-2.2.16/cmd/root.go       2025-09-13 20:04:28.000000000 
+0200
+++ new/kubectl-switch-2.3.0/cmd/root.go        2025-10-13 00:17:55.000000000 
+0200
@@ -3,15 +3,16 @@
 import (
        "os"
 
-       "github.com/mirceanton/kubectl-switch/v2/pkg/kubeconfig"
+       "github.com/mirceanton/kubectl-switch/v2/internal/config"
+       "github.com/mirceanton/kubectl-switch/v2/internal/manager"
        log "github.com/sirupsen/logrus"
        "github.com/spf13/cobra"
+       "github.com/spf13/viper"
 )
 
 var (
-       configDir string // Global flag for the kubeconfig directory
-       version   string // The version of the tool, set at build time
-       manager   = kubeconfig.NewManager()
+       version       string
+       configManager *manager.Manager
 )
 
 var rootCmd = &cobra.Command{
@@ -22,15 +23,28 @@
        Short:   "A tool to switch Kubernetes contexts",
        Long:    `kubectl-switch is a CLI tool to switch Kubernetes contexts 
from multiple kubeconfig files.`,
        Version: version,
+       PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
+               // Load configuration
+               cfg, err := config.Load()
+               if err != nil {
+                       return err
+               }
+
+               // Set up logging
+               log.SetLevel(cfg.LogLevel)
+               log.SetFormatter(cfg.LogFormat)
+
+               // Create manager with config
+               configManager, err = manager.NewManager(cfg.Kubeconfig, 
cfg.KubeconfigDir)
+               if err != nil {
+                       return err
+               }
+               return nil
+       },
        RunE: func(cmd *cobra.Command, args []string) error {
-               // Check if the "-" argument is provided to switch to the 
previous config
                if len(args) == 1 && args[0] == "-" {
-                       if err := manager.SwitchToPrevious(); err != nil {
-                               if err == kubeconfig.ErrNoPreviousConfig {
-                                       log.Fatal("No previous configuration 
found")
-                               } else {
-                                       log.Fatalf("Failed to switch to 
previous config: %v", err)
-                               }
+                       if err := configManager.Restore(); err != nil {
+                               log.Fatalf("Failed to switch to previous 
config: %v", err)
                        }
                        return nil
                }
@@ -39,12 +53,37 @@
 }
 
 func Execute() {
-       err := rootCmd.Execute()
-       if err != nil {
+       if err := rootCmd.Execute(); err != nil {
                os.Exit(1)
        }
 }
 
 func init() {
-       rootCmd.PersistentFlags().StringVarP(&configDir, "kubeconfig-dir", "", 
"", "Directory containing kubeconfig files")
+       // Initialize Viper
+       cobra.OnInitialize(config.Init)
+
+       // Bind flags to Viper
+       rootCmd.PersistentFlags().String("kubeconfig-dir", "", "Directory 
containing kubeconfig files (env: KUBECONFIG_DIR)")
+       err := viper.BindPFlag("kubeconfig-dir", 
rootCmd.PersistentFlags().Lookup("kubeconfig-dir"))
+       if err != nil {
+               log.Fatalf("Failed to bind flag: %v", err)
+       }
+
+       rootCmd.PersistentFlags().String("kubeconfig", "", "Currently active 
kubeconfig file (env: KUBECONFIG)")
+       err = viper.BindPFlag("kubeconfig", 
rootCmd.PersistentFlags().Lookup("kubeconfig"))
+       if err != nil {
+               log.Fatalf("Failed to bind flag: %v", err)
+       }
+
+       rootCmd.PersistentFlags().String("log-level", "info", "Log level 
(trace, debug, info, warn, error, fatal, panic) (env: LOG_LEVEL)")
+       err = viper.BindPFlag("log-level", 
rootCmd.PersistentFlags().Lookup("log-level"))
+       if err != nil {
+               log.Fatalf("Failed to bind flag: %v", err)
+       }
+
+       rootCmd.PersistentFlags().String("log-format", "text", "Log format 
(text, json) (env: LOG_FORMAT)")
+       err = viper.BindPFlag("log-format", 
rootCmd.PersistentFlags().Lookup("log-format"))
+       if err != nil {
+               log.Fatalf("Failed to bind flag: %v", err)
+       }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-switch-2.2.16/go.mod 
new/kubectl-switch-2.3.0/go.mod
--- old/kubectl-switch-2.2.16/go.mod    2025-09-13 20:04:28.000000000 +0200
+++ new/kubectl-switch-2.3.0/go.mod     2025-10-13 00:17:55.000000000 +0200
@@ -9,6 +9,7 @@
        github.com/AlecAivazis/survey/v2 v2.3.7
        github.com/sirupsen/logrus v1.9.3
        github.com/spf13/cobra v1.10.1
+       github.com/spf13/viper v1.21.0
        k8s.io/apimachinery v0.34.1
        k8s.io/client-go v0.34.1
 )
@@ -16,14 +17,15 @@
 require (
        github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // 
indirect
        github.com/emicklei/go-restful/v3 v3.12.2 // indirect
+       github.com/fsnotify/fsnotify v1.9.0 // indirect
        github.com/fxamacker/cbor/v2 v2.9.0 // indirect
        github.com/go-logr/logr v1.4.2 // indirect
        github.com/go-openapi/jsonpointer v0.21.0 // indirect
        github.com/go-openapi/jsonreference v0.20.2 // indirect
        github.com/go-openapi/swag v0.23.0 // indirect
+       github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
        github.com/gogo/protobuf v1.3.2 // indirect
        github.com/google/gnostic-models v0.7.0 // indirect
-       github.com/google/go-cmp v0.7.0 // indirect
        github.com/google/uuid v1.6.0 // indirect
        github.com/inconshreveable/mousetrap v1.1.0 // indirect
        github.com/josharian/intern v1.0.0 // indirect
@@ -36,8 +38,14 @@
        github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // 
indirect
        github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // 
indirect
        github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // 
indirect
+       github.com/pelletier/go-toml/v2 v2.2.4 // indirect
        github.com/pkg/errors v0.9.1 // indirect
-       github.com/spf13/pflag v1.0.9 // indirect
+       github.com/sagikazarmark/locafero v0.11.0 // indirect
+       github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // 
indirect
+       github.com/spf13/afero v1.15.0 // indirect
+       github.com/spf13/cast v1.10.0 // indirect
+       github.com/spf13/pflag v1.0.10 // indirect
+       github.com/subosito/gotenv v1.6.0 // indirect
        github.com/x448/float16 v0.8.4 // indirect
        go.yaml.in/yaml/v2 v2.4.2 // indirect
        go.yaml.in/yaml/v3 v3.0.4 // indirect
@@ -45,7 +53,7 @@
        golang.org/x/oauth2 v0.27.0 // indirect
        golang.org/x/sys v0.31.0 // indirect
        golang.org/x/term v0.30.0 // indirect
-       golang.org/x/text v0.23.0 // indirect
+       golang.org/x/text v0.28.0 // indirect
        golang.org/x/time v0.9.0 // indirect
        google.golang.org/protobuf v1.36.5 // indirect
        gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
@@ -57,7 +65,6 @@
        k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
        sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
        sigs.k8s.io/randfill v1.0.0 // indirect
-       sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
        sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
        sigs.k8s.io/yaml v1.6.0 // indirect
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-switch-2.2.16/go.sum 
new/kubectl-switch-2.3.0/go.sum
--- old/kubectl-switch-2.2.16/go.sum    2025-09-13 20:04:28.000000000 +0200
+++ new/kubectl-switch-2.3.0/go.sum     2025-10-13 00:17:55.000000000 +0200
@@ -10,12 +10,12 @@
 github.com/davecgh/go-spew v1.1.1/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc 
h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/emicklei/go-restful/v3 v3.11.0 
h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
-github.com/emicklei/go-restful/v3 v3.11.0/go.mod 
h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
 github.com/emicklei/go-restful/v3 v3.12.2 
h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
 github.com/emicklei/go-restful/v3 v3.12.2/go.mod 
h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
-github.com/fxamacker/cbor/v2 v2.7.0 
h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
-github.com/fxamacker/cbor/v2 v2.7.0/go.mod 
h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
+github.com/frankban/quicktest v1.14.6 
h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod 
h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
+github.com/fsnotify/fsnotify v1.9.0 
h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
+github.com/fsnotify/fsnotify v1.9.0/go.mod 
h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
 github.com/fxamacker/cbor/v2 v2.9.0 
h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
 github.com/fxamacker/cbor/v2 v2.9.0/go.mod 
h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
 github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
@@ -30,13 +30,12 @@
 github.com/go-openapi/swag v0.23.0/go.mod 
h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
 github.com/go-task/slim-sprig/v3 v3.0.0 
h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
 github.com/go-task/slim-sprig/v3 v3.0.0/go.mod 
h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
+github.com/go-viper/mapstructure/v2 v2.4.0 
h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
+github.com/go-viper/mapstructure/v2 v2.4.0/go.mod 
h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod 
h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/google/gnostic-models v0.6.9 
h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
-github.com/google/gnostic-models v0.6.9/go.mod 
h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
 github.com/google/gnostic-models v0.7.0 
h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
 github.com/google/gnostic-models v0.7.0/go.mod 
h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
-github.com/google/go-cmp v0.5.9/go.mod 
h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
 github.com/google/go-cmp v0.7.0/go.mod 
h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 github.com/google/gofuzz v1.0.0/go.mod 
h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -78,7 +77,6 @@
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod 
h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd 
h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod 
h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v1.0.2 
h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 github.com/modern-go/reflect2 v1.0.2/go.mod 
h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee 
h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
 github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod 
h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
@@ -88,6 +86,8 @@
 github.com/onsi/ginkgo/v2 v2.21.0/go.mod 
h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
 github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
 github.com/onsi/gomega v1.35.1/go.mod 
h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
+github.com/pelletier/go-toml/v2 v2.2.4 
h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
+github.com/pelletier/go-toml/v2 v2.2.4/go.mod 
h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod 
h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 
h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -95,20 +95,23 @@
 github.com/rogpeppe/go-internal v1.13.1 
h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
 github.com/rogpeppe/go-internal v1.13.1/go.mod 
h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod 
h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/sagikazarmark/locafero v0.11.0 
h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
+github.com/sagikazarmark/locafero v0.11.0/go.mod 
h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
 github.com/sirupsen/logrus v1.9.3 
h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 github.com/sirupsen/logrus v1.9.3/go.mod 
h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
-github.com/spf13/cobra v1.9.1/go.mod 
h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
-github.com/spf13/cobra v1.10.0 h1:a5/WeUlSDCvV5a45ljW2ZFtV0bTDpkfSAj3uqB6Sc+0=
-github.com/spf13/cobra v1.10.0/go.mod 
h1:9dhySC7dnTtEiqzmqfkLj47BslqLCUPMXjG2lj/NgoE=
+github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 
h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
+github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod 
h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
+github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
+github.com/spf13/afero v1.15.0/go.mod 
h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
+github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
+github.com/spf13/cast v1.10.0/go.mod 
h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
 github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
 github.com/spf13/cobra v1.10.1/go.mod 
h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
-github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
-github.com/spf13/pflag v1.0.6/go.mod 
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/pflag v1.0.8 h1:/v546uKZ4gFGHpyXvV6CNKDeJBu4l5PRvxwQvdWrc0I=
-github.com/spf13/pflag v1.0.8/go.mod 
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
 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/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
+github.com/spf13/viper v1.21.0/go.mod 
h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
 github.com/stretchr/objx v0.1.0/go.mod 
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod 
h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/objx v0.5.0/go.mod 
h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -120,8 +123,10 @@
 github.com/stretchr/testify v1.7.1/go.mod 
h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod 
h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1/go.mod 
h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.10.0 
h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
-github.com/stretchr/testify v1.10.0/go.mod 
h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+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/subosito/gotenv v1.6.0 
h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
+github.com/subosito/gotenv v1.6.0/go.mod 
h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
 github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
 github.com/x448/float16 v0.8.4/go.mod 
h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
 github.com/yuin/goldmark v1.1.27/go.mod 
h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -173,8 +178,8 @@
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
-golang.org/x/text v0.23.0/go.mod 
h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
+golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
+golang.org/x/text v0.28.0/go.mod 
h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
 golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
 golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -182,8 +187,8 @@
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod 
h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod 
h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.12/go.mod 
h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
-golang.org/x/tools v0.26.0/go.mod 
h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
+golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
+golang.org/x/tools v0.35.0/go.mod 
h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -200,64 +205,23 @@
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod 
h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-k8s.io/api v0.33.1 h1:tA6Cf3bHnLIrUK4IqEgb2v++/GYUtqiu9sRVk3iBXyw=
-k8s.io/api v0.33.1/go.mod h1:87esjTn9DRSRTD4fWMXamiXxJhpOIREjWOSjsW1kEHw=
-k8s.io/api v0.33.2 h1:YgwIS5jKfA+BZg//OQhkJNIfie/kmRsO0BmNaVSimvY=
-k8s.io/api v0.33.2/go.mod h1:fhrbphQJSM2cXzCWgqU29xLDuks4mu7ti9vveEnpSXs=
-k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8=
-k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE=
-k8s.io/api v0.33.4 h1:oTzrFVNPXBjMu0IlpA2eDDIU49jsuEorGHB4cvKupkk=
-k8s.io/api v0.33.4/go.mod h1:VHQZ4cuxQ9sCUMESJV5+Fe8bGnqAARZ08tSTdHWfeAc=
-k8s.io/api v0.34.0 h1:L+JtP2wDbEYPUeNGbeSa/5GwFtIA662EmT2YSLOkAVE=
-k8s.io/api v0.34.0/go.mod h1:YzgkIzOOlhl9uwWCZNqpw6RJy9L2FK4dlJeayUoydug=
 k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM=
 k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk=
-k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4=
-k8s.io/apimachinery v0.33.1/go.mod 
h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
-k8s.io/apimachinery v0.33.2 h1:IHFVhqg59mb8PJWTLi8m1mAoepkUNYmptHsV+Z1m5jY=
-k8s.io/apimachinery v0.33.2/go.mod 
h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
-k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA=
-k8s.io/apimachinery v0.33.3/go.mod 
h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
-k8s.io/apimachinery v0.33.4 h1:SOf/JW33TP0eppJMkIgQ+L6atlDiP/090oaX0y9pd9s=
-k8s.io/apimachinery v0.33.4/go.mod 
h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
-k8s.io/apimachinery v0.34.0 h1:eR1WO5fo0HyoQZt1wdISpFDffnWOvFLOOeJ7MgIv4z0=
-k8s.io/apimachinery v0.34.0/go.mod 
h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
 k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4=
 k8s.io/apimachinery v0.34.1/go.mod 
h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
-k8s.io/client-go v0.33.1 h1:ZZV/Ks2g92cyxWkRRnfUDsnhNn28eFpt26aGc8KbXF4=
-k8s.io/client-go v0.33.1/go.mod h1:JAsUrl1ArO7uRVFWfcj6kOomSlCv+JpvIsp6usAGefA=
-k8s.io/client-go v0.33.2 h1:z8CIcc0P581x/J1ZYf4CNzRKxRvQAwoAolYPbtQes+E=
-k8s.io/client-go v0.33.2/go.mod h1:9mCgT4wROvL948w6f6ArJNb7yQd7QsvqavDeZHvNmHo=
-k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA=
-k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg=
-k8s.io/client-go v0.33.4 h1:TNH+CSu8EmXfitntjUPwaKVPN0AYMbc9F1bBS8/ABpw=
-k8s.io/client-go v0.33.4/go.mod h1:LsA0+hBG2DPwovjd931L/AoaezMPX9CmBgyVyBZmbCY=
-k8s.io/client-go v0.34.0 h1:YoWv5r7bsBfb0Hs2jh8SOvFbKzzxyNo0nSb0zC19KZo=
-k8s.io/client-go v0.34.0/go.mod h1:ozgMnEKXkRjeMvBZdV1AijMHLTh3pbACPvK7zFR+QQY=
 k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY=
 k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8=
 k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
 k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
-k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff 
h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
-k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod 
h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
 k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b 
h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
 k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod 
h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
-k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 
h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
-k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod 
h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
 k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 
h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
 k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod 
h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
-sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 
h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
-sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod 
h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
 sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 
h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
 sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod 
h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
-sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod 
h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
 sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
 sigs.k8s.io/randfill v1.0.0/go.mod 
h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
-sigs.k8s.io/structured-merge-diff/v4 v4.6.0 
h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc=
-sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod 
h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
 sigs.k8s.io/structured-merge-diff/v6 v6.3.0 
h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
 sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod 
h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
-sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
-sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
 sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
 sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-switch-2.2.16/internal/config/config.go 
new/kubectl-switch-2.3.0/internal/config/config.go
--- old/kubectl-switch-2.2.16/internal/config/config.go 1970-01-01 
01:00:00.000000000 +0100
+++ new/kubectl-switch-2.3.0/internal/config/config.go  2025-10-13 
00:17:55.000000000 +0200
@@ -0,0 +1,146 @@
+package config
+
+import (
+       "fmt"
+       "os"
+       "path/filepath"
+       "strings"
+
+       log "github.com/sirupsen/logrus"
+
+       "github.com/spf13/viper"
+)
+
+// Config holds all configuration for the application
+type Config struct {
+       KubeconfigDir string
+       Kubeconfig    string
+       LogLevel      log.Level
+       LogFormat     log.Formatter
+}
+
+const (
+       // Configuration keys
+       keyKubeconfigDir = "kubeconfig-dir"
+       keyKubeconfig    = "kubeconfig"
+       keyLogLevel      = "log-level"
+       keyLogFormat     = "log-format"
+
+       // Default values
+       defaultLogLevel  = "info"
+       defaultLogFormat = "text"
+)
+
+var (
+       defaultKubeconfigDir = filepath.Join(os.Getenv("HOME"), ".kube", 
"configs/")
+       defaultKubeconfig    = filepath.Join(os.Getenv("HOME"), ".kube", 
"config")
+)
+
+// Init initializes Viper configuration
+func Init() {
+       // Enable reading from environment variables
+       viper.AutomaticEnv()
+
+       // Replace hyphens with underscores in env vars
+       // This allows --kubeconfig-dir to map to KUBECONFIG_DIR
+       viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
+
+       // Set default values
+       viper.SetDefault(keyKubeconfigDir, defaultKubeconfigDir)
+       viper.SetDefault(keyKubeconfig, defaultKubeconfig)
+       viper.SetDefault(keyLogLevel, defaultLogLevel)
+       viper.SetDefault(keyLogFormat, defaultLogFormat)
+}
+
+// Load returns the current configuration
+func Load() (*Config, error) {
+       cfg := &Config{}
+
+       // Parse log level
+       levelStr := viper.GetString(keyLogLevel)
+       level, err := log.ParseLevel(levelStr)
+       if err != nil {
+               return nil, fmt.Errorf("invalid log level: %s", levelStr)
+       }
+       cfg.LogLevel = level
+
+       // Parse log format
+       formatStr := viper.GetString(keyLogFormat)
+       switch strings.ToLower(formatStr) {
+       case "json":
+               cfg.LogFormat = &log.JSONFormatter{}
+       case "text":
+               cfg.LogFormat = &log.TextFormatter{FullTimestamp: true}
+       default:
+               return nil, fmt.Errorf("invalid log format: %s", formatStr)
+       }
+
+       // Expand and validate kubeconfig dir path
+       cfg.KubeconfigDir, err = expandPath(viper.GetString(keyKubeconfigDir))
+       if err != nil {
+               return nil, fmt.Errorf("failed to expand kubeconfig directory 
path: %w", err)
+       }
+       if err := cfg.validateKubeconfigDir(); err != nil {
+               return nil, err
+       }
+
+       // Expand and validate kubeconfig dir path
+       cfg.Kubeconfig, err = expandPath(viper.GetString(keyKubeconfig))
+       if err != nil {
+               return nil, fmt.Errorf("failed to expand kubeconfig path: %w", 
err)
+       }
+       if err := cfg.validateKubeconfig(); err != nil {
+               log.Warnf("Kubeconfig file validation failed: %v", err)
+               log.Warn("You can still switch contexts, but namespace 
operations will not be available")
+       }
+
+       return cfg, nil
+}
+
+// validateKubeconfigDir validates that the kubeconfig directory exists and is 
a directory
+func (c *Config) validateKubeconfigDir() error {
+       info, err := os.Stat(c.KubeconfigDir)
+       if err != nil {
+               if os.IsNotExist(err) {
+                       return fmt.Errorf("kubeconfig directory does not exist: 
%s", c.KubeconfigDir)
+               }
+               return fmt.Errorf("failed to stat kubeconfig directory: %w", 
err)
+       }
+
+       if !info.IsDir() {
+               return fmt.Errorf("kubeconfig directory path is not a 
directory: %s", c.KubeconfigDir)
+       }
+
+       return nil
+}
+
+// validateKubeconfig validates that the kubeconfig file exists and is a file
+func (c *Config) validateKubeconfig() error {
+       info, err := os.Stat(c.Kubeconfig)
+       if err != nil {
+               if os.IsNotExist(err) {
+                       return fmt.Errorf("kubeconfig file does not exist: %s", 
c.Kubeconfig)
+               }
+               return fmt.Errorf("failed to stat kubeconfig file: %w", err)
+       }
+
+       if info.IsDir() {
+               return fmt.Errorf("kubeconfig path is a directory, expected a 
file: %s", c.Kubeconfig)
+       }
+
+       return nil
+}
+
+// expandPath expands a path starting with ~/ to the full home directory path.
+func expandPath(path string) (string, error) {
+       if !strings.HasPrefix(path, "~/") {
+               return path, nil
+       }
+
+       homeDir, err := os.UserHomeDir()
+       if err != nil {
+               return "", fmt.Errorf("failed to get home directory: %w", err)
+       }
+
+       return filepath.Join(homeDir, path[2:]), nil
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-switch-2.2.16/internal/manager/manager.go 
new/kubectl-switch-2.3.0/internal/manager/manager.go
--- old/kubectl-switch-2.2.16/internal/manager/manager.go       1970-01-01 
01:00:00.000000000 +0100
+++ new/kubectl-switch-2.3.0/internal/manager/manager.go        2025-10-13 
00:17:55.000000000 +0200
@@ -0,0 +1,216 @@
+package manager
+
+import (
+       "context"
+       "fmt"
+       "os"
+       "path/filepath"
+
+       log "github.com/sirupsen/logrus"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/client-go/kubernetes"
+       "k8s.io/client-go/tools/clientcmd"
+)
+
+// Manager handles kubeconfig file operations and Kubernetes context switching.
+type Manager struct {
+       kubeconfigPath string
+       backupPath     string
+       kubeconfigDir  string
+       contextMap     map[string]string
+       contextNames   []string
+       namespaceNames []string
+}
+
+// NewManager creates a new kubeconfig Manager instance.
+// It takes the validated configuration paths and loads available contexts.
+func NewManager(kubeconfigPath, kubeconfigDir string) (*Manager, error) {
+       m := &Manager{
+               kubeconfigPath: kubeconfigPath,
+               kubeconfigDir:  kubeconfigDir,
+               backupPath:     kubeconfigPath + ".previous",
+       }
+
+       // Load available contexts from the config directory
+       if err := m.loadContexts(); err != nil {
+               return nil, fmt.Errorf("failed to load contexts: %w", err)
+       }
+
+       // Load available namespaces from the current cluster
+       // This may fail if no kubeconfig is active, which is OK
+       if err := m.loadNamespaces(); err != nil {
+               log.Warnf("Failed to load namespaces: %v", err)
+               log.Warn("Namespace operations will not be available until a 
valid context is selected")
+               m.namespaceNames = []string{} // Initialize to empty slice
+       }
+
+       return m, nil
+}
+
+// GetAllContexts returns the available context names.
+func (m *Manager) GetAllContexts() []string {
+       return m.contextNames
+}
+
+// GetAllNamespaces retrieves all namespaces from the current Kubernetes 
cluster.
+func (m *Manager) GetAllNamespaces() []string {
+       return m.namespaceNames
+}
+
+// SwitchToContext switches to the specified Kubernetes context.
+func (m *Manager) SwitchToContext(contextName string) error {
+       // Find the kubeconfig file containing the desired context
+       contextFilePath, exists := m.contextMap[contextName]
+       if !exists {
+               return fmt.Errorf("context '%s' not found", contextName)
+       }
+
+       // Load the kubeconfig file containing the desired context
+       kubeconfig, err := clientcmd.LoadFromFile(contextFilePath)
+       if err != nil {
+               return fmt.Errorf("failed to load kubeconfig from %s: %w", 
contextFilePath, err)
+       }
+
+       // Update the current context in the loaded kubeconfig
+       kubeconfig.CurrentContext = contextName
+
+       // Backup current config
+       if err := m.backup(); err != nil {
+               log.Warnf("Failed to save current configuration as previous: 
%v", err)
+       }
+
+       // Write updated kubeconfig back to the main kubeconfig file
+       if err := clientcmd.WriteToFile(*kubeconfig, m.kubeconfigPath); err != 
nil {
+               return fmt.Errorf("failed to write kubeconfig: %w", err)
+       }
+
+       return nil
+}
+
+// SwitchToNamespace switches the namespace for the current context.
+func (m *Manager) SwitchToNamespace(namespace string) error {
+       // Parse current kubeconfig
+       kubeconfig, err := clientcmd.LoadFromFile(m.kubeconfigPath)
+       if err != nil {
+               return fmt.Errorf("failed to load current kubeconfig: %w", err)
+       }
+
+       // Update namespace for current context
+       kubeconfig.Contexts[kubeconfig.CurrentContext].Namespace = namespace
+
+       // Backup current config
+       if err := m.backup(); err != nil {
+               log.Warnf("Failed to save current configuration as previous: 
%v", err)
+       }
+
+       // Write updated kubeconfig back to file
+       if err := clientcmd.WriteToFile(*kubeconfig, m.kubeconfigPath); err != 
nil {
+               return fmt.Errorf("failed to write kubeconfig: %w", err)
+       }
+
+       return nil
+}
+
+// Restore swaps the current kubeconfig with the previous backup.
+func (m *Manager) Restore() error {
+       // Read current kubeconfig
+       currentConfig, err := os.ReadFile(m.kubeconfigPath)
+       if err != nil {
+               return fmt.Errorf("failed to read current config: %w", err)
+       }
+
+       // Read backup kubeconfig
+       prevConfig, err := os.ReadFile(m.backupPath)
+       if err != nil {
+               return fmt.Errorf("failed to read previous config: %w", err)
+       }
+
+       // Swap the files
+       if err := os.WriteFile(m.kubeconfigPath, prevConfig, 0o600); err != nil 
{
+               return fmt.Errorf("failed to write current config: %w", err)
+       }
+       if err := os.WriteFile(m.backupPath, currentConfig, 0o600); err != nil {
+               return fmt.Errorf("failed to write previous config: %w", err)
+       }
+
+       return nil
+}
+
+// 
================================================================================================
+// Helper functions
+// 
================================================================================================
+
+// backup backs up the current kubeconfig to config.previous.
+func (m *Manager) backup() error {
+       data, err := os.ReadFile(m.kubeconfigPath)
+       if err != nil {
+               return fmt.Errorf("failed to read current kubeconfig: %w", err)
+       }
+
+       if err := os.WriteFile(m.backupPath, data, 0o600); err != nil {
+               return fmt.Errorf("failed to write previous kubeconfig: %w", 
err)
+       }
+
+       return nil
+}
+
+// loadContexts scans the config directory for kubeconfig files and loads all 
available contexts.
+func (m *Manager) loadContexts() error {
+       m.contextMap = make(map[string]string)
+       m.contextNames = nil
+
+       files, err := os.ReadDir(m.kubeconfigDir)
+       if err != nil {
+               return fmt.Errorf("failed to read config directory: %w", err)
+       }
+
+       for _, file := range files {
+               if file.IsDir() {
+                       continue
+               }
+
+               path := filepath.Join(m.kubeconfigDir, file.Name())
+               kubeconfig, err := clientcmd.LoadFromFile(path)
+               if err != nil {
+                       log.WithField("file", file.Name()).Warnf("Failed to 
parse kubeconfig file: %v", err)
+                       continue
+               }
+
+               for contextName := range kubeconfig.Contexts {
+                       if existingPath, exists := m.contextMap[contextName]; 
exists {
+                               log.Warnf("Duplicate context name '%s' found in 
files:\n  - %s\n  - %s",
+                                       contextName, existingPath, path)
+                               continue
+                       }
+                       m.contextMap[contextName] = path
+                       m.contextNames = append(m.contextNames, contextName)
+               }
+       }
+
+       return nil
+}
+
+// loadNamespaces loads all namespaces from the current Kubernetes cluster.
+func (m *Manager) loadNamespaces() error {
+       config, err := clientcmd.BuildConfigFromFlags("", m.kubeconfigPath)
+       if err != nil {
+               return fmt.Errorf("failed to build config: %w", err)
+       }
+
+       clientset, err := kubernetes.NewForConfig(config)
+       if err != nil {
+               return fmt.Errorf("failed to create clientset: %w", err)
+       }
+
+       namespaces, err := clientset.CoreV1().Namespaces().List(context.TODO(), 
metav1.ListOptions{})
+       if err != nil {
+               return fmt.Errorf("failed to list namespaces: %w", err)
+       }
+
+       m.namespaceNames = make([]string, 0, len(namespaces.Items))
+       for _, ns := range namespaces.Items {
+               m.namespaceNames = append(m.namespaceNames, ns.Name)
+       }
+
+       return nil
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-switch-2.2.16/pkg/kubeconfig/context.go 
new/kubectl-switch-2.3.0/pkg/kubeconfig/context.go
--- old/kubectl-switch-2.2.16/pkg/kubeconfig/context.go 2025-09-13 
20:04:28.000000000 +0200
+++ new/kubectl-switch-2.3.0/pkg/kubeconfig/context.go  1970-01-01 
01:00:00.000000000 +0100
@@ -1,78 +0,0 @@
-package kubeconfig
-
-import (
-       "os"
-       "path/filepath"
-       "strings"
-)
-
-// ContextManager handles operations related to Kubernetes contexts
-type ContextManager struct {
-       kubeconfigManager *Manager
-}
-
-// NewContextManager creates a new context manager
-func NewContextManager(km *Manager) *ContextManager {
-       return &ContextManager{
-               kubeconfigManager: km,
-       }
-}
-
-// GetContextsFromDir gets all contexts from kubeconfig files in a directory
-func (cm *ContextManager) GetContextsFromDir(configDir string) 
(map[string]string, []string, error) {
-       // Handle ~/ in path
-       if strings.HasPrefix(configDir, "~/") {
-               homeDir, err := os.UserHomeDir()
-               if err != nil {
-                       return nil, nil, err
-               }
-               configDir = filepath.Join(homeDir, configDir[2:])
-       }
-
-       return cm.kubeconfigManager.GetContextsFromDir(configDir)
-}
-
-// SwitchContext switches to the specified context
-func (cm *ContextManager) SwitchContext(contextFilePath string, contextName 
string) error {
-       // Ensure the destination directory exists
-       if err := cm.kubeconfigManager.EnsureDir(); err != nil {
-               return err
-       }
-
-       // Switch to the selected context
-       if err := cm.kubeconfigManager.SwitchContext(contextFilePath, 
contextName); err != nil {
-               return err
-       }
-
-       return nil
-}
-
-// ValidateConfigDir ensures the kubeconfig directory is valid and available
-func (cm *ContextManager) ValidateConfigDir(configDir string) (string, error) {
-       if configDir == "" {
-               configDir = os.Getenv("KUBECONFIG_DIR")
-               if configDir == "" {
-                       return "", ErrNoConfigDir
-               }
-       }
-
-       // Handle ~/ in path
-       if strings.HasPrefix(configDir, "~/") {
-               homeDir, err := os.UserHomeDir()
-               if err != nil {
-                       return "", err
-               }
-               configDir = filepath.Join(homeDir, configDir[2:])
-       }
-
-       // Check if the directory exists
-       info, err := os.Stat(configDir)
-       if err != nil {
-               return "", err
-       }
-       if !info.IsDir() {
-               return "", ErrNotADirectory
-       }
-
-       return configDir, nil
-}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-switch-2.2.16/pkg/kubeconfig/errors.go 
new/kubectl-switch-2.3.0/pkg/kubeconfig/errors.go
--- old/kubectl-switch-2.2.16/pkg/kubeconfig/errors.go  2025-09-13 
20:04:28.000000000 +0200
+++ new/kubectl-switch-2.3.0/pkg/kubeconfig/errors.go   1970-01-01 
01:00:00.000000000 +0100
@@ -1,12 +0,0 @@
-package kubeconfig
-
-import (
-       "errors"
-)
-
-// Common errors for kubeconfig operations
-var (
-       ErrNoConfigDir      = errors.New("kubeconfig directory not provided, 
please provide the directory containing kubeconfig files via the --config-dir 
flag or KUBECONFIG_DIR environment variable")
-       ErrNotADirectory    = errors.New("the provided path is not a directory")
-       ErrNoPreviousConfig = errors.New("no previous configuration found")
-)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-switch-2.2.16/pkg/kubeconfig/kubeconfig.go 
new/kubectl-switch-2.3.0/pkg/kubeconfig/kubeconfig.go
--- old/kubectl-switch-2.2.16/pkg/kubeconfig/kubeconfig.go      2025-09-13 
20:04:28.000000000 +0200
+++ new/kubectl-switch-2.3.0/pkg/kubeconfig/kubeconfig.go       1970-01-01 
01:00:00.000000000 +0100
@@ -1,185 +0,0 @@
-package kubeconfig
-
-import (
-       "os"
-       "path/filepath"
-
-       log "github.com/sirupsen/logrus"
-       "k8s.io/client-go/tools/clientcmd"
-)
-
-// Manager handles operations related to kubeconfig files
-type Manager struct{}
-
-// NewManager creates a new kubeconfig manager
-func NewManager() *Manager {
-       return &Manager{}
-}
-
-// GetPath returns the path to the current kubeconfig file
-func (m *Manager) GetPath() string {
-       path := os.Getenv("KUBECONFIG")
-       if path == "" {
-               homeDir, err := os.UserHomeDir()
-               if err != nil {
-                       log.Fatalf("Failed to determine home directory: %v", 
err)
-               }
-               path = filepath.Join(homeDir, ".kube", "config")
-       }
-       return path
-}
-
-// GetPreviousPath returns the path to the previous kubeconfig file
-func (m *Manager) GetPreviousPath() string {
-       return filepath.Join(filepath.Dir(m.GetPath()), "config.previous")
-}
-
-// SaveCurrent backs up the current kubeconfig file
-func (m *Manager) SaveCurrent() error {
-       kubeconfigPath := m.GetPath()
-       if _, err := os.Stat(kubeconfigPath); os.IsNotExist(err) {
-               return err
-       }
-
-       prevPath := m.GetPreviousPath()
-       data, err := os.ReadFile(kubeconfigPath)
-       if err != nil {
-               return err
-       }
-
-       return os.WriteFile(prevPath, data, 0o600)
-}
-
-// SwitchToPrevious switches back to the previous Kubernetes config
-func (m *Manager) SwitchToPrevious() error {
-       kubeconfigPath := m.GetPath()
-       prevPath := m.GetPreviousPath()
-
-       // Check if previous config exists
-       if _, err := os.Stat(prevPath); os.IsNotExist(err) {
-               return ErrNoPreviousConfig
-       }
-
-       // Swap the current and previous configs
-       tempPath := filepath.Join(filepath.Dir(kubeconfigPath), "config.temp")
-
-       // Read current config
-       currentConfig, err := os.ReadFile(kubeconfigPath)
-       if err != nil {
-               return err
-       }
-
-       // Read previous config
-       prevConfig, err := os.ReadFile(prevPath)
-       if err != nil {
-               return err
-       }
-
-       // Write current to temp
-       if err := os.WriteFile(tempPath, currentConfig, 0o600); err != nil {
-               return err
-       }
-
-       // Write previous to current
-       if err := os.WriteFile(kubeconfigPath, prevConfig, 0o600); err != nil {
-               return err
-       }
-
-       // Write temp to previous
-       if err := os.WriteFile(prevPath, currentConfig, 0o600); err != nil {
-               return err
-       }
-
-       // Remove temp file
-       if err := os.Remove(tempPath); err != nil {
-               log.Warnf("Failed to remove temporary file: %v", err)
-       }
-
-       return nil
-}
-
-// EnsureDir ensures that the directory for the kubeconfig file exists
-func (m *Manager) EnsureDir() error {
-       destDir := filepath.Dir(m.GetPath())
-       return os.MkdirAll(destDir, 0o755)
-}
-
-// GetContextsFromDir gets all contexts from kubeconfig files in a directory
-func (m *Manager) GetContextsFromDir(configDir string) (map[string]string, 
[]string, error) {
-       contextMap := make(map[string]string)
-       var contextNames []string
-
-       files, err := os.ReadDir(configDir)
-       if err != nil {
-               return nil, nil, err
-       }
-
-       for _, file := range files {
-               if file.IsDir() {
-                       continue
-               }
-
-               ext := filepath.Ext(file.Name())
-               if ext != ".yaml" && ext != ".yml" {
-                       continue
-               }
-
-               path := filepath.Join(configDir, file.Name())
-               kubeconfig, err := clientcmd.LoadFromFile(path)
-               if err != nil {
-                       log.WithFields(log.Fields{"file": 
file.Name()}).Warnf("Failed to parse kubeconfig file: %v", err)
-                       continue
-               }
-
-               for contextName := range kubeconfig.Contexts {
-                       if _, exists := contextMap[contextName]; exists {
-                               log.Warnf("Duplicate context name '%s' found in 
files:\n- %s\n- %s", contextName, contextMap[contextName], path)
-                               continue
-                       }
-                       contextMap[contextName] = path
-                       contextNames = append(contextNames, contextName)
-               }
-       }
-
-       return contextMap, contextNames, nil
-}
-
-// SwitchContext switches to the specified context
-func (m *Manager) SwitchContext(contextFilePath string, contextName string) 
error {
-       // Save the current config as the previous config
-       if err := m.SaveCurrent(); err != nil {
-               log.Warnf("Failed to save current configuration as previous: 
%v", err)
-       }
-
-       // Load the kubeconfig file for the selected context
-       kubeconfig, err := clientcmd.LoadFromFile(contextFilePath)
-       if err != nil {
-               return err
-       }
-
-       // Update the current context
-       kubeconfig.CurrentContext = contextName
-
-       // Write the updated kubeconfig back to the file
-       return clientcmd.WriteToFile(*kubeconfig, m.GetPath())
-}
-
-// SwitchNamespace switches the namespace in the current context
-func (m *Manager) SwitchNamespace(namespace string) error {
-       // Save the current config as the previous config
-       if err := m.SaveCurrent(); err != nil {
-               log.Warnf("Failed to save current configuration as previous: 
%v", err)
-       }
-
-       // Load the kubeconfig file
-       kubeconfig, err := clientcmd.LoadFromFile(m.GetPath())
-       if err != nil {
-               return err
-       }
-
-       // Update the namespace in the current context
-       kubeconfig.Contexts[kubeconfig.CurrentContext].Namespace = namespace
-
-       // Write the updated kubeconfig back to the file
-       return clientcmd.WriteToFile(*kubeconfig, m.GetPath())
-}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-switch-2.2.16/pkg/kubeconfig/namespace.go 
new/kubectl-switch-2.3.0/pkg/kubeconfig/namespace.go
--- old/kubectl-switch-2.2.16/pkg/kubeconfig/namespace.go       2025-09-13 
20:04:28.000000000 +0200
+++ new/kubectl-switch-2.3.0/pkg/kubeconfig/namespace.go        1970-01-01 
01:00:00.000000000 +0100
@@ -1,56 +0,0 @@
-package kubeconfig
-
-import (
-       "context"
-
-       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-       "k8s.io/client-go/kubernetes"
-       "k8s.io/client-go/tools/clientcmd"
-)
-
-// NamespaceManager handles operations related to Kubernetes namespaces
-type NamespaceManager struct {
-       kubeconfigManager *Manager
-}
-
-// NewNamespaceManager creates a new namespace manager
-func NewNamespaceManager(km *Manager) *NamespaceManager {
-       return &NamespaceManager{
-               kubeconfigManager: km,
-       }
-}
-
-// GetNamespaces gets all namespaces from the current context
-func (nm *NamespaceManager) GetNamespaces() ([]string, error) {
-       kubeconfigPath := nm.kubeconfigManager.GetPath()
-
-       // Build the Kubernetes client configuration
-       config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath)
-       if err != nil {
-               return nil, err
-       }
-
-       // Create the Kubernetes clientset
-       clientset, err := kubernetes.NewForConfig(config)
-       if err != nil {
-               return nil, err
-       }
-
-       // Get all namespaces
-       var namespaceNames []string
-       namespaces, err := clientset.CoreV1().Namespaces().List(context.TODO(), 
metav1.ListOptions{})
-       if err != nil {
-               return nil, err
-       }
-
-       for _, ns := range namespaces.Items {
-               namespaceNames = append(namespaceNames, ns.Name)
-       }
-
-       return namespaceNames, nil
-}
-
-// SwitchNamespace switches the namespace in the current context
-func (nm *NamespaceManager) SwitchNamespace(namespace string) error {
-       return nm.kubeconfigManager.SwitchNamespace(namespace)
-}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kubectl-switch-2.2.16/scripts/test.sh 
new/kubectl-switch-2.3.0/scripts/test.sh
--- old/kubectl-switch-2.2.16/scripts/test.sh   2025-09-13 20:04:28.000000000 
+0200
+++ new/kubectl-switch-2.3.0/scripts/test.sh    2025-10-13 00:17:55.000000000 
+0200
@@ -2,27 +2,10 @@
 set -e
 
 # Create config directory if it doesn't exist
-export KUBECONFIG_DIR="./configs/"
+export KUBECONFIG="./test/config"
+export KUBECONFIG_DIR="./test/configs/"
 mkdir -p $KUBECONFIG_DIR
 
-cleanup() {
-    echo "Performing cleanup..."
-
-    # Stop and delete minikube clusters
-    echo "Stopping and deleting test-cluster-1..."
-    KUBECONFIG=./configs/cluster1.yaml minikube stop -p test-cluster-1 || true
-    KUBECONFIG=./configs/cluster1.yaml minikube delete -p test-cluster-1 || 
true
-
-    echo "Stopping and deleting test-cluster-2..."
-    KUBECONFIG=./configs/cluster2.yaml minikube stop -p test-cluster-2 || true
-    KUBECONFIG=./configs/cluster2.yaml minikube delete -p test-cluster-2 || 
true
-
-    # Remove config directory
-    echo "Removing configs directory..."
-    rm -rf ./configs/
-
-    echo "Cleanup completed."
-}
 
 setup() {
     echo "Performing setup..."
@@ -33,13 +16,34 @@
 
     # Set up Kubernetes clusters
     echo "Setting up test clusters..."
-    KUBECONFIG=./configs/cluster1.yaml minikube start -p test-cluster-1 &
-    KUBECONFIG=./configs/cluster2.yaml minikube start -p test-cluster-2 &
+    KUBECONFIG=$KUBECONFIG_DIR/cluster1.yaml minikube start -p test-cluster-1 &
+    KUBECONFIG=$KUBECONFIG_DIR/cluster2.yaml minikube start -p test-cluster-2 &
     wait
 
     echo "Setup completed."
 }
 
+cleanup() {
+    echo "Performing cleanup..."
+
+    # Stop and delete minikube clusters
+    echo "Stopping and deleting test-cluster-1..."
+    KUBECONFIG=$KUBECONFIG_DIR/cluster1.yaml minikube stop -p test-cluster-1 
|| true
+    KUBECONFIG=$KUBECONFIG_DIR/cluster1.yaml minikube delete -p test-cluster-1 
|| true
+
+    echo "Stopping and deleting test-cluster-2..."
+    KUBECONFIG=$KUBECONFIG_DIR/cluster2.yaml minikube stop -p test-cluster-2 
|| true
+    KUBECONFIG=$KUBECONFIG_DIR/cluster2.yaml minikube delete -p test-cluster-2 
|| true
+
+    # Remove config directory
+    echo "Removing test directory..."
+    rm -rf tests/
+    rm kubectl-switch
+
+    echo "Cleanup completed."
+}
+
+
 run_tests() {
     echo "Running tests..."
 

++++++ kubectl-switch.obsinfo ++++++
--- /var/tmp/diff_new_pack.JWGj7P/_old  2025-10-13 15:37:03.645565406 +0200
+++ /var/tmp/diff_new_pack.JWGj7P/_new  2025-10-13 15:37:03.709568096 +0200
@@ -1,5 +1,5 @@
 name: kubectl-switch
-version: 2.2.16
-mtime: 1757786668
-commit: 6bb6ac390fbf54d2f17de8dea1de44928fe632bf
+version: 2.3.0
+mtime: 1760307475
+commit: 1339ef9aaa2e74313d2f67988d4ac6ec08de852a
 

++++++ vendor.tar.gz ++++++
/work/SRC/openSUSE:Factory/kubectl-switch/vendor.tar.gz 
/work/SRC/openSUSE:Factory/.kubectl-switch.new.18484/vendor.tar.gz differ: char 
13, line 1

Reply via email to