diff --git a/Cargo.lock b/Cargo.lock index 24ae3ba..cf65203 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2137,6 +2137,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf16_iter" version = "1.0.5" @@ -2542,6 +2548,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", + "urlencoding", "uuid", "which", ] diff --git a/Cargo.toml b/Cargo.toml index c902fcf..34d9bef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ lazy_static = "1.4" reqwest = { version = "0.11", features = ["json"] } libc = "0.2" nix = { version = "0.27.1", features = ["fs"] } +urlencoding = "2.1.3" [profile.release] codegen-units = 1 diff --git a/README.md b/README.md index 53fed7b..1d62c63 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ WRKFLW is a powerful command-line tool for validating and executing GitHub Actio - **Special Action Handling**: Native handling for commonly used actions like `actions/checkout` - **Output Capturing**: View logs, step outputs, and execution details - **Parallel Job Execution**: Runs independent jobs in parallel for faster workflow execution -- **Trigger Workflows Remotely**: Manually trigger workflow runs on GitHub +- **Trigger Workflows Remotely**: Manually trigger workflow runs on GitHub or GitLab ## Installation @@ -113,6 +113,9 @@ wrkflw tui --emulate ```bash # Trigger a workflow remotely on GitHub wrkflw trigger workflow-name --branch main --input key1=value1 --input key2=value2 + +# Trigger a pipeline remotely on GitLab +wrkflw trigger-gitlab --branch main --variable key1=value1 --variable key2=value2 ``` ## TUI Controls @@ -233,6 +236,7 @@ WRKFLW automatically cleans up any Docker containers created during workflow exe - ✅ Local actions - ✅ Special handling for common actions (e.g., `actions/checkout`) - ✅ Workflow triggering via `workflow_dispatch` +- ✅ GitLab pipeline triggering - ✅ Environment files (`GITHUB_OUTPUT`, `GITHUB_ENV`, `GITHUB_PATH`, `GITHUB_STEP_SUMMARY`) ### Limited or Unsupported Features diff --git a/src/main.rs b/src/main.rs index f0cb05f..b744fbf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ mod cleanup_test; mod evaluator; mod executor; mod github; +mod gitlab; mod logging; mod matrix; mod matrix_test; @@ -87,6 +88,17 @@ enum Commands { input: Option>, }, + /// Trigger a GitLab pipeline remotely + TriggerGitlab { + /// Branch to run the pipeline on + #[arg(short, long)] + branch: Option, + + /// Key-value variables for the pipeline in format key=value + #[arg(short, long, value_parser = parse_key_val)] + variable: Option>, + }, + /// List available workflows List, } @@ -350,17 +362,17 @@ async fn main() { branch, input, }) => { - let inputs = input.as_ref().map(|kv_pairs| { - kv_pairs - .iter() - .cloned() + logging::info(&format!("Triggering workflow {} on GitHub", workflow)); + + // Convert inputs to HashMap + let input_map = input.as_ref().map(|i| { + i.iter() + .map(|(k, v)| (k.clone(), v.clone())) .collect::>() }); - match github::trigger_workflow(workflow, branch.as_deref(), inputs.clone()).await { - Ok(_) => { - // Success is already reported in the github module with detailed info - } + match github::trigger_workflow(workflow, branch.as_deref(), input_map).await { + Ok(_) => logging::info("Workflow triggered successfully"), Err(e) => { eprintln!("Error triggering workflow: {}", e); std::process::exit(1); @@ -368,29 +380,70 @@ async fn main() { } } - Some(Commands::List) => match github::get_repo_info() { - Ok(repo_info) => match github::list_workflows(&repo_info).await { - Ok(workflows) => { - if workflows.is_empty() { - println!("No workflows found in the .github/workflows directory"); - } else { - println!("Available workflows:"); - for workflow in workflows { - println!(" {}", workflow); - } - println!("\nTrigger a workflow with: wrkflw trigger [options]"); - } - } + Some(Commands::TriggerGitlab { branch, variable }) => { + logging::info("Triggering pipeline on GitLab"); + + // Convert variables to HashMap + let variable_map = variable.as_ref().map(|v| { + v.iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect::>() + }); + + match gitlab::trigger_pipeline(branch.as_deref(), variable_map).await { + Ok(_) => logging::info("GitLab pipeline triggered successfully"), Err(e) => { - eprintln!("Error listing workflows: {}", e); + eprintln!("Error triggering GitLab pipeline: {}", e); std::process::exit(1); } - }, - Err(e) => { - eprintln!("Error getting repository info: {}", e); - std::process::exit(1); } - }, + } + + Some(Commands::List) => { + logging::info("Listing available workflows"); + + // Attempt to get GitHub repo info + if let Ok(repo_info) = github::get_repo_info() { + match github::list_workflows(&repo_info).await { + Ok(workflows) => { + if workflows.is_empty() { + println!("No GitHub workflows found in repository"); + } else { + println!("GitHub workflows:"); + for workflow in workflows { + println!(" {}", workflow); + } + } + } + Err(e) => { + eprintln!("Error listing GitHub workflows: {}", e); + } + } + } else { + println!("Not a GitHub repository or unable to get repository information"); + } + + // Attempt to get GitLab repo info + if let Ok(repo_info) = gitlab::get_repo_info() { + match gitlab::list_pipelines(&repo_info).await { + Ok(pipelines) => { + if pipelines.is_empty() { + println!("No GitLab pipelines found in repository"); + } else { + println!("GitLab pipelines:"); + for pipeline in pipelines { + println!(" {}", pipeline); + } + } + } + Err(e) => { + eprintln!("Error listing GitLab pipelines: {}", e); + } + } + } else { + println!("Not a GitLab repository or unable to get repository information"); + } + } None => { // Default to TUI interface if no subcommand