mirror of
https://github.com/qwjyh/xdbm
synced 2025-04-20 03:35:55 +09:00
new(sync): implement sync subcommand (WIP)
TODO - update CHANGELOG - refactor sync func
This commit is contained in:
parent
47b3a5e69d
commit
9316290d28
6 changed files with 137 additions and 19 deletions
|
@ -63,6 +63,12 @@ pub(crate) enum Commands {
|
||||||
Sync {
|
Sync {
|
||||||
/// Remote name to sync.
|
/// Remote name to sync.
|
||||||
remote_name: Option<String>,
|
remote_name: Option<String>,
|
||||||
|
/// Whether to use ssh-agent
|
||||||
|
#[arg(long)]
|
||||||
|
use_sshagent: bool,
|
||||||
|
/// Manually specify ssh key
|
||||||
|
#[arg(long)]
|
||||||
|
ssh_key: Option<PathBuf>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Check config files validity.
|
/// Check config files validity.
|
||||||
|
|
|
@ -40,9 +40,7 @@ fn clone_repo(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Cred::ssh_key(
|
Cred::ssh_key(
|
||||||
username_from_url
|
username_from_url.ok_or(git2::Error::from_str("No username found from the url"))?,
|
||||||
.context("No username found from the url")
|
|
||||||
.unwrap(),
|
|
||||||
None,
|
None,
|
||||||
key as &Path,
|
key as &Path,
|
||||||
passwd.as_deref(),
|
passwd.as_deref(),
|
||||||
|
@ -51,9 +49,7 @@ fn clone_repo(
|
||||||
// use ssh agent
|
// use ssh agent
|
||||||
info!("Using ssh agent to access the repository");
|
info!("Using ssh agent to access the repository");
|
||||||
Cred::ssh_key_from_agent(
|
Cred::ssh_key_from_agent(
|
||||||
username_from_url
|
username_from_url.ok_or(git2::Error::from_str("No username found from the url"))?,
|
||||||
.context("No username found from the url")
|
|
||||||
.unwrap(),
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
error!("no ssh_key and use_sshagent");
|
error!("no ssh_key and use_sshagent");
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use git2::Repository;
|
use git2::{Cred, PushOptions, RemoteCallbacks, Repository};
|
||||||
|
|
||||||
pub(crate) fn cmd_sync(config_dir: &PathBuf, remote_name: Option<String>) -> Result<()> {
|
pub(crate) fn cmd_sync(
|
||||||
|
config_dir: &PathBuf,
|
||||||
|
remote_name: Option<String>,
|
||||||
|
use_sshagent: bool,
|
||||||
|
ssh_key: Option<PathBuf>,
|
||||||
|
) -> Result<()> {
|
||||||
warn!("Experimental");
|
warn!("Experimental");
|
||||||
let repo = Repository::open(config_dir)?;
|
let repo = Repository::open(config_dir)?;
|
||||||
let remote_name = match remote_name {
|
let remote_name = match remote_name {
|
||||||
|
@ -16,7 +21,74 @@ pub(crate) fn cmd_sync(config_dir: &PathBuf, remote_name: Option<String>) -> Res
|
||||||
remotes.get(0).unwrap().to_string()
|
remotes.get(0).unwrap().to_string()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// using credentials
|
||||||
|
let mut callbacks = RemoteCallbacks::new();
|
||||||
|
callbacks
|
||||||
|
.credentials(|_url, username_from_url, _allowed_types| {
|
||||||
|
if let Some(key) = &ssh_key {
|
||||||
|
info!("Using provided ssh key to access the repository");
|
||||||
|
let passwd = match inquire::Password::new("SSH passphrase").prompt() {
|
||||||
|
std::result::Result::Ok(s) => Some(s),
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed to get ssh passphrase: {:?}", err);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Cred::ssh_key(
|
||||||
|
username_from_url
|
||||||
|
.ok_or(git2::Error::from_str("No username found from the url"))?,
|
||||||
|
None,
|
||||||
|
key as &Path,
|
||||||
|
passwd.as_deref(),
|
||||||
|
)
|
||||||
|
} else if use_sshagent {
|
||||||
|
// use ssh agent
|
||||||
|
info!("Using ssh agent to access the repository");
|
||||||
|
Cred::ssh_key_from_agent(
|
||||||
|
username_from_url
|
||||||
|
.ok_or(git2::Error::from_str("No username found from the url"))?,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
error!("no ssh_key and use_sshagent");
|
||||||
|
panic!("This option must be unreachable.")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.push_transfer_progress(|current, total, bytes| {
|
||||||
|
trace!("{current},\t{total},\t{bytes}");
|
||||||
|
});
|
||||||
|
callbacks.push_update_reference(|reference_name, status_msg| {
|
||||||
|
debug!("remote reference_name {reference_name}");
|
||||||
|
match status_msg {
|
||||||
|
None => {
|
||||||
|
info!("successfully pushed");
|
||||||
|
eprintln!("successfully pushed to {}", reference_name);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Some(status) => {
|
||||||
|
error!("failed to push: {}", status);
|
||||||
|
Err(git2::Error::from_str(&format!(
|
||||||
|
"failed to push to {}: {}",
|
||||||
|
reference_name, status
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let mut push_options = PushOptions::new();
|
||||||
|
push_options.remote_callbacks(callbacks);
|
||||||
let mut remote = repo.find_remote(&remote_name)?;
|
let mut remote = repo.find_remote(&remote_name)?;
|
||||||
remote.push(&[] as &[&str], None)?;
|
trace!("remote: {:?}", remote.name());
|
||||||
|
if remote.refspecs().len() != 1 {
|
||||||
|
warn!("multiple refspecs found");
|
||||||
|
}
|
||||||
|
trace!("refspec: {:?}", remote.get_refspec(0).unwrap().str());
|
||||||
|
trace!("refspec: {:?}", remote.get_refspec(0).unwrap().direction());
|
||||||
|
trace!("refspec: {:?}", repo.head().unwrap().name());
|
||||||
|
trace!("head is branch: {:?}", repo.head().unwrap().is_branch());
|
||||||
|
trace!("head is remote: {:?}", repo.head().unwrap().is_remote());
|
||||||
|
remote.push(
|
||||||
|
&[repo.head().unwrap().name().unwrap()] as &[&str],
|
||||||
|
Some(&mut push_options),
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
40
src/git.rs
Normal file
40
src/git.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use git2::{Cred, RemoteCallbacks};
|
||||||
|
use inquire::Password;
|
||||||
|
|
||||||
|
pub(crate) fn get_credential<'a>(
|
||||||
|
use_sshagent: bool,
|
||||||
|
ssh_key: Option<PathBuf>,
|
||||||
|
) -> RemoteCallbacks<'a> {
|
||||||
|
// using credentials
|
||||||
|
let mut callbacks = RemoteCallbacks::new();
|
||||||
|
callbacks.credentials(move |_url, username_from_url, _allowed_types| {
|
||||||
|
if let Some(key) = &ssh_key {
|
||||||
|
info!("Using provided ssh key to access the repository");
|
||||||
|
let passwd = match Password::new("SSH passphrase").prompt() {
|
||||||
|
std::result::Result::Ok(s) => Some(s),
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed to get ssh passphrase: {:?}", err);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Cred::ssh_key(
|
||||||
|
username_from_url.ok_or(git2::Error::from_str("No username found from the url"))?,
|
||||||
|
None,
|
||||||
|
key as &Path,
|
||||||
|
passwd.as_deref(),
|
||||||
|
)
|
||||||
|
} else if use_sshagent {
|
||||||
|
// use ssh agent
|
||||||
|
info!("Using ssh agent to access the repository");
|
||||||
|
Cred::ssh_key_from_agent(
|
||||||
|
username_from_url.ok_or(git2::Error::from_str("No username found from the url"))?,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
error!("no ssh_key and use_sshagent");
|
||||||
|
panic!("This option must be unreachable.")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
callbacks
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ mod cmd_status;
|
||||||
mod cmd_storage;
|
mod cmd_storage;
|
||||||
mod cmd_sync;
|
mod cmd_sync;
|
||||||
mod devices;
|
mod devices;
|
||||||
|
mod git;
|
||||||
mod inquire_filepath_completer;
|
mod inquire_filepath_completer;
|
||||||
mod storages;
|
mod storages;
|
||||||
mod util;
|
mod util;
|
||||||
|
@ -91,7 +92,11 @@ fn main() -> Result<()> {
|
||||||
Commands::Path {} => {
|
Commands::Path {} => {
|
||||||
println!("{}", &config_dir.display());
|
println!("{}", &config_dir.display());
|
||||||
}
|
}
|
||||||
Commands::Sync { remote_name } => cmd_sync::cmd_sync(&config_dir, remote_name)?,
|
Commands::Sync {
|
||||||
|
remote_name,
|
||||||
|
use_sshagent,
|
||||||
|
ssh_key,
|
||||||
|
} => cmd_sync::cmd_sync(&config_dir, remote_name, use_sshagent, ssh_key)?,
|
||||||
Commands::Status {
|
Commands::Status {
|
||||||
path,
|
path,
|
||||||
storage,
|
storage,
|
||||||
|
|
15
tests/cli.rs
15
tests/cli.rs
|
@ -313,15 +313,14 @@ mod integrated_test {
|
||||||
assert!(config_dir_2.join("backups").join("second.yml").exists());
|
assert!(config_dir_2.join("backups").join("second.yml").exists());
|
||||||
|
|
||||||
// sync
|
// sync
|
||||||
std::process::Command::new("git")
|
Command::cargo_bin("xdbm")?
|
||||||
.arg("push")
|
.arg("-c")
|
||||||
.current_dir(&config_dir_2)
|
.arg(config_dir_2.path())
|
||||||
|
.arg("sync")
|
||||||
|
.arg("-vvvv")
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success()
|
||||||
// let repo_2 = Repository::open(config_dir_2)?;
|
.stderr(predicate::str::contains("successfully pushed"));
|
||||||
// // return Err(anyhow!("{:?}", repo_2.remotes()?.iter().collect::<Vec<_>>()));
|
|
||||||
// let mut repo_2_remote = repo_2.find_remote(repo_2.remotes()?.get(0).unwrap())?;
|
|
||||||
// repo_2_remote.push(&[] as &[&str], None)?;
|
|
||||||
std::process::Command::new("git")
|
std::process::Command::new("git")
|
||||||
.arg("pull")
|
.arg("pull")
|
||||||
.current_dir(&config_dir_1)
|
.current_dir(&config_dir_1)
|
||||||
|
|
Loading…
Add table
Reference in a new issue