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>
t.Setenv is the modern standard Go testing utility (since Go 1.17) that automatically restores environment variables when the test completes. This replaces the custom testutil.WithEnvVar function which manually saved and restored env var state.
The testutil.go file is deleted as it only contained WithEnvVar. The testutil package remains for its other utilities like KubeconfigBuilder.
This change addresses eight key improvements to the kubectx/kubens codebase:
Resource Management Fixes:
- Fix use-after-close bugs where Kubeconfig was accessed after Close()
- Fix resource leaks on error paths by ensuring defer kc.Close() is called
- Fix YAML encoder not being closed after Encode(), causing buffered data loss
API Design Improvements:
- Change ContextNames() to return ([]string, error) instead of silently returning
nil on error, making parse failures distinguishable from empty results
- Change GetCurrentContext() to return (string, error) instead of returning ""
for both "not set" and parse error cases
- Update all 16 call sites across cmd/kubectx and cmd/kubens packages to handle
the new error returns while preserving backward-compatible behavior
Error Handling:
- Add explicit error handling for printer.Success() calls in 5+ locations
by prefixing unchecked calls with _ =
Performance:
- Add slice pre-allocation in namespace list pagination using slices.Grow()
before append loops, reducing allocations when fetching 500+ item batches
All changes maintain backward compatibility for missing kubeconfig keys while
improving error transparency and resource safety.
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>