added interactive mode (#44)

Co-authored-by: kjuulh <contact@kjuulh.io>
Reviewed-on: #44
This commit is contained in:
Kasper Juul Hermansen 2022-12-01 08:51:52 +00:00
parent c00b3a97d2
commit 0dedf9268d
8 changed files with 117 additions and 5 deletions

View File

@ -66,6 +66,15 @@ pub fn execute_cmd() -> Command {
.default_value("true") .default_value("true")
.required(false), .required(false),
) )
.arg(
Arg::new("interactive")
.long("interactive")
.short('i')
.action(ArgAction::Set)
.env("OCTOPUSH_INTERACTIVE")
.default_value("false")
.required(false),
)
} }
pub async fn execute_subcommand(args: &ArgMatches) -> eyre::Result<()> { pub async fn execute_subcommand(args: &ArgMatches) -> eyre::Result<()> {
@ -84,6 +93,11 @@ pub async fn execute_subcommand(args: &ArgMatches) -> eyre::Result<()> {
.ok_or(eyre::anyhow!("--dry-run is required"))? .ok_or(eyre::anyhow!("--dry-run is required"))?
.parse()?; .parse()?;
let interactive: bool = args
.get_one::<String>("interactive")
.ok_or(eyre::anyhow!("--interactive is required"))?
.parse()?;
if dryrun { if dryrun {
tracing::info!("running in dry-run mode"); tracing::info!("running in dry-run mode");
} }
@ -123,21 +137,21 @@ pub async fn execute_subcommand(args: &ArgMatches) -> eyre::Result<()> {
if let Some(git) = &select.git { if let Some(git) = &select.git {
service_register service_register
.git_selector .git_selector
.run(git, &action_path, &action, dryrun) .run(git, &action_path, &action, dryrun, interactive)
.await?; .await?;
} }
if let Some(gitea) = &select.gitea { if let Some(gitea) = &select.gitea {
service_register service_register
.gitea_selector .gitea_selector
.run(gitea, &action_path, &action, dryrun) .run(gitea, &action_path, &action, dryrun, interactive)
.await?; .await?;
} }
if let Some(github) = &select.github { if let Some(github) = &select.github {
service_register service_register
.github_selector .github_selector
.run(github, &action_path, &action, dryrun) .run(github, &action_path, &action, dryrun, interactive)
.await?; .await?;
} }
} }

View File

@ -5,3 +5,4 @@ pub mod schema;
pub mod selectors; pub mod selectors;
mod shell; mod shell;
pub mod storage; pub mod storage;
pub mod ui;

View File

@ -6,18 +6,21 @@ use crate::{
executor::executor::DynExecutor, executor::executor::DynExecutor,
git::DynGitProvider, git::DynGitProvider,
schema::models::{Action, Git}, schema::models::{Action, Git},
ui::DynUI,
}; };
pub struct GitSelector { pub struct GitSelector {
git_provider: DynGitProvider, git_provider: DynGitProvider,
executor: DynExecutor, executor: DynExecutor,
ui: DynUI,
} }
impl GitSelector { impl GitSelector {
pub fn new(git_provider: DynGitProvider, executor: DynExecutor) -> Self { pub fn new(git_provider: DynGitProvider, executor: DynExecutor, ui: DynUI) -> Self {
Self { Self {
git_provider, git_provider,
executor, executor,
ui,
} }
} }
@ -27,6 +30,7 @@ impl GitSelector {
action_path: &PathBuf, action_path: &PathBuf,
action: &Action, action: &Action,
dryrun: bool, dryrun: bool,
interactive: bool,
) -> eyre::Result<()> { ) -> eyre::Result<()> {
tracing::info!("fetching repos"); tracing::info!("fetching repos");
for repo in &git.repositories { for repo in &git.repositories {
@ -46,6 +50,10 @@ impl GitSelector {
continue; continue;
} }
if interactive {
self.ui.confirm().await?;
}
if let Some(push) = &git.push { if let Some(push) = &git.push {
self.git_provider self.git_provider
.push_branch(repo, &push.branch.name) .push_branch(repo, &push.branch.name)

View File

@ -6,12 +6,14 @@ use crate::{
executor::executor::DynExecutor, executor::executor::DynExecutor,
git::{gitea::DynGiteaProvider, DynGitProvider}, git::{gitea::DynGiteaProvider, DynGitProvider},
schema::models::{Action, Gitea}, schema::models::{Action, Gitea},
ui::DynUI,
}; };
pub struct GiteaSelector { pub struct GiteaSelector {
gitea_provider: DynGiteaProvider, gitea_provider: DynGiteaProvider,
git_provider: DynGitProvider, git_provider: DynGitProvider,
executor: DynExecutor, executor: DynExecutor,
ui: DynUI,
} }
impl GiteaSelector { impl GiteaSelector {
@ -19,11 +21,13 @@ impl GiteaSelector {
gitea_provider: DynGiteaProvider, gitea_provider: DynGiteaProvider,
git_provider: DynGitProvider, git_provider: DynGitProvider,
executor: DynExecutor, executor: DynExecutor,
ui: DynUI,
) -> Self { ) -> Self {
Self { Self {
gitea_provider, gitea_provider,
git_provider, git_provider,
executor, executor,
ui,
} }
} }
@ -33,6 +37,7 @@ impl GiteaSelector {
action_path: &PathBuf, action_path: &PathBuf,
action: &Action, action: &Action,
dryrun: bool, dryrun: bool,
interactive: bool,
) -> eyre::Result<()> { ) -> eyre::Result<()> {
tracing::info!("fetching repos"); tracing::info!("fetching repos");
for repo in &git.repositories { for repo in &git.repositories {
@ -52,6 +57,10 @@ impl GiteaSelector {
continue; continue;
} }
if interactive {
self.ui.confirm().await?;
}
if let Some(push) = &git.push { if let Some(push) = &git.push {
self.git_provider self.git_provider
.push_branch(repo, &push.pull_request.name) .push_branch(repo, &push.pull_request.name)

View File

@ -6,12 +6,14 @@ use crate::{
executor::executor::DynExecutor, executor::executor::DynExecutor,
git::{github::DynGitHubProvider, DynGitProvider}, git::{github::DynGitHubProvider, DynGitProvider},
schema::models::{Action, GitHub}, schema::models::{Action, GitHub},
ui::DynUI,
}; };
pub struct GitHubSelector { pub struct GitHubSelector {
github_provider: DynGitHubProvider, github_provider: DynGitHubProvider,
git_provider: DynGitProvider, git_provider: DynGitProvider,
executor: DynExecutor, executor: DynExecutor,
ui: DynUI,
} }
impl GitHubSelector { impl GitHubSelector {
@ -19,11 +21,13 @@ impl GitHubSelector {
github_provider: DynGitHubProvider, github_provider: DynGitHubProvider,
git_provider: DynGitProvider, git_provider: DynGitProvider,
executor: DynExecutor, executor: DynExecutor,
ui: DynUI,
) -> Self { ) -> Self {
Self { Self {
github_provider, github_provider,
git_provider, git_provider,
executor, executor,
ui,
} }
} }
@ -33,6 +37,7 @@ impl GitHubSelector {
action_path: &PathBuf, action_path: &PathBuf,
action: &Action, action: &Action,
dryrun: bool, dryrun: bool,
interactive: bool,
) -> eyre::Result<()> { ) -> eyre::Result<()> {
tracing::info!("fetching repos"); tracing::info!("fetching repos");
for repo in &git.repositories { for repo in &git.repositories {
@ -52,6 +57,10 @@ impl GitHubSelector {
continue; continue;
} }
if interactive {
self.ui.confirm().await?;
}
if let Some(push) = &git.push { if let Some(push) = &git.push {
self.git_provider self.git_provider
.push_branch(repo, &push.pull_request.name) .push_branch(repo, &push.pull_request.name)

View File

@ -0,0 +1,12 @@
pub mod terminal_ui;
use std::sync::Arc;
use async_trait::async_trait;
#[async_trait]
pub trait UI {
async fn confirm(&self) -> eyre::Result<()>;
}
pub type DynUI = Arc<dyn UI + Send + Sync>;

View File

@ -0,0 +1,47 @@
use std::{
io::{self, Write},
process::exit,
};
use async_trait::async_trait;
use super::UI;
pub struct TerminalUI {}
impl TerminalUI {
pub fn new() -> Self {
Self {}
}
pub async fn query_console(&self) -> eyre::Result<()> {
print!("Continue? ([Y]es/[N]o): ");
std::io::stdout().lock().flush()?;
let mut input = String::new();
let _ = io::stdin().read_line(&mut input)?;
if input.to_lowercase().starts_with("y") {
return Ok(());
} else if input.to_lowercase().starts_with("n") {
exit(0)
} else {
Err(eyre::anyhow!("input not valid"))
}
}
}
#[async_trait]
impl UI for TerminalUI {
async fn confirm(&self) -> eyre::Result<()> {
match self.query_console().await {
Ok(_) => Ok(()),
Err(e) => {
if e.to_string().starts_with("input not valid") {
self.query_console().await
} else {
Err(e)
}
}
}
}
}

View File

@ -22,6 +22,7 @@ use octopush_core::{
git_selector::GitSelector, gitea_selector::GiteaSelector, github_selector::GitHubSelector, git_selector::GitSelector, gitea_selector::GiteaSelector, github_selector::GitHubSelector,
}, },
storage::{local::LocalStorageEngine, DynStorageEngine}, storage::{local::LocalStorageEngine, DynStorageEngine},
ui::{terminal_ui::TerminalUI, DynUI},
}; };
pub struct ServiceRegister { pub struct ServiceRegister {
@ -35,6 +36,7 @@ pub struct ServiceRegister {
pub gitea_selector: Arc<GiteaSelector>, pub gitea_selector: Arc<GiteaSelector>,
pub github_provider: DynGitHubProvider, pub github_provider: DynGitHubProvider,
pub github_selector: Arc<GitHubSelector>, pub github_selector: Arc<GitHubSelector>,
pub ui: DynUI,
} }
impl ServiceRegister { impl ServiceRegister {
@ -57,11 +59,19 @@ impl ServiceRegister {
storage_engine.clone(), storage_engine.clone(),
gitea_client.clone(), gitea_client.clone(),
)); ));
let git_selector = Arc::new(GitSelector::new(git_provider.clone(), executor.clone()));
let ui = Arc::new(TerminalUI::new());
let git_selector = Arc::new(GitSelector::new(
git_provider.clone(),
executor.clone(),
ui.clone(),
));
let gitea_selector = Arc::new(GiteaSelector::new( let gitea_selector = Arc::new(GiteaSelector::new(
gitea_provider.clone(), gitea_provider.clone(),
git_provider.clone(), git_provider.clone(),
executor.clone(), executor.clone(),
ui.clone(),
)); ));
let github_client = Arc::new(DefaultGitHubClient::new(&github_client_options)?); let github_client = Arc::new(DefaultGitHubClient::new(&github_client_options)?);
let github_provider = Arc::new(DefaultGitHubProvider::new( let github_provider = Arc::new(DefaultGitHubProvider::new(
@ -73,6 +83,7 @@ impl ServiceRegister {
github_provider.clone(), github_provider.clone(),
git_provider.clone(), git_provider.clone(),
executor.clone(), executor.clone(),
ui.clone(),
)); ));
Ok(Self { Ok(Self {
@ -86,6 +97,7 @@ impl ServiceRegister {
gitea_selector, gitea_selector,
github_provider, github_provider,
github_selector, github_selector,
ui,
}) })
} }