From e3acf7efc7ffb6714769c443a37ba42e70ad6911 Mon Sep 17 00:00:00 2001 From: Hazem Nureldin Date: Tue, 5 May 2026 08:31:26 +0100 Subject: [PATCH 01/15] created a this file to test workflow cli --- examples/conventional-templates/hello_cli_test | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 examples/conventional-templates/hello_cli_test diff --git a/examples/conventional-templates/hello_cli_test b/examples/conventional-templates/hello_cli_test new file mode 100644 index 000000000..332c07f96 --- /dev/null +++ b/examples/conventional-templates/hello_cli_test @@ -0,0 +1,18 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: hello-world-running- +spec: + entrypoint: hello-sleep + templates: + - name: hello-sleep + container: + image: alpine:3.18 + command: [sh, -c] + args: + - | + echo "Hello World from workflows CLI" + echo "Sleeping for 20 seconds... Zz... ZZzzz.." + sleep 20 + echo "Done" + From f8f776f14e46a81034940bad002a47c521179c31 Mon Sep 17 00:00:00 2001 From: Hazem Nureldin Date: Tue, 5 May 2026 10:02:19 +0100 Subject: [PATCH 02/15] renamed the file --- .../conventional-templates/hello_cli_test | 18 ------------- .../hello_cli_test.yaml | 25 +++++++++++++++++++ 2 files changed, 25 insertions(+), 18 deletions(-) delete mode 100644 examples/conventional-templates/hello_cli_test create mode 100644 examples/conventional-templates/hello_cli_test.yaml diff --git a/examples/conventional-templates/hello_cli_test b/examples/conventional-templates/hello_cli_test deleted file mode 100644 index 332c07f96..000000000 --- a/examples/conventional-templates/hello_cli_test +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Workflow -metadata: - generateName: hello-world-running- -spec: - entrypoint: hello-sleep - templates: - - name: hello-sleep - container: - image: alpine:3.18 - command: [sh, -c] - args: - - | - echo "Hello World from workflows CLI" - echo "Sleeping for 20 seconds... Zz... ZZzzz.." - sleep 20 - echo "Done" - diff --git a/examples/conventional-templates/hello_cli_test.yaml b/examples/conventional-templates/hello_cli_test.yaml new file mode 100644 index 000000000..6e2615b77 --- /dev/null +++ b/examples/conventional-templates/hello_cli_test.yaml @@ -0,0 +1,25 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + Name: hello-world-cli-test + labels: + workflows.diamond.ac.uk/science-group-examples: "true" + annotations: + workflows.argoproj.io/title: Example + workflows.argoproj.io/description: | + This workflow is used to test the workflows CLI by running + a simple Hello World example. + workflows.diamond.ac.uk/repository: "https://github.com/DiamondLightSource/workflows" +spec: + entrypoint: hello-sleep + templates: + - name: hello-sleep + container: + image: alpine:3.18 + command: [sh, -c] + args: + - | + echo "Hello World from workflows CLI" + echo "Sleeping for 20 seconds... Zz... ZZzzz.." + sleep 20 + echo "Done" From 5b8952eb3714f479ee7219f2b947e833d0db1545 Mon Sep 17 00:00:00 2001 From: Hazem Nureldin Date: Tue, 5 May 2026 10:09:03 +0100 Subject: [PATCH 03/15] adjusted the metadata --- examples/conventional-templates/hello_cli_test.yaml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/conventional-templates/hello_cli_test.yaml b/examples/conventional-templates/hello_cli_test.yaml index 6e2615b77..8c9c924b4 100644 --- a/examples/conventional-templates/hello_cli_test.yaml +++ b/examples/conventional-templates/hello_cli_test.yaml @@ -1,14 +1,12 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Workflow metadata: - Name: hello-world-cli-test + name: hello-world-cli-test labels: workflows.diamond.ac.uk/science-group-examples: "true" annotations: workflows.argoproj.io/title: Example workflows.argoproj.io/description: | - This workflow is used to test the workflows CLI by running - a simple Hello World example. + This workflow is used to test the workflows CLI + with a simple long-running Hello World example. workflows.diamond.ac.uk/repository: "https://github.com/DiamondLightSource/workflows" spec: entrypoint: hello-sleep From 820e4ed9457e01c5b8191fcb1d1bd8ca352b44ed Mon Sep 17 00:00:00 2001 From: Hazem Nureldin Date: Tue, 5 May 2026 10:24:32 +0100 Subject: [PATCH 04/15] added the lines at top --- examples/conventional-templates/hello_cli_test.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/conventional-templates/hello_cli_test.yaml b/examples/conventional-templates/hello_cli_test.yaml index 8c9c924b4..2bd7a3a17 100644 --- a/examples/conventional-templates/hello_cli_test.yaml +++ b/examples/conventional-templates/hello_cli_test.yaml @@ -1,3 +1,6 @@ + +apiVersion: argoproj.io/v1alpha1 +kind: Workflow metadata: name: hello-world-cli-test labels: From 4baa2ee3ba0839124668eedeaaa07b3632bd25a6 Mon Sep 17 00:00:00 2001 From: Hazem Nureldin Date: Tue, 5 May 2026 16:39:24 +0100 Subject: [PATCH 05/15] changes in generate_template_repo & copy_directory in order to validate inputs --- workflows-cli/src/create/mod.rs | 157 +++++++++++++++++++++----------- 1 file changed, 105 insertions(+), 52 deletions(-) diff --git a/workflows-cli/src/create/mod.rs b/workflows-cli/src/create/mod.rs index 799e0ddcd..ed270cc91 100644 --- a/workflows-cli/src/create/mod.rs +++ b/workflows-cli/src/create/mod.rs @@ -18,42 +18,85 @@ pub fn create(args: CreateArgs) { }; } -fn generate_template_repo(args: &CreateArgs, prompt_fn: fn(&str) -> bool) -> Result<(), String> { +fn generate_template_repo( + args: &CreateArgs, + prompt_fn: fn(&str) -> bool, +) -> Result<(), String> { + // Reject workflow YAML files + if args.name.ends_with(".yaml") || args.name.ends_with(".yml") { + return Err(format!( + "Invalid name '{}': create expects a directory name, not a workflow file", + args.name + )); + } + let root_path = Path::new(&args.name); - println!("Generating Template Repo: {:?}", args.name); - let new_dir = fs::create_dir(root_path); - match new_dir { - Ok(_) => println!("Created directory: {}", args.name), - Err(e) => { - let err = format!("Failed to create directory {}: {}", args.name, e); - return Err(err); + println!("Generating Template Repo: {}", root_path.display()); + + // Create root directory safely + if let Err(e) = fs::create_dir(root_path) { + if e.kind() != std::io::ErrorKind::AlreadyExists { + return Err(format!( + "Failed to create directory {}: {}", + root_path.display(), + e + )); } + } else { + println!("Created directory: {}", root_path.display()); + } + + let workflows_home = Path::new(&args.workflows_home); + + let conventional_src = + workflows_home.join("template-boilerplate/conventional-templates"); + let helm_src = + workflows_home.join("template-boilerplate/helm-based-templates"); + + // Validate sources BEFORE prompting + if !conventional_src.exists() { + return Err(format!( + "Missing conventional template source directory: {}", + conventional_src.display() + )); + } + + if !helm_src.exists() { + return Err(format!( + "Missing helm template source directory: {}", + helm_src.display() + )); } let conventional_dest = root_path.join("conventional-templates"); let helm_dest = root_path.join("helm-based-templates"); - let workflow_home_path = Path::new(&args.workflows_home); - let conventional_src = workflow_home_path.join("template-boilerplate/conventional-templates"); - let helm_src = workflow_home_path.join("template-boilerplate/helm-based-templates"); - if !args.manifest { - if prompt_fn("Would you like to store conventional WorkflowTemplate manifests? (y/n)") { - copy_directory(&conventional_src, &conventional_dest); - } + // Conventional templates + let include_conventional = if args.manifest { + true } else { - copy_directory(&conventional_src, &conventional_dest); + prompt_fn("Would you like to store conventional WorkflowTemplate manifests? (y/n)") + }; + + if include_conventional { + copy_directory(&conventional_src, &conventional_dest)?; } - if !args.helm { - if prompt_fn("Would you like to store helm-based WorkflowTemplates? (y/n)") { - copy_directory(&helm_src, &helm_dest); - } + // Helm templates + let include_helm = if args.helm { + true } else { - copy_directory(&helm_src, &helm_dest); + prompt_fn("Would you like to store helm-based WorkflowTemplates? (y/n)") + }; + + if include_helm { + copy_directory(&helm_src, &helm_dest)?; } + Ok(()) } + fn prompt(message: &str) -> bool { println!("{message}"); let mut selection = String::new(); @@ -72,42 +115,52 @@ fn prompt(message: &str) -> bool { } } -fn copy_directory(src: &PathBuf, dest: &PathBuf) { - if let Err(e) = fs::create_dir_all(dest) { - eprintln!( +fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { + if !src.exists() { + return Err(format!("Source directory does not exist: {}", src.display())); + } + + fs::create_dir_all(dest).map_err(|e| { + format!( "Failed to create destination directory {}: {}", dest.display(), e - ); - return; - } - if let Ok(entries) = fs::read_dir(src) { - for entry in entries.flatten() { - let src_path = entry.path(); - let dest_path = dest.join(entry.file_name()); - - if src_path.is_symlink() { - if let Ok(link_target) = fs::read_link(&src_path) { - #[cfg(unix)] - if let Err(e) = fs_sym::symlink(&link_target, &dest_path) { - eprintln!("Failed to create symlink {}: {}", dest_path.display(), e); - } - } - } else if src_path.is_dir() { - if let Err(e) = fs::create_dir_all(&dest_path) { - eprintln!("Failed to create directory {}: {}", dest_path.display(), e); - continue; - } - copy_directory(&src_path, &dest_path); - } else if src_path.is_file() - && let Err(e) = fs::copy(&src_path, &dest_path) - { - eprintln!("Failed to copy {}: {}", src_path.display(), e); - } + ) + })?; + + for entry in fs::read_dir(src) + .map_err(|e| format!("Failed to read source folder {}: {}", src.display(), e))? + { + let entry = entry.map_err(|e| e.to_string())?; + let src_path = entry.path(); + let dest_path = dest.join(entry.file_name()); + + if src_path.is_symlink() { + let target = fs::read_link(&src_path) + .map_err(|e| format!("Failed to read symlink {}: {}", src_path.display(), e))?; + + #[cfg(unix)] + fs_sym::symlink(&target, &dest_path).map_err(|e| { + format!( + "Failed to create symlink {}: {}", + dest_path.display(), + e + ) + })?; + } else if src_path.is_dir() { + copy_directory(&src_path, &dest_path)?; + } else if src_path.is_file() { + fs::copy(&src_path, &dest_path).map_err(|e| { + format!( + "Failed to copy file {}: {}", + src_path.display(), + e + ) + })?; } - } else { - eprintln!("Failed to read source folder: {}", src.display()); } + + Ok(()) } #[cfg(test)] From 07090f0bfe52e9340e819e7bb31299ec592f1c02 Mon Sep 17 00:00:00 2001 From: Hazem Nureldin Date: Thu, 7 May 2026 16:27:28 +0100 Subject: [PATCH 06/15] Fix workflows create: validate input and fail on missing boilerplate --- workflows-cli/src/create/mod.rs | 48 +++++++++------------------------ 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/workflows-cli/src/create/mod.rs b/workflows-cli/src/create/mod.rs index ed270cc91..92f4d4b57 100644 --- a/workflows-cli/src/create/mod.rs +++ b/workflows-cli/src/create/mod.rs @@ -22,7 +22,7 @@ fn generate_template_repo( args: &CreateArgs, prompt_fn: fn(&str) -> bool, ) -> Result<(), String> { - // Reject workflow YAML files + // ✅ Reject workflow files if args.name.ends_with(".yaml") || args.name.ends_with(".yml") { return Err(format!( "Invalid name '{}': create expects a directory name, not a workflow file", @@ -33,18 +33,11 @@ fn generate_template_repo( let root_path = Path::new(&args.name); println!("Generating Template Repo: {}", root_path.display()); - // Create root directory safely - if let Err(e) = fs::create_dir(root_path) { - if e.kind() != std::io::ErrorKind::AlreadyExists { - return Err(format!( - "Failed to create directory {}: {}", - root_path.display(), - e - )); - } - } else { - println!("Created directory: {}", root_path.display()); - } + fs::create_dir(root_path).map_err(|e| { + format!("Failed to create directory {}: {}", root_path.display(), e) + })?; + + println!("Created directory: {}", root_path.display()); let workflows_home = Path::new(&args.workflows_home); @@ -53,14 +46,13 @@ fn generate_template_repo( let helm_src = workflows_home.join("template-boilerplate/helm-based-templates"); - // Validate sources BEFORE prompting + // Validate sources BEFORE asking user anything if !conventional_src.exists() { return Err(format!( "Missing conventional template source directory: {}", conventional_src.display() )); } - if !helm_src.exists() { return Err(format!( "Missing helm template source directory: {}", @@ -71,7 +63,6 @@ fn generate_template_repo( let conventional_dest = root_path.join("conventional-templates"); let helm_dest = root_path.join("helm-based-templates"); - // Conventional templates let include_conventional = if args.manifest { true } else { @@ -82,7 +73,6 @@ fn generate_template_repo( copy_directory(&conventional_src, &conventional_dest)?; } - // Helm templates let include_helm = if args.helm { true } else { @@ -97,6 +87,7 @@ fn generate_template_repo( } + fn prompt(message: &str) -> bool { println!("{message}"); let mut selection = String::new(); @@ -116,16 +107,8 @@ fn prompt(message: &str) -> bool { } fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { - if !src.exists() { - return Err(format!("Source directory does not exist: {}", src.display())); - } - fs::create_dir_all(dest).map_err(|e| { - format!( - "Failed to create destination directory {}: {}", - dest.display(), - e - ) + format!("Failed to create destination directory {}: {}", dest.display(), e) })?; for entry in fs::read_dir(src) @@ -141,21 +124,13 @@ fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { #[cfg(unix)] fs_sym::symlink(&target, &dest_path).map_err(|e| { - format!( - "Failed to create symlink {}: {}", - dest_path.display(), - e - ) + format!("Failed to create symlink {}: {}", dest_path.display(), e) })?; } else if src_path.is_dir() { copy_directory(&src_path, &dest_path)?; } else if src_path.is_file() { fs::copy(&src_path, &dest_path).map_err(|e| { - format!( - "Failed to copy file {}: {}", - src_path.display(), - e - ) + format!("Failed to copy file {}: {}", src_path.display(), e) })?; } } @@ -163,6 +138,7 @@ fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { Ok(()) } + #[cfg(test)] mod tests { use super::*; From b50fc55ecb9ddf2c537c47f1091b793f63335143 Mon Sep 17 00:00:00 2001 From: Hazem Nureldin Date: Thu, 7 May 2026 17:16:05 +0100 Subject: [PATCH 07/15] fix: remove unused PathBuf import --- workflows-cli/src/create/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workflows-cli/src/create/mod.rs b/workflows-cli/src/create/mod.rs index 92f4d4b57..458b67ecc 100644 --- a/workflows-cli/src/create/mod.rs +++ b/workflows-cli/src/create/mod.rs @@ -1,7 +1,7 @@ use crate::CreateArgs; use std::io; use std::os::unix::fs as fs_sym; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::{fs, process}; pub fn create(args: CreateArgs) { @@ -22,7 +22,7 @@ fn generate_template_repo( args: &CreateArgs, prompt_fn: fn(&str) -> bool, ) -> Result<(), String> { - // ✅ Reject workflow files + // Reject workflow files if args.name.ends_with(".yaml") || args.name.ends_with(".yml") { return Err(format!( "Invalid name '{}': create expects a directory name, not a workflow file", From 77329543e807f35aa660d3bc98eb48f739ef9fe2 Mon Sep 17 00:00:00 2001 From: Hazem Nureldin Date: Thu, 7 May 2026 18:13:34 +0100 Subject: [PATCH 08/15] chore: apply rustfmt formatting --- workflows-cli/src/create/mod.rs | 35 +++++++++++++-------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/workflows-cli/src/create/mod.rs b/workflows-cli/src/create/mod.rs index 458b67ecc..59e61941f 100644 --- a/workflows-cli/src/create/mod.rs +++ b/workflows-cli/src/create/mod.rs @@ -18,10 +18,7 @@ pub fn create(args: CreateArgs) { }; } -fn generate_template_repo( - args: &CreateArgs, - prompt_fn: fn(&str) -> bool, -) -> Result<(), String> { +fn generate_template_repo(args: &CreateArgs, prompt_fn: fn(&str) -> bool) -> Result<(), String> { // Reject workflow files if args.name.ends_with(".yaml") || args.name.ends_with(".yml") { return Err(format!( @@ -33,18 +30,15 @@ fn generate_template_repo( let root_path = Path::new(&args.name); println!("Generating Template Repo: {}", root_path.display()); - fs::create_dir(root_path).map_err(|e| { - format!("Failed to create directory {}: {}", root_path.display(), e) - })?; + fs::create_dir(root_path) + .map_err(|e| format!("Failed to create directory {}: {}", root_path.display(), e))?; println!("Created directory: {}", root_path.display()); let workflows_home = Path::new(&args.workflows_home); - let conventional_src = - workflows_home.join("template-boilerplate/conventional-templates"); - let helm_src = - workflows_home.join("template-boilerplate/helm-based-templates"); + let conventional_src = workflows_home.join("template-boilerplate/conventional-templates"); + let helm_src = workflows_home.join("template-boilerplate/helm-based-templates"); // Validate sources BEFORE asking user anything if !conventional_src.exists() { @@ -86,8 +80,6 @@ fn generate_template_repo( Ok(()) } - - fn prompt(message: &str) -> bool { println!("{message}"); let mut selection = String::new(); @@ -108,7 +100,11 @@ fn prompt(message: &str) -> bool { fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { fs::create_dir_all(dest).map_err(|e| { - format!("Failed to create destination directory {}: {}", dest.display(), e) + format!( + "Failed to create destination directory {}: {}", + dest.display(), + e + ) })?; for entry in fs::read_dir(src) @@ -123,22 +119,19 @@ fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { .map_err(|e| format!("Failed to read symlink {}: {}", src_path.display(), e))?; #[cfg(unix)] - fs_sym::symlink(&target, &dest_path).map_err(|e| { - format!("Failed to create symlink {}: {}", dest_path.display(), e) - })?; + fs_sym::symlink(&target, &dest_path) + .map_err(|e| format!("Failed to create symlink {}: {}", dest_path.display(), e))?; } else if src_path.is_dir() { copy_directory(&src_path, &dest_path)?; } else if src_path.is_file() { - fs::copy(&src_path, &dest_path).map_err(|e| { - format!("Failed to copy file {}: {}", src_path.display(), e) - })?; + fs::copy(&src_path, &dest_path) + .map_err(|e| format!("Failed to copy file {}: {}", src_path.display(), e))?; } } Ok(()) } - #[cfg(test)] mod tests { use super::*; From 493c3f73bda7ed850a2dcfdbc11dcf62e19df945 Mon Sep 17 00:00:00 2001 From: Hazem Nureldin Date: Fri, 8 May 2026 08:00:20 +0100 Subject: [PATCH 09/15] fix: the templates copying --- workflows-cli/src/create/mod.rs | 76 +++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 18 deletions(-) diff --git a/workflows-cli/src/create/mod.rs b/workflows-cli/src/create/mod.rs index 59e61941f..9dda2796a 100644 --- a/workflows-cli/src/create/mod.rs +++ b/workflows-cli/src/create/mod.rs @@ -65,6 +65,7 @@ fn generate_template_repo(args: &CreateArgs, prompt_fn: fn(&str) -> bool) -> Res if include_conventional { copy_directory(&conventional_src, &conventional_dest)?; + println!("Copied conventional templates"); } let include_helm = if args.helm { @@ -75,6 +76,7 @@ fn generate_template_repo(args: &CreateArgs, prompt_fn: fn(&str) -> bool) -> Res if include_helm { copy_directory(&helm_src, &helm_dest)?; + println!("Copied helm templates"); } Ok(()) @@ -85,19 +87,19 @@ fn prompt(message: &str) -> bool { let mut selection = String::new(); loop { + selection.clear(); io::stdin() .read_line(&mut selection) .expect("Failed to read line"); - if selection.to_lowercase().trim() == "y" { - return true; - } else if selection.to_lowercase().trim() == "n" { - return false; - } else { - println!("Invalid input. Please enter 'y' or 'n'."); + if selection.trim().to_lowercase().as_str() { + "y" => return true, + "n" => return false, + _ => println!("Invalid input. Please enter 'y' or 'n'."), } } } +// Recursively copy directory contents fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { fs::create_dir_all(dest).map_err(|e| { format!( @@ -107,25 +109,63 @@ fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { ) })?; - for entry in fs::read_dir(src) - .map_err(|e| format!("Failed to read source folder {}: {}", src.display(), e))? - { + for entry in fs::read_dir(src).map_err(|e| { + format!( + "Failed to read source folder {}: {}", + src.display(), + e + ) + })? { let entry = entry.map_err(|e| e.to_string())?; + let src_path = entry.path(); + let dest_path = dest.join(entry.file_name()); - if src_path.is_symlink() { - let target = fs::read_link(&src_path) - .map_err(|e| format!("Failed to read symlink {}: {}", src_path.display(), e))?; + // Important: use symlink_metadata + let metadata = fs::symlink_metadata(&src_path).map_err(|e| { + format!( + "Failed to read metadata for {}: {}", + src_path.display(), + e + ) + })?; + + // Handle symlink + if metadata.file_type().is_symlink() { + let target = fs::read_link(&src_path).map_err(|e| { + format!( + "Failed to read symlink {}: {}", + src_path.display(), + e + ) + })?; #[cfg(unix)] - fs_sym::symlink(&target, &dest_path) - .map_err(|e| format!("Failed to create symlink {}: {}", dest_path.display(), e))?; - } else if src_path.is_dir() { + { + fs_sym::symlink(&target, &dest_path).map_err(|e| { + format!( + "Failed to create symlink {}: {}", + dest_path.display(), + e + ) + })?; + } + + // Handle directory + else if metadata.is_dir() { copy_directory(&src_path, &dest_path)?; - } else if src_path.is_file() { - fs::copy(&src_path, &dest_path) - .map_err(|e| format!("Failed to copy file {}: {}", src_path.display(), e))?; + } + // Handle regular file + else if metadata.is_file() { + fs::copy(&src_path, &dest_path).map_err(|e| { + format!( + "Failed to copy file {} -> {}: {}", + src_path.display(), + dest_path.display(), + e + ) + })?; } } From 276c47f4929668083207f543fba55928d2b0556d Mon Sep 17 00:00:00 2001 From: Hazem Nureldin Date: Fri, 8 May 2026 08:52:46 +0100 Subject: [PATCH 10/15] chore: unclosed delimiter --- workflows-cli/src/create/mod.rs | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/workflows-cli/src/create/mod.rs b/workflows-cli/src/create/mod.rs index 9dda2796a..cdafc450f 100644 --- a/workflows-cli/src/create/mod.rs +++ b/workflows-cli/src/create/mod.rs @@ -119,10 +119,8 @@ fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { let entry = entry.map_err(|e| e.to_string())?; let src_path = entry.path(); - let dest_path = dest.join(entry.file_name()); - // Important: use symlink_metadata let metadata = fs::symlink_metadata(&src_path).map_err(|e| { format!( "Failed to read metadata for {}: {}", @@ -131,7 +129,7 @@ fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { ) })?; - // Handle symlink + //SYMLINK if metadata.file_type().is_symlink() { let target = fs::read_link(&src_path).map_err(|e| { format!( @@ -141,22 +139,19 @@ fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { ) })?; - #[cfg(unix)] - { - fs_sym::symlink(&target, &dest_path).map_err(|e| { - format!( - "Failed to create symlink {}: {}", - dest_path.display(), - e - ) - })?; - } - - // Handle directory + fs_sym::symlink(&target, &dest_path).map_err(|e| { + format!( + "Failed to create symlink {}: {}", + dest_path.display(), + e + ) + })?; + } + //DIRECTORY else if metadata.is_dir() { copy_directory(&src_path, &dest_path)?; } - // Handle regular file + // FILE else if metadata.is_file() { fs::copy(&src_path, &dest_path).map_err(|e| { format!( @@ -172,6 +167,7 @@ fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { Ok(()) } + #[cfg(test)] mod tests { use super::*; From 77ff9db3a0939898c41537b0864390c15795262a Mon Sep 17 00:00:00 2001 From: Hazem Nureldin Date: Fri, 8 May 2026 09:17:01 +0100 Subject: [PATCH 11/15] fix: mixed python with rust code --- workflows-cli/src/create/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/workflows-cli/src/create/mod.rs b/workflows-cli/src/create/mod.rs index cdafc450f..c247a63b7 100644 --- a/workflows-cli/src/create/mod.rs +++ b/workflows-cli/src/create/mod.rs @@ -87,18 +87,20 @@ fn prompt(message: &str) -> bool { let mut selection = String::new(); loop { - selection.clear(); + selection.clear(); io::stdin() .read_line(&mut selection) .expect("Failed to read line"); if selection.trim().to_lowercase().as_str() { - "y" => return true, + "y" => return true, "n" => return false, _ => println!("Invalid input. Please enter 'y' or 'n'."), } } } + + // Recursively copy directory contents fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { fs::create_dir_all(dest).map_err(|e| { From 20b1fc521548d9c12d4d1258fef840581a305aa2 Mon Sep 17 00:00:00 2001 From: Hazem Nureldin Date: Fri, 8 May 2026 09:55:10 +0100 Subject: [PATCH 12/15] fix: match expr inside an if --- workflows-cli/src/create/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/workflows-cli/src/create/mod.rs b/workflows-cli/src/create/mod.rs index c247a63b7..1473d1050 100644 --- a/workflows-cli/src/create/mod.rs +++ b/workflows-cli/src/create/mod.rs @@ -87,11 +87,13 @@ fn prompt(message: &str) -> bool { let mut selection = String::new(); loop { - selection.clear(); + selection.clear(); + io::stdin() .read_line(&mut selection) .expect("Failed to read line"); - if selection.trim().to_lowercase().as_str() { + + match selection.trim().to_lowercase().as_str() { "y" => return true, "n" => return false, _ => println!("Invalid input. Please enter 'y' or 'n'."), From a569491b1d16305201ac1ac5d69c2dc23e45f8c7 Mon Sep 17 00:00:00 2001 From: Hazem Nureldin Date: Fri, 8 May 2026 11:01:00 +0100 Subject: [PATCH 13/15] chore: caode formatting --- workflows-cli/src/create/mod.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/workflows-cli/src/create/mod.rs b/workflows-cli/src/create/mod.rs index 1473d1050..8eeb542a7 100644 --- a/workflows-cli/src/create/mod.rs +++ b/workflows-cli/src/create/mod.rs @@ -101,9 +101,7 @@ fn prompt(message: &str) -> bool { } } - - -// Recursively copy directory contents +/// Recursively copy directory contents fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { fs::create_dir_all(dest).map_err(|e| { format!( @@ -133,7 +131,7 @@ fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { ) })?; - //SYMLINK + // SYMLINK if metadata.file_type().is_symlink() { let target = fs::read_link(&src_path).map_err(|e| { format!( @@ -151,7 +149,7 @@ fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { ) })?; } - //DIRECTORY + // DIRECTORY else if metadata.is_dir() { copy_directory(&src_path, &dest_path)?; } From be09a72019dd7f73d0892dc7e60fb8f59e4e5ce8 Mon Sep 17 00:00:00 2001 From: Thomas Binu Thomas Date: Fri, 8 May 2026 13:02:09 +0100 Subject: [PATCH 14/15] fix: fixing formatting --- backend/auth-daemon/README.md | 2 +- workflows-cli/src/create/mod.rs | 38 ++++++++------------------------- 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/backend/auth-daemon/README.md b/backend/auth-daemon/README.md index baf768611..68576f3f1 100644 --- a/backend/auth-daemon/README.md +++ b/backend/auth-daemon/README.md @@ -1,6 +1,6 @@ # auth-daemon -Sidecar HTTP proxy that injects valid OIDC access tokens into GraphQL requests made from within Argo workflows. Workflows call the daemon on `localhost` instead of the GraphQL API directly; the daemon handles token refresh and auth header injection transparently. + Sidecar HTTP proxy that injects valid OIDC access tokens into GraphQL requests made from within Argo workflows. Workflows call the daemon on `localhost` instead of the GraphQL API directly; the daemon handles token refresh and auth header injection transparently. ## Usage diff --git a/workflows-cli/src/create/mod.rs b/workflows-cli/src/create/mod.rs index 8eeb542a7..d063de391 100644 --- a/workflows-cli/src/create/mod.rs +++ b/workflows-cli/src/create/mod.rs @@ -111,43 +111,24 @@ fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { ) })?; - for entry in fs::read_dir(src).map_err(|e| { - format!( - "Failed to read source folder {}: {}", - src.display(), - e - ) - })? { + for entry in fs::read_dir(src) + .map_err(|e| format!("Failed to read source folder {}: {}", src.display(), e))? + { let entry = entry.map_err(|e| e.to_string())?; let src_path = entry.path(); let dest_path = dest.join(entry.file_name()); - let metadata = fs::symlink_metadata(&src_path).map_err(|e| { - format!( - "Failed to read metadata for {}: {}", - src_path.display(), - e - ) - })?; + let metadata = fs::symlink_metadata(&src_path) + .map_err(|e| format!("Failed to read metadata for {}: {}", src_path.display(), e))?; // SYMLINK if metadata.file_type().is_symlink() { - let target = fs::read_link(&src_path).map_err(|e| { - format!( - "Failed to read symlink {}: {}", - src_path.display(), - e - ) - })?; + let target = fs::read_link(&src_path) + .map_err(|e| format!("Failed to read symlink {}: {}", src_path.display(), e))?; - fs_sym::symlink(&target, &dest_path).map_err(|e| { - format!( - "Failed to create symlink {}: {}", - dest_path.display(), - e - ) - })?; + fs_sym::symlink(&target, &dest_path) + .map_err(|e| format!("Failed to create symlink {}: {}", dest_path.display(), e))?; } // DIRECTORY else if metadata.is_dir() { @@ -169,7 +150,6 @@ fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { Ok(()) } - #[cfg(test)] mod tests { use super::*; From 0abb4193e3267ed676912fa15772ce54bafc9b4d Mon Sep 17 00:00:00 2001 From: Hazem Nureldin Date: Mon, 11 May 2026 11:56:04 +0100 Subject: [PATCH 15/15] chore(fmt): apply cargo fmt formatting --- workflows-cli/src/create/mod.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/workflows-cli/src/create/mod.rs b/workflows-cli/src/create/mod.rs index d063de391..9e684e5dd 100644 --- a/workflows-cli/src/create/mod.rs +++ b/workflows-cli/src/create/mod.rs @@ -129,13 +129,9 @@ fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { fs_sym::symlink(&target, &dest_path) .map_err(|e| format!("Failed to create symlink {}: {}", dest_path.display(), e))?; - } - // DIRECTORY - else if metadata.is_dir() { + } else if metadata.is_dir() { copy_directory(&src_path, &dest_path)?; - } - // FILE - else if metadata.is_file() { + } else if metadata.is_file() { fs::copy(&src_path, &dest_path).map_err(|e| { format!( "Failed to copy file {} -> {}: {}", @@ -153,7 +149,7 @@ fn copy_directory(src: &Path, dest: &Path) -> Result<(), String> { #[cfg(test)] mod tests { use super::*; - use ::serial_test::serial; + use serial_test::serial; struct TestCleanup { directory: String, }