diff --git a/src/events/git_clone.rs b/src/events/git_clone.rs index d08efc6..ad95b5e 100644 --- a/src/events/git_clone.rs +++ b/src/events/git_clone.rs @@ -1,7 +1,7 @@ use super::AtomicEvent; use crate::bgit_error::{BGitError, BGitErrorWorkflowType, NO_RULE, NO_STEP}; use crate::rules::Rule; -use git2::{Cred, CredentialType}; +use crate::utils::git_auth::setup_auth_callbacks; use std::env; use std::path::Path; @@ -122,142 +122,10 @@ impl GitClone { } } - /// Set up authentication callbacks for git operations - fn setup_auth_callbacks() -> git2::RemoteCallbacks<'static> { - use std::sync::Arc; - use std::sync::atomic::{AtomicUsize, Ordering}; - - let mut callbacks = git2::RemoteCallbacks::new(); - let attempt_count = Arc::new(AtomicUsize::new(0)); - - callbacks.credentials(move |url, username_from_url, allowed_types| { - let current_attempt = attempt_count.fetch_add(1, Ordering::SeqCst); - // Limit authentication attempts to prevent infinite loops - if current_attempt > 3 { - return Err(git2::Error::new( - git2::ErrorCode::Auth, - git2::ErrorClass::Net, - "Maximum authentication attempts exceeded", - )); - } - - // If SSH key authentication is allowed - if allowed_types.contains(CredentialType::SSH_KEY) { - if let Some(username) = username_from_url { - // Try SSH agent first (most common and secure) - match Cred::ssh_key_from_agent(username) { - Ok(cred) => { - return Ok(cred); - } - Err(e) => { - println!("SSH agent failed: {}", e); - } - } - - // Try to find SSH keys in standard locations - let home_dir = std::env::var("HOME") - .or_else(|_| std::env::var("USERPROFILE")) - .unwrap_or_else(|_| ".".to_string()); - - let ssh_dir = Path::new(&home_dir).join(".ssh"); - - // Common SSH key file names in order of preference - let key_files = [ - ("id_ed25519", "id_ed25519.pub"), - ("id_rsa", "id_rsa.pub"), - ("id_ecdsa", "id_ecdsa.pub"), - ("id_dsa", "id_dsa.pub"), - ]; - - for (private_name, public_name) in &key_files { - let private_key = ssh_dir.join(private_name); - let public_key = ssh_dir.join(public_name); - - if private_key.exists() { - // Try with public key if it exists - if public_key.exists() { - match Cred::ssh_key(username, Some(&public_key), &private_key, None) - { - Ok(cred) => { - return Ok(cred); - } - Err(e) => { - eprintln!("SSH key with public key failed: {}", e); - } - } - } - - // Try without public key - match Cred::ssh_key(username, None, &private_key, None) { - Ok(cred) => { - return Ok(cred); - } - Err(e) => { - eprintln!("SSH key without public key failed: {}", e); - } - } - } - } - } else { - eprintln!("No username provided for SSH authentication"); - } - } - - // If username/password authentication is allowed (HTTPS) - if allowed_types.contains(CredentialType::USER_PASS_PLAINTEXT) { - // Try to get credentials from git config or environment - if let (Ok(username), Ok(password)) = - (std::env::var("GIT_USERNAME"), std::env::var("GIT_PASSWORD")) - { - return Cred::userpass_plaintext(&username, &password); - } - - // For GitHub, you might want to use a personal access token - if url.contains("github.com") { - if let Ok(token) = std::env::var("GITHUB_TOKEN") { - return Cred::userpass_plaintext("git", &token); - } - } - } - - // Default authentication (tries default SSH key) - if allowed_types.contains(CredentialType::DEFAULT) { - match Cred::default() { - Ok(cred) => { - return Ok(cred); - } - Err(e) => { - eprintln!("Default authentication failed: {}", e); - } - } - } - - Err(git2::Error::new( - git2::ErrorCode::Auth, - git2::ErrorClass::Net, - format!( - "Authentication failed after {} attempts for {}. Available methods: {:?}", - current_attempt + 1, - url, - allowed_types - ), - )) - }); - - // Set up certificate check callback for HTTPS - callbacks.certificate_check(|_cert, _host| { - // In production, you should properly validate certificates - // For now, we'll accept all certificates (not recommended for production) - Ok(git2::CertificateCheckStatus::CertificateOk) - }); - - callbacks - } - /// Create fetch options with authentication fn create_fetch_options() -> git2::FetchOptions<'static> { let mut fetch_options = git2::FetchOptions::new(); - fetch_options.remote_callbacks(Self::setup_auth_callbacks()); + fetch_options.remote_callbacks(setup_auth_callbacks()); fetch_options } } diff --git a/src/events/git_pull.rs b/src/events/git_pull.rs index fe34c95..731464c 100644 --- a/src/events/git_pull.rs +++ b/src/events/git_pull.rs @@ -3,7 +3,8 @@ use std::path::Path; use super::AtomicEvent; use crate::bgit_error::{BGitError, BGitErrorWorkflowType, NO_RULE, NO_STEP}; use crate::rules::Rule; -use git2::{Cred, CredentialType, Repository}; +use crate::utils::git_auth::setup_auth_callbacks; +use git2::Repository; pub struct GitPull { pub pre_check_rules: Vec>, @@ -462,142 +463,10 @@ impl GitPull { Ok(()) } - /// Set up authentication callbacks for git operations - fn setup_auth_callbacks() -> git2::RemoteCallbacks<'static> { - use std::sync::Arc; - use std::sync::atomic::{AtomicUsize, Ordering}; - - let mut callbacks = git2::RemoteCallbacks::new(); - let attempt_count = Arc::new(AtomicUsize::new(0)); - - callbacks.credentials(move |url, username_from_url, allowed_types| { - let current_attempt = attempt_count.fetch_add(1, Ordering::SeqCst); - // Limit authentication attempts to prevent infinite loops - if current_attempt > 3 { - return Err(git2::Error::new( - git2::ErrorCode::Auth, - git2::ErrorClass::Net, - "Maximum authentication attempts exceeded", - )); - } - - // If SSH key authentication is allowed - if allowed_types.contains(CredentialType::SSH_KEY) { - if let Some(username) = username_from_url { - // Try SSH agent first (most common and secure) - match Cred::ssh_key_from_agent(username) { - Ok(cred) => { - return Ok(cred); - } - Err(e) => { - println!("SSH agent failed: {}", e); - } - } - - // Try to find SSH keys in standard locations - let home_dir = std::env::var("HOME") - .or_else(|_| std::env::var("USERPROFILE")) - .unwrap_or_else(|_| ".".to_string()); - - let ssh_dir = Path::new(&home_dir).join(".ssh"); - - // Common SSH key file names in order of preference - let key_files = [ - ("id_ed25519", "id_ed25519.pub"), - ("id_rsa", "id_rsa.pub"), - ("id_ecdsa", "id_ecdsa.pub"), - ("id_dsa", "id_dsa.pub"), - ]; - - for (private_name, public_name) in &key_files { - let private_key = ssh_dir.join(private_name); - let public_key = ssh_dir.join(public_name); - - if private_key.exists() { - // Try with public key if it exists - if public_key.exists() { - match Cred::ssh_key(username, Some(&public_key), &private_key, None) - { - Ok(cred) => { - return Ok(cred); - } - Err(e) => { - eprintln!("SSH key with public key failed: {}", e); - } - } - } - - // Try without public key - match Cred::ssh_key(username, None, &private_key, None) { - Ok(cred) => { - return Ok(cred); - } - Err(e) => { - eprintln!("SSH key without public key failed: {}", e); - } - } - } - } - } else { - eprintln!("No username provided for SSH authentication"); - } - } - - // If username/password authentication is allowed (HTTPS) - if allowed_types.contains(CredentialType::USER_PASS_PLAINTEXT) { - // Try to get credentials from git config or environment - if let (Ok(username), Ok(password)) = - (std::env::var("GIT_USERNAME"), std::env::var("GIT_PASSWORD")) - { - return Cred::userpass_plaintext(&username, &password); - } - - // For GitHub, you might want to use a personal access token - if url.contains("github.com") { - if let Ok(token) = std::env::var("GITHUB_TOKEN") { - return Cred::userpass_plaintext("git", &token); - } - } - } - - // Default authentication (tries default SSH key) - if allowed_types.contains(CredentialType::DEFAULT) { - match Cred::default() { - Ok(cred) => { - return Ok(cred); - } - Err(e) => { - eprintln!("Default authentication failed: {}", e); - } - } - } - - Err(git2::Error::new( - git2::ErrorCode::Auth, - git2::ErrorClass::Net, - format!( - "Authentication failed after {} attempts for {}. Available methods: {:?}", - current_attempt + 1, - url, - allowed_types - ), - )) - }); - - // Set up certificate check callback for HTTPS - callbacks.certificate_check(|_cert, _host| { - // In production, you should properly validate certificates - // For now, we'll accept all certificates (not recommended for production) - Ok(git2::CertificateCheckStatus::CertificateOk) - }); - - callbacks - } - /// Create fetch options with authentication fn create_fetch_options() -> git2::FetchOptions<'static> { let mut fetch_options = git2::FetchOptions::new(); - fetch_options.remote_callbacks(Self::setup_auth_callbacks()); + fetch_options.remote_callbacks(setup_auth_callbacks()); fetch_options } } diff --git a/src/events/git_push.rs b/src/events/git_push.rs index 39a1549..fa12627 100644 --- a/src/events/git_push.rs +++ b/src/events/git_push.rs @@ -1,9 +1,9 @@ -use std::path::Path; - use super::AtomicEvent; use crate::bgit_error::{BGitError, BGitErrorWorkflowType, NO_RULE, NO_STEP}; use crate::rules::Rule; -use git2::{Cred, CredentialType, Repository}; +use crate::utils::git_auth::setup_auth_callbacks; +use git2::Repository; +use std::path::Path; pub struct GitPush { pub pre_check_rules: Vec>, @@ -239,155 +239,10 @@ impl GitPush { Ok(()) } - /// Set up authentication callbacks for git operations - fn setup_auth_callbacks() -> git2::RemoteCallbacks<'static> { - use std::sync::Arc; - use std::sync::atomic::{AtomicUsize, Ordering}; - - let mut callbacks = git2::RemoteCallbacks::new(); - let attempt_count = Arc::new(AtomicUsize::new(0)); - - callbacks.credentials(move |url, username_from_url, allowed_types| { - let current_attempt = attempt_count.fetch_add(1, Ordering::SeqCst); - // Limit authentication attempts to prevent infinite loops - if current_attempt > 3 { - return Err(git2::Error::new( - git2::ErrorCode::Auth, - git2::ErrorClass::Net, - "Maximum authentication attempts exceeded", - )); - } - - // If SSH key authentication is allowed - if allowed_types.contains(CredentialType::SSH_KEY) { - if let Some(username) = username_from_url { - // Try SSH agent first (most common and secure) - match Cred::ssh_key_from_agent(username) { - Ok(cred) => { - return Ok(cred); - } - Err(e) => { - println!("SSH agent failed: {}", e); - } - } - - // Try to find SSH keys in standard locations - let home_dir = std::env::var("HOME") - .or_else(|_| std::env::var("USERPROFILE")) - .unwrap_or_else(|_| ".".to_string()); - - let ssh_dir = Path::new(&home_dir).join(".ssh"); - - // Common SSH key file names in order of preference - let key_files = [ - ("id_ed25519", "id_ed25519.pub"), - ("id_rsa", "id_rsa.pub"), - ("id_ecdsa", "id_ecdsa.pub"), - ("id_dsa", "id_dsa.pub"), - ]; - - for (private_name, public_name) in &key_files { - let private_key = ssh_dir.join(private_name); - let public_key = ssh_dir.join(public_name); - - if private_key.exists() { - // Try with public key if it exists - if public_key.exists() { - match Cred::ssh_key(username, Some(&public_key), &private_key, None) - { - Ok(cred) => { - return Ok(cred); - } - Err(e) => { - println!("SSH key with public key failed: {}", e); - } - } - } - - // Try without public key - match Cred::ssh_key(username, None, &private_key, None) { - Ok(cred) => { - return Ok(cred); - } - Err(e) => { - println!("SSH key without public key failed: {}", e); - } - } - } - } - } else { - println!("No username provided for SSH authentication"); - } - } - - // If username/password authentication is allowed (HTTPS) - if allowed_types.contains(CredentialType::USER_PASS_PLAINTEXT) { - // Try to get credentials from git config or environment - if let (Ok(username), Ok(password)) = - (std::env::var("GIT_USERNAME"), std::env::var("GIT_PASSWORD")) - { - return Cred::userpass_plaintext(&username, &password); - } - - // For GitHub, you might want to use a personal access token - if url.contains("github.com") { - if let Ok(token) = std::env::var("GITHUB_TOKEN") { - return Cred::userpass_plaintext("git", &token); - } - } - } - - // Default authentication (tries default SSH key) - if allowed_types.contains(CredentialType::DEFAULT) { - match Cred::default() { - Ok(cred) => { - return Ok(cred); - } - Err(e) => { - println!("Default authentication failed: {}", e); - } - } - } - - Err(git2::Error::new( - git2::ErrorCode::Auth, - git2::ErrorClass::Net, - format!( - "Authentication failed after {} attempts for {}. Available methods: {:?}", - current_attempt + 1, - url, - allowed_types - ), - )) - }); - - // Add push update reference callback for better error reporting - callbacks.push_update_reference(|refname, status| match status { - Some(msg) => { - println!("Push failed for {}: {}", refname, msg); - Err(git2::Error::from_str(msg)) - } - None => { - println!("Push successful for {}", refname); - Ok(()) - } - }); - - // Set up certificate check callback for HTTPS - callbacks.certificate_check(|_cert, _host| { - // In production, you should properly validate certificates - // For now, we'll accept all certificates (not recommended for production) - println!("Certificate check for host: {}", _host); - Ok(git2::CertificateCheckStatus::CertificateOk) - }); - - callbacks - } - /// Create push options with authentication fn create_push_options() -> git2::PushOptions<'static> { let mut push_options = git2::PushOptions::new(); - push_options.remote_callbacks(Self::setup_auth_callbacks()); + push_options.remote_callbacks(setup_auth_callbacks()); push_options } } diff --git a/src/main.rs b/src/main.rs index f15d1fd..1a0a525 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ mod hook_executor; mod rules; mod step; mod util; +mod utils; mod workflow_queue; mod workflows; diff --git a/src/utils/git_auth.rs b/src/utils/git_auth.rs new file mode 100644 index 0000000..666fb58 --- /dev/null +++ b/src/utils/git_auth.rs @@ -0,0 +1,277 @@ +use dialoguer::theme::ColorfulTheme; +use dialoguer::{Input, Password}; +use git2::{ + CertificateCheckStatus, Cred, CredentialType, Error, ErrorClass, ErrorCode, RemoteCallbacks, +}; +use log::debug; +use std::path::Path; +use std::sync::{Arc, Mutex}; + +fn try_ssh_agent_auth(username: &str) -> Result { + debug!("Attempting SSH agent authentication for user: {}", username); + + if std::env::var("SSH_AUTH_SOCK").is_err() { + debug!("SSH_AUTH_SOCK not set, skipping ssh_key_from_agent"); + return Err(Error::new( + ErrorCode::Auth, + ErrorClass::Net, + "SSH_AUTH_SOCK not available", + )); + } + + match Cred::ssh_key_from_agent(username) { + Ok(cred) => { + debug!("SSH agent authentication succeeded"); + Ok(cred) + } + Err(e) => { + debug!("SSH agent authentication failed: {}", e); + Err(e) + } + } +} + +fn try_ssh_key_files( + username: &str, + key_index: usize, + use_public_key: bool, +) -> Result { + debug!( + "Attempting SSH key file authentication for user: {}, key_index: {}, use_public_key: {}", + username, key_index, use_public_key + ); + + let home_dir = std::env::var("HOME") + .or_else(|_| std::env::var("USERPROFILE")) + .unwrap_or_else(|_| ".".to_string()); + debug!("Home directory resolved to: {}", home_dir); + + let ssh_dir = Path::new(&home_dir).join(".ssh"); + debug!("Checking .ssh directory at: {:?}", ssh_dir); + + // Common SSH key file names in order of preference + let key_files = [ + ("id_ed25519", "id_ed25519.pub"), + ("id_rsa", "id_rsa.pub"), + ("id_ecdsa", "id_ecdsa.pub"), + ("id_dsa", "id_dsa.pub"), + ]; + + if key_index >= key_files.len() { + debug!("Key index {} out of range", key_index); + return Err(Error::new( + ErrorCode::Auth, + ErrorClass::Net, + "All SSH key files exhausted", + )); + } + + let (private_name, public_name) = key_files[key_index]; + let private_key = ssh_dir.join(private_name); + let public_key = ssh_dir.join(public_name); + + if !private_key.exists() { + debug!("Private key not found: {:?}", private_key); + return Err(Error::new( + ErrorCode::Auth, + ErrorClass::Net, + format!("Private key not found: {}", private_name), + )); + } + + debug!("Found private key: {:?}", private_key); + + if use_public_key { + if public_key.exists() { + debug!("Found public key: {:?}, trying with public key", public_key); + match Cred::ssh_key(username, Some(&public_key), &private_key, None) { + Ok(cred) => { + debug!( + "SSH key auth with public key succeeded for {}", + private_name + ); + Ok(cred) + } + Err(e) => { + debug!("SSH key with public key failed for {}: {}", private_name, e); + Err(e) + } + } + } else { + debug!( + "Public key not found for {}, skipping this attempt", + private_name + ); + Err(Error::new( + ErrorCode::Auth, + ErrorClass::Net, + format!("Public key not found for {}", private_name), + )) + } + } else { + debug!("Trying SSH key without public key for {}", private_name); + match Cred::ssh_key(username, None, &private_key, None) { + Ok(cred) => { + debug!( + "SSH key auth without public key succeeded for {}", + private_name + ); + Ok(cred) + } + Err(e) => { + debug!( + "SSH key without public key failed for {}: {}", + private_name, e + ); + Err(e) + } + } + } +} +fn try_userpass_authentication(username_from_url: Option<&str>) -> Result { + debug!("USER_PASS_PLAINTEXT authentication is allowed, prompting for credentials"); + + // Prompt for username if not provided in URL + let username = if let Some(user) = username_from_url { + user.to_string() + } else { + Input::::with_theme(&ColorfulTheme::default()) + .with_prompt("Enter your username") + .interact() + .map_err(|e| { + Error::new( + ErrorCode::Auth, + ErrorClass::Net, + format!("Failed to read username: {}", e), + ) + })? + }; + + let token = Password::with_theme(&ColorfulTheme::default()) + .with_prompt("Enter your personal access token") + .interact() + .map_err(|e| { + Error::new( + ErrorCode::Auth, + ErrorClass::Net, + format!("Failed to read token: {}", e), + ) + })?; + + if !username.is_empty() && !token.is_empty() { + debug!("Creating credentials with username and token"); + match Cred::userpass_plaintext(&username, &token) { + Ok(cred) => { + debug!("Username/token authentication succeeded"); + Ok(cred) + } + Err(e) => { + debug!("Username/token authentication failed: {}", e); + Err(e) + } + } + } else { + debug!("Username or token is empty, skipping userpass authentication"); + Err(Error::new( + ErrorCode::Auth, + ErrorClass::Net, + "Username or token cannot be empty", + )) + } +} + +fn ssh_authenticate_git( + url: &str, + username_from_url: Option<&str>, + allowed_types: CredentialType, + attempt_count: usize, +) -> Result { + debug!( + "Git authentication attempt #{} for URL: {}", + attempt_count, url + ); + debug!("Username from URL: {:?}", username_from_url); + debug!("Allowed credential types: {:?}", allowed_types); + + // Prevent infinite loops + if attempt_count > 20 { + debug!( + "Too many authentication attempts ({}), failing to prevent infinite loop", + attempt_count + ); + return Err(Error::new( + ErrorCode::Auth, + ErrorClass::Net, + "Too many authentication attempts", + )); + } + + // Try SSH key authentication if allowed + if allowed_types.contains(CredentialType::SSH_KEY) { + if let Some(username) = username_from_url { + debug!("SSH key authentication is allowed, trying SSH methods"); + + // Try SSH agent first (only on first attempt) + if attempt_count == 1 { + if let Ok(cred) = try_ssh_agent_auth(username) { + return Ok(cred); + } + // If SSH agent fails, fall through to try SSH key files on same attempt + } + + // Try SSH key files with progression + // Attempt 1+: Start with id_ed25519 if SSH agent failed + // Attempt 1: id_ed25519 with public key + // Attempt 2: id_ed25519 without public key + // Attempt 3: id_rsa with public key + // Attempt 4: id_rsa without public key + // etc. + let key_attempt = attempt_count - 1; + let key_index = key_attempt / 2; + let use_public_key = key_attempt % 2 == 0; + + if let Ok(cred) = try_ssh_key_files(username, key_index, use_public_key) { + return Ok(cred); + } + } else { + debug!("No username provided for SSH authentication"); + } + } + + debug!( + "All authentication methods failed for attempt {}", + attempt_count + ); + Err(Error::new( + ErrorCode::Auth, + ErrorClass::Net, + format!("Authentication failed - attempt {}", attempt_count), + )) +} +pub fn setup_auth_callbacks() -> RemoteCallbacks<'static> { + let mut callbacks = RemoteCallbacks::new(); + + // Track attempt count across callback invocations + let attempt_count: Arc> = Arc::new(Mutex::new(0)); + + callbacks.credentials(move |url, username_from_url, allowed_types| { + let mut count = attempt_count.lock().unwrap(); + *count += 1; + let current_attempt = *count; + drop(count); + + if allowed_types.contains(CredentialType::USER_PASS_PLAINTEXT) { + try_userpass_authentication(username_from_url) + } else { + ssh_authenticate_git(url, username_from_url, allowed_types, current_attempt) + } + }); + + // Set up certificate check callback for HTTPS + callbacks.certificate_check(|_cert, _host| { + debug!("Skipping certificate verification (INSECURE)"); + Ok(CertificateCheckStatus::CertificateOk) + }); + + callbacks +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..75d5ade --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1 @@ +pub mod git_auth; diff --git a/src/workflows/default/action/mod.rs b/src/workflows/default/action/mod.rs index 1c994cc..ee1b038 100644 --- a/src/workflows/default/action/mod.rs +++ b/src/workflows/default/action/mod.rs @@ -4,7 +4,6 @@ pub(crate) mod ta03_pop_stash; pub(crate) mod ta04_has_unstaged; pub(crate) mod ta07_has_uncommitted; pub(crate) mod ta08_is_pulled_pushed; -pub(crate) mod ta09_pull_push; pub(crate) mod ta10_is_branch_main; pub(crate) mod ta11_is_sole_contributor; pub(crate) mod ta12_move_changes; diff --git a/src/workflows/default/prompt/mod.rs b/src/workflows/default/prompt/mod.rs index 1c1cdd4..c35378b 100644 --- a/src/workflows/default/prompt/mod.rs +++ b/src/workflows/default/prompt/mod.rs @@ -11,3 +11,4 @@ pub(crate) mod pa09_ask_branch_name; pub(crate) mod pa10_ask_same_feat; pub(crate) mod pa11_ask_ai_commit_msg; pub(crate) mod pa12_ask_commit_msg; +pub(crate) mod pa13_pull_push; diff --git a/src/workflows/default/prompt/pa07_ask_pull_push.rs b/src/workflows/default/prompt/pa07_ask_pull_push.rs index 72a5455..eca2dd2 100644 --- a/src/workflows/default/prompt/pa07_ask_pull_push.rs +++ b/src/workflows/default/prompt/pa07_ask_pull_push.rs @@ -1,11 +1,11 @@ use crate::config::{StepFlags, WorkflowRules}; -use crate::workflows::default::action::ta09_pull_push::PullAndPush; +use crate::step::Task::PromptStepTask; +use crate::workflows::default::prompt::pa13_pull_push::PullAndPush; use crate::{ bgit_error::{BGitError, BGitErrorWorkflowType, NO_EVENT, NO_RULE}, - step::{ActionStep, PromptStep, Step, Task::ActionStepTask}, + step::{PromptStep, Step}, }; use dialoguer::{Select, theme::ColorfulTheme}; - pub(crate) struct AskPushPull { name: String, } @@ -46,7 +46,7 @@ impl PromptStep for AskPushPull { })?; match selection { - 0 => Ok(Step::Task(ActionStepTask(Box::new(PullAndPush::new())))), + 0 => Ok(Step::Task(PromptStepTask(Box::new(PullAndPush::new())))), 1 => Ok(Step::Stop), _ => Err(Box::new(BGitError::new( "Invalid selection", diff --git a/src/workflows/default/action/ta09_pull_push.rs b/src/workflows/default/prompt/pa13_pull_push.rs similarity index 73% rename from src/workflows/default/action/ta09_pull_push.rs rename to src/workflows/default/prompt/pa13_pull_push.rs index f480ffd..526745b 100644 --- a/src/workflows/default/action/ta09_pull_push.rs +++ b/src/workflows/default/prompt/pa13_pull_push.rs @@ -5,16 +5,14 @@ use crate::events::git_push::GitPush; use crate::rules::Rule; use crate::rules::a14_big_repo_size::IsRepoSizeTooBig; -use crate::{ - bgit_error::BGitError, - step::{ActionStep, Step}, -}; +use crate::step::PromptStep; +use crate::{bgit_error::BGitError, step::Step}; pub(crate) struct PullAndPush { name: String, } -impl ActionStep for PullAndPush { +impl PromptStep for PullAndPush { fn new() -> Self where Self: Sized, @@ -33,25 +31,18 @@ impl ActionStep for PullAndPush { _step_config_flags: Option<&StepFlags>, workflow_rules_config: Option<&WorkflowRules>, ) -> Result> { - // Create GitPull instance with rebase flag enabled let git_pull = GitPull::new().with_rebase(true); - // Execute pull with rebase match git_pull.execute() { Ok(_) => { - // Pull successful, now attempt push let mut git_push = GitPush::new(); git_push.add_pre_check_rule(Box::new(IsRepoSizeTooBig::new(workflow_rules_config))); - // Configure push options - you can customize these as needed git_push.set_force(false).set_upstream_flag(false); match git_push.execute() { - Ok(_) => { - // Both pull and push successful - Ok(Step::Stop) - } + Ok(_) => Ok(Step::Stop), Err(e) => { // Push failed, return error Err(e)