From 8c87a6238a6f853d692c9119d135f175215944b0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 00:57:04 +0000 Subject: [PATCH 1/3] Initial plan From d54065b0e0ce726eeffae75213c99b7ebbc51a72 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 01:00:22 +0000 Subject: [PATCH 2/3] feat: add kubeconfig and context CLI flags Agent-Logs-Url: https://github.com/rawkode/kubectl-ditto/sessions/73e5cad3-cf0a-481f-b88f-9499a37bffed Co-authored-by: rawkode <145816+rawkode@users.noreply.github.com> --- README.md | 5 ++++- src/cli.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 23 ++++++++++++++++++++++- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2f39d74..4f66f4a 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,9 @@ kubectl ditto deployment -n my-namespace my-app --full # Suppress description comments kubectl ditto deployment -n my-namespace my-app --no-comments + +# Use a specific kubeconfig or context +kubectl ditto deployment my-app --kubeconfig ~/.kube/prod-config --context production ``` ## Install @@ -65,7 +68,7 @@ The binary `kubectl-ditto` is placed on your PATH. kubectl automatically discove ## Requirements -- A running Kubernetes cluster (uses your current kubeconfig context) +- A running Kubernetes cluster (uses your current kubeconfig context unless `--kubeconfig` or `--context` is provided) - CRDs must be installed for custom resources ## Release diff --git a/src/cli.rs b/src/cli.rs index 0868ef5..b178319 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use clap::Parser; /// Generate YAML for any Kubernetes resource or CRD using cluster schema and smart defaults. @@ -14,6 +16,14 @@ pub struct Args { #[arg(short, long)] pub namespace: Option, + /// Path to the kubeconfig file to use + #[arg(long)] + pub kubeconfig: Option, + + /// Name of the kubeconfig context to use + #[arg(long)] + pub context: Option, + /// Output only required fields (skip optional fields with defaults) #[arg(long)] pub minimal: bool, @@ -34,3 +44,37 @@ pub struct Args { #[arg(long)] pub dump_schema: bool, } + +#[cfg(test)] +mod tests { + use super::Args; + use clap::Parser; + use std::path::PathBuf; + + #[test] + fn parses_kubeconfig_and_context_flags() { + let args = Args::parse_from([ + "kubectl-ditto", + "--kubeconfig", + "/tmp/kubeconfig", + "--context", + "production", + "deployment", + "my-app", + ]); + + assert_eq!(args.kubeconfig, Some(PathBuf::from("/tmp/kubeconfig"))); + assert_eq!(args.context.as_deref(), Some("production")); + assert_eq!(args.resource.as_deref(), Some("deployment")); + assert_eq!(args.name.as_deref(), Some("my-app")); + } + + #[test] + fn parses_context_without_custom_kubeconfig() { + let args = Args::parse_from(["kubectl-ditto", "--context", "staging", "deployment"]); + + assert_eq!(args.kubeconfig, None); + assert_eq!(args.context.as_deref(), Some("staging")); + assert_eq!(args.resource.as_deref(), Some("deployment")); + } +} diff --git a/src/main.rs b/src/main.rs index d34ab8e..3bb232b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,27 @@ mod schema; use anyhow::Result; use clap::Parser; +use kube::config::{KubeConfigOptions, Kubeconfig}; + +async fn client_for_args(args: &cli::Args) -> Result { + if args.kubeconfig.is_none() && args.context.is_none() { + return Ok(kube::Client::try_default().await?); + } + + let options = KubeConfigOptions { + context: args.context.clone(), + ..Default::default() + }; + + let config = if let Some(path) = &args.kubeconfig { + let kubeconfig = Kubeconfig::read_from(path)?; + kube::Config::from_custom_kubeconfig(kubeconfig, &options).await? + } else { + kube::Config::from_kubeconfig(&options).await? + }; + + Ok(kube::Client::try_from(config)?) +} #[tokio::main] async fn main() -> Result<()> { @@ -19,7 +40,7 @@ async fn main() -> Result<()> { } }; - let client = kube::Client::try_default().await?; + let client = client_for_args(&args).await?; // 1. Resolve the resource type (dynamic short names from API server) let resolved = discovery::resolve_resource(&client, &resource).await?; From cac764356965edbb3ed12aa2dcb85dae18713776 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 01:03:50 +0000 Subject: [PATCH 3/3] test: align CLI test naming with Rust conventions Agent-Logs-Url: https://github.com/rawkode/kubectl-ditto/sessions/73e5cad3-cf0a-481f-b88f-9499a37bffed Co-authored-by: rawkode <145816+rawkode@users.noreply.github.com> --- src/cli.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index b178319..11f5276 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -52,7 +52,7 @@ mod tests { use std::path::PathBuf; #[test] - fn parses_kubeconfig_and_context_flags() { + fn test_parses_kubeconfig_and_context_flags() { let args = Args::parse_from([ "kubectl-ditto", "--kubeconfig", @@ -70,7 +70,7 @@ mod tests { } #[test] - fn parses_context_without_custom_kubeconfig() { + fn test_parses_context_without_custom_kubeconfig() { let args = Args::parse_from(["kubectl-ditto", "--context", "staging", "deployment"]); assert_eq!(args.kubeconfig, None);