coliru

A minimal, flexible, dotfile installer
git clone https://git.ashermorgan.net/coliru/
Log | Files | Refs | README

commit d53e8d5f6a9eb93cbc22f1374e36a75f6e8a83d0
parent 314cf9940496f884d17acb59b14578268c111fe2
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Thu,  4 Jul 2024 13:35:18 -0700

Implement run command for remote hosts

Diffstat:
Msrc/core.rs | 81++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/ssh.rs | 6+++---
Mtests/common/mod.rs | 2+-
Mtests/ssh.rs | 65++++++++++++++++++++++++++++++++---------------------------------
4 files changed, 79 insertions(+), 75 deletions(-)

diff --git a/src/core.rs b/src/core.rs @@ -3,7 +3,7 @@ use std::path::Path; use super::manifest::{CopyLinkOptions, RunOptions, parse_manifest_file}; use super::tags::tags_match; use super::local::{copy_file, link_file, run_script}; -use super::ssh::{send_staged_files, stage_file}; +use super::ssh::{send_command, send_staged_files, stage_file}; use tempfile::tempdir; /// Execute the steps in a coliru manifest file according to a set of tag rules @@ -34,47 +34,30 @@ pub fn execute_manifest_file(path: &Path, tag_rules: Vec<String>, host: &str, let step_str = format!("[{}/{}]", i+1, manifest.steps.len()); - if host == "" { - execute_copies(&step.copy, dry_run, &step_str); - } else { - execute_copies_remote(&step.copy, host, temp_dir.path(), dry_run, - &step_str); - } + execute_copies(&step.copy, host, temp_dir.path(), dry_run, &step_str); + if !copy && host == "" { execute_links(&step.link, dry_run, &step_str); - } else if host != "" { - execute_copies_remote(&step.link, host, temp_dir.path(), dry_run, - &step_str); } else { - execute_copies(&step.link, dry_run, &step_str); - } - execute_runs(&step.run, &tag_rules, host, dry_run, &step_str); - } -} - -/// Execute a set of copy commands on the local machine -fn execute_copies(copies: &[CopyLinkOptions], dry_run: bool, step_str: &str) { - for copy in copies { - print!("{} Copy {} to {}", step_str, copy.src, copy.dst); - - if dry_run { - println!(" (DRY RUN)"); - continue; + execute_copies(&step.link, host, temp_dir.path(), dry_run, + &step_str); } - println!(""); - if let Err(why) = copy_file(&copy.src, &copy.dst) { - eprintln!(" Error: {}", why); - } + execute_runs(&step.run, &tag_rules, host, temp_dir.path(), dry_run, + &step_str); } } -/// Execute a set of copy commands on a remote machine -fn execute_copies_remote(copies: &[CopyLinkOptions], host: &str, - staging_dir: &Path, dry_run: bool, step_str: &str) { +/// Execute a set of copy commands +fn execute_copies(copies: &[CopyLinkOptions], host: &str, staging_dir: &Path, + dry_run: bool, step_str: &str) { for copy in copies { - print!("{} Copy {} to {}:{}", step_str, copy.src, host, copy.dst); + if host == "" { + print!("{} Copy {} to {}", step_str, copy.src, copy.dst); + } else { + print!("{} Copy {} to {}:{}", step_str, copy.src, host, copy.dst); + } if dry_run { println!(" (DRY RUN)"); @@ -82,8 +65,14 @@ fn execute_copies_remote(copies: &[CopyLinkOptions], host: &str, } println!(""); - if let Err(why) = stage_file(&copy.src, &copy.dst, staging_dir) { - eprintln!(" Error: {}", why); + if host == "" { + if let Err(why) = copy_file(&copy.src, &copy.dst) { + eprintln!(" Error: {}", why); + } + } else { + if let Err(why) = stage_file(&copy.src, &copy.dst, staging_dir) { + eprintln!(" Error: {}", why); + } } } @@ -94,7 +83,7 @@ fn execute_copies_remote(copies: &[CopyLinkOptions], host: &str, } } -/// Execute a set of link commands on the local machine +/// Execute a set of link commands fn execute_links(links: &[CopyLinkOptions], dry_run: bool, step_str: &str) { for link in links { print!("{} Link {} to {}", step_str, link.src, link.dst); @@ -111,9 +100,21 @@ fn execute_links(links: &[CopyLinkOptions], dry_run: bool, step_str: &str) { } } -/// Execute a set of run commands on the local machine +/// Execute a set of run commands fn execute_runs(runs: &[RunOptions], tag_rules: &[String], host: &str, - dry_run: bool, step_str: &str) { + staging_dir: &Path, dry_run: bool, step_str: &str) { + + if !dry_run && host != "" { + for run in runs { + if let Err(why) = stage_file(&run.src, &run.src, staging_dir) { + eprintln!("Error: {}", why); + } + } + + if let Err(why) = send_staged_files(staging_dir, host) { + eprintln!("Error: {}", why); + } + } for run in runs { let postfix = run.postfix.replace("$COLIRU_RULES", &tag_rules.join(" ")); @@ -135,7 +136,11 @@ fn execute_runs(runs: &[RunOptions], tag_rules: &[String], host: &str, eprintln!(" Error: {}", why); } } else { - eprintln!(" Error: not implemented"); + let cmd = format!("cd .coliru && {} {} {}", &run.prefix, &run.src, + &postfix); + if let Err(why) = send_command(&cmd, host) { + eprintln!(" Error: {}", why); + } } } } diff --git a/src/ssh.rs b/src/ssh.rs @@ -22,7 +22,7 @@ pub fn stage_file(src: &str, dst: &str, staging_dir: &Path) -> Result<(), String .into(); // Resolve relative paths to home staging directory: - _dst = home_dir.join(_dst); + _dst = home_dir.join(".coliru").join(_dst); // Resolve other absolute paths to root staging directory: if !_dst.starts_with(home_dir) { @@ -88,7 +88,6 @@ pub fn send_command(command: &str, host: &str) -> Result<(), String> { // SSH options and port for test server hard coded for now cmd.args(["-o", "StrictHostKeyChecking=no", "-p", "2222"]); } - println!("{host} {command} running"); cmd.args([host, command]); let status = cmd.status().map_err(|why| why.to_string())?; @@ -130,7 +129,8 @@ mod tests { let src = tmp.local.join("foo"); let dst = "dir/bar"; - let dst_real = tmp.local.join("home").join("dir").join("bar"); + let dst_real = tmp.local.join("home").join(".coliru").join("dir") + .join("bar"); let staging = &tmp.local; write_file(&src, "contents of foo"); diff --git a/tests/common/mod.rs b/tests/common/mod.rs @@ -115,7 +115,7 @@ pub fn setup_e2e_ssh(name: &str) -> (TempDirs, Command) { /// scripts/*) will be replaced with the value of home_dir and script_dir /// respectively to ensures that dotfiles are isolated across tests when /// necessary. -pub fn copy_manifest(dir: &Path, home_dir: &str, script_dir: &str) { +fn copy_manifest(dir: &Path, home_dir: &str, script_dir: &str) { let examples = env::current_exe().unwrap().parent().unwrap().to_path_buf() .join("../../../examples/test"); diff --git a/tests/ssh.rs b/tests/ssh.rs @@ -12,27 +12,27 @@ fn test_ssh_standard() { let (dirs, mut cmd) = setup_e2e_ssh("test_ssh_standard"); cmd.args(["manifest.yml", "-t", "linux"]); - let expected_stdout = format!("\ + let expected = format!("\ [1/3] Copy gitconfig to {SSH_HOST}:~/test_ssh_standard/.gitconfig [2/3] Copy bashrc to {SSH_HOST}:~/test_ssh_standard/.bashrc [2/3] Copy vimrc to {SSH_HOST}:~/test_ssh_standard/.vimrc [2/3] Run sh test_ssh_standard/script.sh arg1 linux on {SSH_HOST} +script.sh called with arg1 linux "); - let expected_stderr = " Error: not implemented\n"; - assert_eq!(&stderr_to_string(&mut cmd), expected_stderr); - assert_eq!(stdout_to_string(&mut cmd), expected_stdout); + assert_eq!(&stderr_to_string(&mut cmd), ""); + assert_eq!(stdout_to_string(&mut cmd), expected); // Assert files are correctly copied/run let bash_contents = read_file(&dirs.ssh.join(".bashrc")); let git_contents = read_file(&dirs.ssh.join(".gitconfig")); let vim1_contents = read_file(&dirs.ssh.join(".vimrc")); let vim2_exists = dirs.ssh.join("_vimrc").exists(); - // let log_contents = read_file(&dirs.local.join("log.txt")); + let log_contents = read_file(&dirs.ssh.join("log.txt")); assert_eq!(bash_contents, "bash #1\n"); assert_eq!(git_contents, "git #1\n"); assert_eq!(vim1_contents, "vim #1\n"); assert_eq!(vim2_exists, false); - // assert_eq!(log_contents, "script.sh called with arg1 linux\n"); + assert_eq!(log_contents, "script.sh called with arg1 linux\n"); } #[test] @@ -41,26 +41,26 @@ fn test_ssh_run_alternate_tag_rules_1() { let (dirs, mut cmd) = setup_e2e_ssh("test_ssh_run_alternate_tag_rules_1"); cmd.args(["manifest.yml", "-t", "linux", "^windows"]); - let expected_stdout = format!("\ + let expected = format!("\ [2/3] Copy bashrc to {SSH_HOST}:~/test_ssh_run_alternate_tag_rules_1/.bashrc [2/3] Copy vimrc to {SSH_HOST}:~/test_ssh_run_alternate_tag_rules_1/.vimrc [2/3] Run sh test_ssh_run_alternate_tag_rules_1/script.sh arg1 linux ^windows on {SSH_HOST} +script.sh called with arg1 linux ^windows "); - let expected_stderr = " Error: not implemented\n"; - assert_eq!(&stderr_to_string(&mut cmd), expected_stderr); - assert_eq!(stdout_to_string(&mut cmd), expected_stdout); + assert_eq!(&stderr_to_string(&mut cmd), ""); + assert_eq!(stdout_to_string(&mut cmd), expected); // Assert files are correctly copied/run let bash_contents = read_file(&dirs.ssh.join(".bashrc")); let git_exists = dirs.ssh.join(".gitconfig").exists(); let vim1_contents = read_file(&dirs.ssh.join(".vimrc")); let vim2_exists = dirs.ssh.join("_vimrc").exists(); - // let log_contents = read_file(&dirs.local.join("log.txt")); + let log_contents = read_file(&dirs.ssh.join("log.txt")); assert_eq!(bash_contents, "bash #1\n"); assert_eq!(git_exists, false); assert_eq!(vim1_contents, "vim #1\n"); assert_eq!(vim2_exists, false); - // assert_eq!(log_contents, "script.sh called with arg1 linux ^windows\n"); + assert_eq!(log_contents, "script.sh called with arg1 linux ^windows\n"); } #[test] @@ -69,27 +69,27 @@ fn test_ssh_run_alternate_tag_rules_2() { let (dirs, mut cmd) = setup_e2e_ssh("test_ssh_run_alternate_tag_rules_2"); cmd.args(["manifest.yml", "-t", "macos"]); - let expected_stdout = format!("\ + let expected = format!("\ [1/3] Copy gitconfig to {SSH_HOST}:~/test_ssh_run_alternate_tag_rules_2/.gitconfig [2/3] Copy bashrc to {SSH_HOST}:~/test_ssh_run_alternate_tag_rules_2/.bashrc [2/3] Copy vimrc to {SSH_HOST}:~/test_ssh_run_alternate_tag_rules_2/.vimrc [2/3] Run sh test_ssh_run_alternate_tag_rules_2/script.sh arg1 macos on {SSH_HOST} +script.sh called with arg1 macos "); - let expected_stderr = " Error: not implemented\n"; - assert_eq!(&stderr_to_string(&mut cmd), expected_stderr); - assert_eq!(stdout_to_string(&mut cmd), expected_stdout); + assert_eq!(&stderr_to_string(&mut cmd), ""); + assert_eq!(stdout_to_string(&mut cmd), expected); // Assert files are correctly copied/run let bash_contents = read_file(&dirs.ssh.join(".bashrc")); let git_contents = read_file(&dirs.ssh.join(".gitconfig")); let vim1_contents = read_file(&dirs.ssh.join(".vimrc")); let vim2_exists = dirs.ssh.join("_vimrc").exists(); - // let log_contents = read_file(&dirs.local.join("log.txt")); + let log_contents = read_file(&dirs.ssh.join("log.txt")); assert_eq!(bash_contents, "bash #1\n"); assert_eq!(git_contents, "git #1\n"); assert_eq!(vim1_contents, "vim #1\n"); assert_eq!(vim2_exists, false); - // assert_eq!(log_contents, "script.sh called with arg1 macos\n"); + assert_eq!(log_contents, "script.sh called with arg1 macos\n"); } #[test] @@ -111,12 +111,12 @@ fn test_ssh_dry_run() { let git_exists = dirs.ssh.join(".gitconfig").exists(); let vim1_exists = dirs.ssh.join(".vimrc").exists(); let vim2_exists = dirs.ssh.join("_vimrc").exists(); - // let log_exists = dirs.local.join("log.txt").exists(); + let log_exists = dirs.ssh.join("log.txt").exists(); assert_eq!(bash_exists, false); assert_eq!(git_exists, false); assert_eq!(vim1_exists, false); assert_eq!(vim2_exists, false); - // assert_eq!(log_exists, false); + assert_eq!(log_exists, false); } #[test] @@ -125,27 +125,27 @@ fn test_ssh_copy() { let (dirs, mut cmd) = setup_e2e_ssh("test_ssh_copy"); cmd.args(["manifest.yml", "--copy", "-t", "linux"]); - let expected_stdout = format!("\ + let expected = format!("\ [1/3] Copy gitconfig to {SSH_HOST}:~/test_ssh_copy/.gitconfig [2/3] Copy bashrc to {SSH_HOST}:~/test_ssh_copy/.bashrc [2/3] Copy vimrc to {SSH_HOST}:~/test_ssh_copy/.vimrc [2/3] Run sh test_ssh_copy/script.sh arg1 linux on {SSH_HOST} +script.sh called with arg1 linux "); - let expected_stderr = " Error: not implemented\n"; - assert_eq!(&stderr_to_string(&mut cmd), expected_stderr); - assert_eq!(stdout_to_string(&mut cmd), expected_stdout); + assert_eq!(&stderr_to_string(&mut cmd), ""); + assert_eq!(stdout_to_string(&mut cmd), expected); // Assert files are correctly copied/run let bash_contents = read_file(&dirs.ssh.join(".bashrc")); let git_contents = read_file(&dirs.ssh.join(".gitconfig")); let vim1_contents = read_file(&dirs.ssh.join(".vimrc")); let vim2_exists = dirs.ssh.join("_vimrc").exists(); - // let log_contents = read_file(&dirs.local.join("log.txt")); + let log_contents = read_file(&dirs.ssh.join("log.txt")); assert_eq!(bash_contents, "bash #1\n"); assert_eq!(git_contents, "git #1\n"); assert_eq!(vim1_contents, "vim #1\n"); assert_eq!(vim2_exists, false); - // assert_eq!(log_contents, "script.sh called with arg1 linux\n"); + assert_eq!(log_contents, "script.sh called with arg1 linux\n"); } #[test] @@ -153,7 +153,7 @@ fn test_ssh_copy() { fn test_ssh_run_failure() { let (dirs, mut cmd) = setup_e2e_ssh("test_ssh_run_failure"); cmd.args(["manifest.yml", "-t", "linux"]); - write_file(&dirs.local.join("script.sh"), "exit 1"); + write_file(&dirs.local.join("test_ssh_run_failure/script.sh"), "exit 1"); let expected_stdout = format!("\ [1/3] Copy gitconfig to {SSH_HOST}:~/test_ssh_run_failure/.gitconfig @@ -161,7 +161,7 @@ fn test_ssh_run_failure() { [2/3] Copy vimrc to {SSH_HOST}:~/test_ssh_run_failure/.vimrc [2/3] Run sh test_ssh_run_failure/script.sh arg1 linux on {SSH_HOST} "); - let expected_stderr = " Error: not implemented\n"; + let expected_stderr = " Error: SSH exited with exit status: 1\n"; assert_eq!(&stderr_to_string(&mut cmd), expected_stderr); assert_eq!(stdout_to_string(&mut cmd), expected_stdout); @@ -188,18 +188,17 @@ fn test_ssh_missing_file() { [2/3] Copy bashrc to {SSH_HOST}:~/test_ssh_missing_file/.bashrc [2/3] Copy vimrc to {SSH_HOST}:~/test_ssh_missing_file/.vimrc [2/3] Run sh test_ssh_missing_file/script.sh arg1 linux on {SSH_HOST} +script.sh called with arg1 linux "); - let expected_stderr = " Error: No such file or directory (os error 2) - Error: not implemented -"; + let expected_stderr = " Error: No such file or directory (os error 2)\n"; assert_eq!(&stderr_to_string(&mut cmd), expected_stderr); assert_eq!(stdout_to_string(&mut cmd), expected_stdout); // Assert files are correctly copied/run let bash_contents = read_file(&dirs.ssh.join(".bashrc")); let git_contents = read_file(&dirs.ssh.join(".gitconfig")); - // let log_contents = read_file(&dirs.ssh.join("log.txt")); + let log_contents = read_file(&dirs.ssh.join("log.txt")); assert_eq!(bash_contents, "bash #1\n"); assert_eq!(git_contents, "git #1\n"); - // assert_eq!(log_contents, "script.sh called with arg1 linux\n"); + assert_eq!(log_contents, "script.sh called with arg1 linux\n"); }