Merge pull request '15-allow-uploading-files-from-another-server' (#18) from 15-allow-uploading-files-from-another-server into master

Reviewed-on: https://stupstech.de/dev/Mr_Steppy/multi-ssh/pulls/18
This commit is contained in:
Leonard Steppy 2025-02-02 14:25:26 +01:00
commit 60f5055c5e
3 changed files with 87 additions and 13 deletions

View File

@ -47,14 +47,14 @@ Upload a new version of the MenuApi plugin and delete old versions from the serv
multi-ssh crea sky city minez lobby -u target/MenuApi-3.4.2.jar
```
Upload a new version of the MineZ plugin but keep backups of the older versions:
Upload a new version of the MineZ plugin from the MineZ-Dev and keep backups of the older versions:
```bash
multi-ssh minez -u target/MineZ-3.0.jar -a
multi-ssh minez -u -S minez-dev plugins/MineZ-3.0.jar -a
```
The program will show you an overview of what it will be doing before actually performing any of the actions:
```
minez:
minez (minez3/plugins):
* renaming MineZ-2.7.jar -> MineZ-2.7.jarr
+ adding MineZ-3.0.jar
Continue [Y|n]:

View File

@ -19,10 +19,6 @@ impl TryFrom<PathBuf> for FileNameInfo {
type Error = FileInfoError;
fn try_from(file: PathBuf) -> Result<Self, Self::Error> {
if !file.is_file() {
return Err(FileInfoError::NotAFile);
}
let file_name = file.file_name().ok_or(FileInfoError::NotAFile)?;
let file_name = file_name
.to_str()

View File

@ -6,7 +6,7 @@ mod os_string_builder;
mod server;
use crate::action::{Action, FileAction, ServerActions};
use crate::command::LogRunnable;
use crate::command::{ExecutionError, LogRunnable};
use crate::file::{FileMatcher, FileNameInfo};
use crate::logger::{LogLevel, Logger};
use crate::os_string_builder::ReplaceWithOsStr;
@ -18,7 +18,7 @@ use std::cell::LazyCell;
use std::hash::Hash;
use std::io::Write;
use std::iter::once;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{env, fs, io};
@ -63,6 +63,12 @@ enum Command {
Upload {
/// The file to upload
file: PathBuf,
/// 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.
/// The upload-directory has no influence on where the file will be taken from.
#[arg(short = 'S', long)]
file_server: Option<ServerReference>,
/// How to handle older versions of the file
#[arg(short = 'a', long, default_value = "delete", default_missing_value = "archive", num_args = 0..=1)]
old_version_policy: OldVersionPolicy,
@ -157,14 +163,14 @@ fn main() -> Result<(), String> {
.servers
.iter()
.map(|server_reference| {
let server_name = server_reference.get_identifier();
let server_identifier = server_reference.get_identifier();
server_reference
.clone()
.try_resolve_lazy(&mut configured_servers)
.map_err(|msg| format!("Can't resolve server directory for '{server_name}': {msg}"))
.map_err(|msg| format!("Can't resolve server directory for '{server_identifier}': {msg}"))
.and_then(|opt_server| {
opt_server.ok_or(format!(
"no server directory has been configured for server '{server_name}'"
"no server directory has been configured for server '{server_identifier}'"
))
})
})
@ -173,6 +179,7 @@ fn main() -> Result<(), String> {
match args.command {
Command::Upload {
file,
file_server,
old_version_policy,
upload_directory,
no_confirm,
@ -181,6 +188,55 @@ fn main() -> Result<(), String> {
require_non_empty_servers(&servers)?;
start_ssh_agent(&logger)?;
//resolve file server
let file_server = match file_server {
Some(server_reference) => {
let file_server_identifier = server_reference.get_identifier().to_string();
let server = server_reference.try_resolve_lazy(&mut configured_servers)
.map_err(|e| format!("Can't resolve server directory for file-server '{file_server_identifier}': {e}"))?
.ok_or_else(|| format!("no server directory has been configured for file-server '{file_server_identifier}'"))?;
Some(server)
}
None => None,
};
//make sure file exists and is a file
match &file_server {
Some(file_server) => {
match &file_server.address {
ServerAddress::Ssh { ssh_address } => {
match ShellCmd::new("ssh")
.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 => {
check_local_file_exists(file_server.server_directory_path.join(&file))?;
}
}
}
None => {
check_local_file_exists(&file)?;
}
}
let file_name_info =
FileNameInfo::try_from(file.clone()).map_err(|e| format!("bad file: {e}"))?;
@ -294,12 +350,19 @@ fn main() -> Result<(), String> {
for file_action in server_actions.actions {
match file_action.kind {
Action::Add | Action::Replace => {
let scp_source = match &file_server {
Some(file_server) => osf!(match &file_server.address {
ServerAddress::Ssh{ ssh_address } => format!("{ssh_address}:"),
ServerAddress::Localhost => "".to_string(),
}) + file_server.server_directory_path.join(&file),
None => osf!(&file),
};
let scp_target = osf!(match &server.address {
ServerAddress::Ssh { ssh_address } => format!("{ssh_address}:"),
ServerAddress::Localhost => "".to_string(),
}) + &server_actions.working_directory;
ShellCmd::new("scp")
.arg(file.clone())
.arg(scp_source)
.arg(scp_target)
.run(&logger)
.map_err(|e| format!("upload failure: {e}"))?;
@ -470,6 +533,21 @@ fn main() -> Result<(), String> {
Ok(())
}
fn check_local_file_exists<P>(path: P) -> Result<(), String>
where
P: AsRef<Path>,
{
let path = path.as_ref();
if !path.is_file() {
return Err(format!(
"{} does not point to a file",
path.to_string_lossy()
));
}
Ok(())
}
fn get_home_directory() -> Result<PathBuf, String> {
homedir::my_home()
.map_err(|e| format!("Failed to determine home directory: {e}"))