When kubens needs to query the Kubernetes API (e.g. to check if a
namespace exists), it builds a REST client from the in-memory
kubeconfig bytes using clientcmd.RESTConfigFromKubeConfig(). This
function has no knowledge of the kubeconfig file's location on disk,
so it cannot resolve relative paths in exec credential plugin commands
(e.g. `command: ../scripts/get-token.sh`). This causes a "no such file
or directory" error for users whose kubeconfig uses relative paths in
exec-based authentication.
The fix threads the kubeconfig file path through a new PathHinter
optional interface on ReadWriteResetCloser. When a file path is
available, newKubernetesClientSet now uses
clientcmd.NewNonInteractiveDeferredLoadingClientConfig with
ExplicitPath, which resolves relative paths relative to the kubeconfig
file's directory — matching kubectl's own behavior. The old
bytes-based fallback is preserved for in-memory configs (e.g. tests).
Fixes#488
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously, kubectx would error with "multiple files in KUBECONFIG are
currently not supported" when KUBECONFIG contained colon-separated paths.
This is a common setup where users maintain separate kubeconfig files for
different clusters/environments.
This change evolves the internal Kubeconfig struct from holding a single
file to a slice of file entries, matching kubectl's merge semantics:
- Reading current-context: first file with a non-empty value wins
- Writing current-context: always written to the first file
- Listing contexts: merged from all files, first occurrence wins for
duplicate names
- Modifying a context (delete/rename/set-namespace): written to the
file that owns that context
- Missing files in the KUBECONFIG list are silently skipped (matching
kubectl behavior), but permission errors are propagated
The Loader interface already returned []ReadWriteResetCloser, so all
public method signatures remain unchanged — zero modifications needed
in cmd/kubectx/ or cmd/kubens/ callers.
Fixes#485Fixes#211
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Modernize the codebase to use idiomatic Go 1.25 patterns, removing deprecated APIs and reducing external dependencies.
- Replace deprecated `io/ioutil` with `os.ReadFile`, `os.WriteFile`, `os.MkdirTemp`, `os.CreateTemp`
- Replace `interface{}` with `any` (Go 1.18+)
- Remove `github.com/pkg/errors` dependency entirely, using stdlib `fmt.Errorf` with `%w` and `errors.New`
- Use `errors.As()` instead of direct type assertions on error values
- Use `strings.Cut()` for delimiter parsing instead of `strings.Split` + length check
- Use `slices.Contains()` for linear search in `ContextExists()`
- Use `t.Setenv()` and `t.TempDir()` in tests instead of manual env save/restore and temp dir cleanup
- Delete unused `internal/testutil/tempfile.go` helper
- Update GitHub Actions CI and release workflows from Go 1.22 to 1.25
Net result: -70 lines, 1 fewer external dependency.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>