Compare commits

..

No commits in common. "f2f7f3bae991604922670625b8151bc6458e3b03" and "60f5055c5ef81a2ae65f8f0fc994b9e80840f2f7" have entirely different histories.

3 changed files with 102 additions and 148 deletions

View File

@ -52,8 +52,6 @@ Upload a new version of the MineZ plugin from the MineZ-Dev and keep backups of
multi-ssh minez -u -S minez-dev plugins/MineZ-3.0.jar -a multi-ssh minez -u -S minez-dev plugins/MineZ-3.0.jar -a
``` ```
Upload of multiple files is also supported.
The program will show you an overview of what it will be doing before actually performing any of the actions: The program will show you an overview of what it will be doing before actually performing any of the actions:
``` ```
minez (minez3/plugins): minez (minez3/plugins):

View File

@ -1,7 +1,7 @@
use crate::server::Server; use crate::server::Server;
use std::ffi::{OsStr, OsString}; use std::ffi::OsString;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::path::{Path, PathBuf}; use std::path::PathBuf;
#[derive(Debug)] #[derive(Debug)]
pub struct ServerActions<'a> { pub struct ServerActions<'a> {
@ -12,12 +12,7 @@ pub struct ServerActions<'a> {
impl Display for ServerActions<'_> { impl Display for ServerActions<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!( write!(f, "{}: ({})", self.server.get_name(), self.working_directory.to_string_lossy())?;
f,
"{}: ({})",
self.server.get_name(),
self.working_directory.to_string_lossy()
)?;
for action in &self.actions { for action in &self.actions {
write!(f, "\n{}", action)?; write!(f, "\n{}", action)?;
} }
@ -28,35 +23,19 @@ impl Display for ServerActions<'_> {
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct FileAction { pub struct FileAction {
pub file: PathBuf, pub file: PathBuf,
pub file_name: OsString,
pub kind: Action, pub kind: Action,
} }
impl FileAction {
pub fn new<P>(file: P, kind: Action) -> Option<Self>
where
P: AsRef<Path>,
{
let file = PathBuf::from(file.as_ref());
let file_name = file.file_name()?.to_os_string();
Some(Self {
file,
file_name,
kind,
})
}
}
impl Display for FileAction { impl Display for FileAction {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match &self.kind { match &self.kind {
Action::Add => write!(f, "+ adding {}", self.file_name.to_string_lossy()), Action::Add => write!(f, "+ adding {}", self.file.to_string_lossy()),
Action::Replace => write!(f, "~ replacing {}", self.file_name.to_string_lossy()), Action::Replace => write!(f, "~ replacing {}", self.file.to_string_lossy()),
Action::Delete => write!(f, "- deleting {}", self.file_name.to_string_lossy()), Action::Delete => write!(f, "- deleting {}", self.file.to_string_lossy()),
Action::Rename { new_name } => write!( Action::Rename { new_name } => write!(
f, f,
"* renaming {} -> {}", "* renaming {} -> {}",
self.file_name.to_string_lossy(), self.file.to_string_lossy(),
new_name.to_string_lossy() new_name.to_string_lossy()
), ),
} }
@ -70,9 +49,3 @@ pub enum Action {
Delete, Delete,
Rename { new_name: OsString }, Rename { new_name: OsString },
} }
impl Action {
pub fn rename<S>(new_name: S) -> Self where S: AsRef<OsStr> {
Self::Rename {new_name: new_name.as_ref().to_owned()}
}
}

View File

@ -6,7 +6,7 @@ mod os_string_builder;
mod server; mod server;
use crate::action::{Action, FileAction, ServerActions}; use crate::action::{Action, FileAction, ServerActions};
use crate::command::{ExecutionError, LogRunnable, SpecificExecutionError}; use crate::command::{ExecutionError, LogRunnable};
use crate::file::{FileMatcher, FileNameInfo}; use crate::file::{FileMatcher, FileNameInfo};
use crate::logger::{LogLevel, Logger}; use crate::logger::{LogLevel, Logger};
use crate::os_string_builder::ReplaceWithOsStr; use crate::os_string_builder::ReplaceWithOsStr;
@ -61,8 +61,8 @@ enum Command {
/// Upload a file to the servers /// Upload a file to the servers
#[command(visible_short_flag_alias = 'u')] #[command(visible_short_flag_alias = 'u')]
Upload { Upload {
/// The files to upload /// The file to upload
files: Vec<PathBuf>, file: PathBuf,
/// The ssh server to get the file from. /// The ssh server to get the file from.
/// ///
/// When this option is set, the file path must be absolute, or relative to the server directory. /// When this option is set, the file path must be absolute, or relative to the server directory.
@ -178,7 +178,7 @@ fn main() -> Result<(), String> {
match args.command { match args.command {
Command::Upload { Command::Upload {
files, file,
file_server, file_server,
old_version_policy, old_version_policy,
upload_directory, upload_directory,
@ -200,35 +200,45 @@ fn main() -> Result<(), String> {
None => None, None => None,
}; };
//make sure files exist //make sure file exists and is a file
match &file_server { match &file_server {
Some(file_server) => match &file_server.address { Some(file_server) => {
match &file_server.address {
ServerAddress::Ssh { ssh_address } => { ServerAddress::Ssh { ssh_address } => {
for file in &files { match ShellCmd::new("ssh")
check_file_exists_on_server(file, ssh_address, &file_server.server_directory_path)?; .arg(ssh_address)
.arg(osf!("test -f ") + file_server.server_directory_path.join(&file))
.collect_output()
{
Ok(_) => {} //file exists on file server
Err(e) => {
match &e.error {
ExecutionError::StartError(_) => {
//error occurred
Err(format!(
"Failed to check whether file exists on file-server: {e}"
))?;
} }
ExecutionError::BadExitStatus(_) => {
//file does not exist on file server
Err("File doesn't exist on file server")?;
}
}
}
};
} }
ServerAddress::Localhost => { ServerAddress::Localhost => {
for file in &files { check_local_file_exists(file_server.server_directory_path.join(&file))?;
check_local_file_exists(file_server.server_directory_path.join(file))?; }
} }
} }
},
None => { None => {
for file in &files { check_local_file_exists(&file)?;
check_local_file_exists(file)?;
}
} }
} }
let file_details = files let file_name_info =
.iter() FileNameInfo::try_from(file.clone()).map_err(|e| format!("bad file: {e}"))?;
.map(|file| {
FileNameInfo::try_from(file.clone())
.map(|info| (PathBuf::from(file), info))
.map_err(|e| format!("Bad file '{}': {e}", file.to_string_lossy()))
})
.collect::<Result<Vec<_>, _>>()?;
//create overview of what has to be done on each server //create overview of what has to be done on each server
let actions = servers let actions = servers
@ -253,11 +263,8 @@ fn main() -> Result<(), String> {
let ls_output = ls_command let ls_output = ls_command
.collect_output() .collect_output()
.map_err(|e| format!("failed to query files: {e}"))?; .map_err(|e| format!("failed to query files: {e}"))?;
let ls_output = String::from_utf8_lossy(&ls_output.stdout); let output = String::from_utf8_lossy(&ls_output.stdout);
file_details
.iter()
.flat_map(|(file, file_name_info)| {
let mut file_matcher = let mut file_matcher =
FileMatcher::from(file_name.as_ref().unwrap_or(&file_name_info.name)); FileMatcher::from(file_name.as_ref().unwrap_or(&file_name_info.name));
if let Some(extension) = file_name_info.extension.as_ref() { if let Some(extension) = file_name_info.extension.as_ref() {
@ -266,48 +273,56 @@ fn main() -> Result<(), String> {
let file_name = file_name_info.to_full_file_name(); let file_name = file_name_info.to_full_file_name();
let add_action = FileAction::new(file, Action::Add).expect("path points to file"); let add_action = FileAction {
file: PathBuf::from(&file_name),
let mut ls_lines = ls_output.lines(); kind: Action::Add,
};
let mut files = output.lines();
match old_version_policy { match old_version_policy {
OldVersionPolicy::Ignore => { OldVersionPolicy::Ignore => {
vec![if ls_lines.any(|file| file == file_name) { vec![if files.any(|file| file == file_name) {
FileAction::new(&file_name, Action::Replace).expect("path points to file") FileAction {
file: PathBuf::from(&file_name),
kind: Action::Replace,
}
} else { } else {
add_action add_action
}] }]
} }
OldVersionPolicy::Archive => ls_lines OldVersionPolicy::Archive => files
.filter(|file| file_matcher.matches(file)) .filter(|file| file_matcher.matches(file))
.map(|file| { .map(|file| FileAction {
FileAction::new( file: PathBuf::from(file),
file, kind: Action::Rename {
Action::rename(format!("{file}{}", file.chars().last().unwrap_or('1'))), new_name: format!("{file}{}", file.chars().last().unwrap_or('1')).into(),
) },
.expect("path points to file")
}) })
.chain(once(add_action)) .chain(once(add_action))
.collect(), .collect(),
OldVersionPolicy::Delete => { OldVersionPolicy::Delete => {
let mut actions = ls_lines let mut actions: Vec<_> = files
.filter(|file| file_matcher.matches(file)) .filter(|file| file_matcher.matches(file))
.map(|file| { .map(|file| {
//special case -> file has the same name as current file, then we just need to replace it //special case -> file has the same name as current file, then we just need to replace it
if file == file_name { if file == file_name {
FileAction::new(file, Action::Replace).expect("path points to file") FileAction {
file: PathBuf::from(file),
kind: Action::Replace,
}
} else { } else {
FileAction::new(file, Action::Delete).expect("path points to file") FileAction {
file: PathBuf::from(file),
kind: Action::Delete,
}
} }
}) })
.collect::<Vec<_>>(); .collect();
if !actions.iter().any(|action| action.kind == Action::Replace) { if !actions.iter().any(|action| action.kind == Action::Replace) {
actions.push(add_action); actions.push(add_action);
} }
actions actions
} }
} }
})
.collect()
}, },
working_directory, working_directory,
}) })
@ -336,13 +351,11 @@ fn main() -> Result<(), String> {
match file_action.kind { match file_action.kind {
Action::Add | Action::Replace => { Action::Add | Action::Replace => {
let scp_source = match &file_server { let scp_source = match &file_server {
Some(file_server) => { Some(file_server) => osf!(match &file_server.address {
osf!(match &file_server.address { ServerAddress::Ssh{ ssh_address } => format!("{ssh_address}:"),
ServerAddress::Ssh { ssh_address } => format!("{ssh_address}:"),
ServerAddress::Localhost => "".to_string(), ServerAddress::Localhost => "".to_string(),
}) + file_server.server_directory_path.join(&file_action.file) }) + file_server.server_directory_path.join(&file),
} None => osf!(&file),
None => osf!(&file_action.file),
}; };
let scp_target = osf!(match &server.address { let scp_target = osf!(match &server.address {
ServerAddress::Ssh { ssh_address } => format!("{ssh_address}:"), ServerAddress::Ssh { ssh_address } => format!("{ssh_address}:"),
@ -535,36 +548,6 @@ where
Ok(()) Ok(())
} }
fn check_file_exists_on_server<P, S, D>(
path: P,
ssh_address: S,
server_directory: D,
) -> Result<(), String>
where
P: AsRef<Path>,
S: AsRef<str>,
D: AsRef<Path>,
{
let full_path = server_directory.as_ref().join(path);
match &ShellCmd::new("ssh")
.arg(ssh_address.as_ref())
.arg(osf!("test -f ") + &full_path)
.collect_output()
{
Ok(_) => Ok(()), //file exists on file server
Err(SpecificExecutionError {
error: ExecutionError::BadExitStatus(_), //test failed
..
}) => Err(format!(
"File '{}' doesn't exist on file server",
full_path.to_string_lossy()
)),
Err(e) => Err(format!(
"Failed to check whether file exists on file-server: {e}"
)),
}
}
fn get_home_directory() -> Result<PathBuf, String> { fn get_home_directory() -> Result<PathBuf, String> {
homedir::my_home() homedir::my_home()
.map_err(|e| format!("Failed to determine home directory: {e}")) .map_err(|e| format!("Failed to determine home directory: {e}"))