mirror of
https://github.com/qwjyh/xdbm
synced 2025-04-11 18:17:55 +09:00
Use git cli in sync subcommand, and make custom implementation optional (#27)
* new: add use_cl option and separate to function * refactor: format * new(sync): add option to use git cli * change(sync)!: now it use git cli by default * lint: remove unnecessary ref * fix(sync): use stderr for log prints * update: CHANGELOG
This commit is contained in:
parent
851c0259a1
commit
e9c1872d79
6 changed files with 110 additions and 30 deletions
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Option to use `git` cli in `sync` subcommand. This is now the default (#27)
|
||||||
|
|
||||||
## [0.4.0] - 2025-03-01
|
## [0.4.0] - 2025-03-01
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! CLI arguments
|
//! CLI arguments
|
||||||
|
|
||||||
use crate::path;
|
|
||||||
use crate::PathBuf;
|
use crate::PathBuf;
|
||||||
|
use crate::path;
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use clap_verbosity_flag::Verbosity;
|
use clap_verbosity_flag::Verbosity;
|
||||||
|
@ -63,6 +63,9 @@ pub(crate) enum Commands {
|
||||||
Sync {
|
Sync {
|
||||||
/// Remote name to sync.
|
/// Remote name to sync.
|
||||||
remote_name: Option<String>,
|
remote_name: Option<String>,
|
||||||
|
/// Use custom git implementation.
|
||||||
|
#[arg(short, long)]
|
||||||
|
use_libgit2: bool,
|
||||||
/// Whether to use ssh-agent
|
/// Whether to use ssh-agent
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
use_sshagent: bool,
|
use_sshagent: bool,
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
//! Initialize xdbm for the device.
|
//! Initialize xdbm for the device.
|
||||||
|
|
||||||
use crate::backups::Backups;
|
use crate::backups::Backups;
|
||||||
use crate::storages::{Storages, STORAGESFILE};
|
use crate::storages::{STORAGESFILE, Storages};
|
||||||
use crate::{
|
use crate::{
|
||||||
add_and_commit, backups, full_status, get_devices, write_devices, Device, DEVICESFILE,
|
DEVICESFILE, Device, add_and_commit, backups,
|
||||||
|
devices::{get_devices, write_devices},
|
||||||
|
full_status,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context, Ok, Result};
|
use anyhow::{Context, Ok, Result, anyhow};
|
||||||
use core::panic;
|
use core::panic;
|
||||||
use git2::{Cred, RemoteCallbacks, Repository};
|
use git2::{Cred, RemoteCallbacks, Repository};
|
||||||
use inquire::Password;
|
use inquire::Password;
|
||||||
|
|
|
@ -1,16 +1,77 @@
|
||||||
use std::{
|
use std::{
|
||||||
io::{self, Write},
|
io::{self, Write},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
process,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{Context, Result, anyhow};
|
||||||
use git2::{build::CheckoutBuilder, Cred, FetchOptions, PushOptions, RemoteCallbacks, Repository};
|
use git2::{Cred, FetchOptions, PushOptions, RemoteCallbacks, Repository, build::CheckoutBuilder};
|
||||||
|
|
||||||
pub(crate) fn cmd_sync(
|
pub(crate) fn cmd_sync(
|
||||||
config_dir: &PathBuf,
|
config_dir: &PathBuf,
|
||||||
remote_name: Option<String>,
|
remote_name: Option<String>,
|
||||||
use_sshagent: bool,
|
use_sshagent: bool,
|
||||||
ssh_key: Option<PathBuf>,
|
ssh_key: Option<PathBuf>,
|
||||||
|
use_libgit2: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
if use_libgit2 {
|
||||||
|
cmd_sync_custom(config_dir, remote_name, use_sshagent, ssh_key)
|
||||||
|
} else {
|
||||||
|
cmd_sync_cl(config_dir, remote_name, ssh_key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmd_sync_cl(
|
||||||
|
config_dir: &PathBuf,
|
||||||
|
remote_name: Option<String>,
|
||||||
|
ssh_key: Option<PathBuf>,
|
||||||
|
) -> Result<()> {
|
||||||
|
info!("cmd_sync (command line version)");
|
||||||
|
|
||||||
|
trace!("pull");
|
||||||
|
let args = |cmd| {
|
||||||
|
let mut args = vec![cmd];
|
||||||
|
if let Some(ref remote_name) = remote_name {
|
||||||
|
args.push(remote_name.clone());
|
||||||
|
}
|
||||||
|
if let Some(ref ssh_key) = ssh_key {
|
||||||
|
args.push("-i".to_string());
|
||||||
|
args.push(ssh_key.to_str().unwrap().to_owned());
|
||||||
|
}
|
||||||
|
args
|
||||||
|
};
|
||||||
|
let git_pull_result = process::Command::new("git")
|
||||||
|
.args(args("pull".to_owned()))
|
||||||
|
.current_dir(config_dir)
|
||||||
|
.status()
|
||||||
|
.context("error while executing git pull")?
|
||||||
|
.success();
|
||||||
|
if git_pull_result {
|
||||||
|
eprintln!("git pull completed");
|
||||||
|
} else {
|
||||||
|
return Err(anyhow!("failed to complete git pull"));
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!("push");
|
||||||
|
let git_push_result = process::Command::new("git")
|
||||||
|
.args(args("push".to_owned()))
|
||||||
|
.current_dir(config_dir)
|
||||||
|
.status()
|
||||||
|
.context("error while executing git push")?
|
||||||
|
.success();
|
||||||
|
if git_push_result {
|
||||||
|
eprintln!("git push completed");
|
||||||
|
} else {
|
||||||
|
return Err(anyhow!("failed to complete git push"));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmd_sync_custom(
|
||||||
|
config_dir: &PathBuf,
|
||||||
|
remote_name: Option<String>,
|
||||||
|
use_sshagent: bool,
|
||||||
|
ssh_key: Option<PathBuf>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
info!("cmd_sync");
|
info!("cmd_sync");
|
||||||
let repo = Repository::open(config_dir)?;
|
let repo = Repository::open(config_dir)?;
|
||||||
|
@ -81,13 +142,13 @@ where
|
||||||
})
|
})
|
||||||
.transfer_progress(|progress| {
|
.transfer_progress(|progress| {
|
||||||
if progress.received_objects() == progress.total_objects() {
|
if progress.received_objects() == progress.total_objects() {
|
||||||
print!(
|
eprint!(
|
||||||
"Resolving deltas {}/{}\r",
|
"Resolving deltas {}/{}\r",
|
||||||
progress.indexed_deltas(),
|
progress.indexed_deltas(),
|
||||||
progress.total_deltas()
|
progress.total_deltas()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
print!(
|
eprint!(
|
||||||
"Received {}/{} objects ({}) in {} bytes\r",
|
"Received {}/{} objects ({}) in {} bytes\r",
|
||||||
progress.received_objects(),
|
progress.received_objects(),
|
||||||
progress.total_objects(),
|
progress.total_objects(),
|
||||||
|
@ -95,7 +156,7 @@ where
|
||||||
progress.received_bytes(),
|
progress.received_bytes(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
io::stdout().flush().unwrap();
|
io::stderr().flush().unwrap();
|
||||||
true
|
true
|
||||||
})
|
})
|
||||||
.sideband_progress(|text| {
|
.sideband_progress(|text| {
|
||||||
|
@ -149,7 +210,7 @@ fn pull(
|
||||||
.context("Failed to fetch (pull)")?;
|
.context("Failed to fetch (pull)")?;
|
||||||
let stats = remote.stats();
|
let stats = remote.stats();
|
||||||
if stats.local_objects() > 0 {
|
if stats.local_objects() > 0 {
|
||||||
println!(
|
eprintln!(
|
||||||
"\rReceived {}/{} objects in {} bytes (used {} local objects)",
|
"\rReceived {}/{} objects in {} bytes (used {} local objects)",
|
||||||
stats.indexed_objects(),
|
stats.indexed_objects(),
|
||||||
stats.total_objects(),
|
stats.total_objects(),
|
||||||
|
@ -157,7 +218,7 @@ fn pull(
|
||||||
stats.local_objects(),
|
stats.local_objects(),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
println!(
|
eprintln!(
|
||||||
"\rReceived {}/{} objects in {} bytes",
|
"\rReceived {}/{} objects in {} bytes",
|
||||||
stats.indexed_objects(),
|
stats.indexed_objects(),
|
||||||
stats.total_objects(),
|
stats.total_objects(),
|
||||||
|
@ -198,7 +259,7 @@ fn pull(
|
||||||
None => String::from_utf8_lossy(ref_remote.name_bytes()).to_string(),
|
None => String::from_utf8_lossy(ref_remote.name_bytes()).to_string(),
|
||||||
};
|
};
|
||||||
let msg = format!("Fast-Forward: Setting {} to id: {}", name, fetch_head.id());
|
let msg = format!("Fast-Forward: Setting {} to id: {}", name, fetch_head.id());
|
||||||
println!("{}", msg);
|
eprintln!("{}", msg);
|
||||||
ref_remote
|
ref_remote
|
||||||
.set_target(fetch_head.id(), &msg)
|
.set_target(fetch_head.id(), &msg)
|
||||||
.context("failed to set target")?;
|
.context("failed to set target")?;
|
||||||
|
@ -238,7 +299,7 @@ fn push(
|
||||||
ssh_key: Option<&PathBuf>,
|
ssh_key: Option<&PathBuf>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
debug!("push");
|
debug!("push");
|
||||||
let callbacks = remote_callback(&use_sshagent, ssh_key);
|
let callbacks = remote_callback(use_sshagent, ssh_key);
|
||||||
let mut push_options = PushOptions::new();
|
let mut push_options = PushOptions::new();
|
||||||
push_options.remote_callbacks(callbacks);
|
push_options.remote_callbacks(callbacks);
|
||||||
let num_push_refspecs = remote
|
let num_push_refspecs = remote
|
||||||
|
|
|
@ -23,7 +23,7 @@ use std::path::{self, PathBuf};
|
||||||
use storages::Storages;
|
use storages::Storages;
|
||||||
|
|
||||||
use crate::cmd_args::{BackupSubCommands, Cli, Commands, StorageCommands};
|
use crate::cmd_args::{BackupSubCommands, Cli, Commands, StorageCommands};
|
||||||
use devices::{Device, DEVICESFILE, *};
|
use devices::{DEVICESFILE, Device};
|
||||||
|
|
||||||
mod backups;
|
mod backups;
|
||||||
mod cmd_args;
|
mod cmd_args;
|
||||||
|
@ -94,9 +94,10 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
Commands::Sync {
|
Commands::Sync {
|
||||||
remote_name,
|
remote_name,
|
||||||
|
use_libgit2,
|
||||||
use_sshagent,
|
use_sshagent,
|
||||||
ssh_key,
|
ssh_key,
|
||||||
} => cmd_sync::cmd_sync(&config_dir, remote_name, use_sshagent, ssh_key)?,
|
} => cmd_sync::cmd_sync(&config_dir, remote_name, use_sshagent, ssh_key, use_libgit2)?,
|
||||||
Commands::Status {
|
Commands::Status {
|
||||||
path,
|
path,
|
||||||
storage,
|
storage,
|
||||||
|
|
40
tests/cli.rs
40
tests/cli.rs
|
@ -5,8 +5,8 @@ mod integrated_test {
|
||||||
path,
|
path,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Ok, Result};
|
use anyhow::{Context, Ok, Result, anyhow};
|
||||||
use assert_cmd::{assert::OutputAssertExt, Command};
|
use assert_cmd::{Command, assert::OutputAssertExt};
|
||||||
use git2::Repository;
|
use git2::Repository;
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use predicates::{boolean::PredicateBooleanExt, prelude::predicate};
|
use predicates::{boolean::PredicateBooleanExt, prelude::predicate};
|
||||||
|
@ -73,13 +73,22 @@ mod integrated_test {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_sync_cmd(config_dir: &path::Path) -> Result<()> {
|
fn run_sync_cmd(config_dir: &path::Path, use_cl: bool) -> Result<()> {
|
||||||
Command::cargo_bin("xdbm")?
|
if use_cl {
|
||||||
.arg("-c")
|
Command::cargo_bin("xdbm")?
|
||||||
.arg(config_dir)
|
.arg("-c")
|
||||||
.args(["sync", "-vvvv"])
|
.arg(config_dir)
|
||||||
.assert()
|
.args(["sync", "-vvvv"])
|
||||||
.success();
|
.assert()
|
||||||
|
.success();
|
||||||
|
} else {
|
||||||
|
Command::cargo_bin("xdbm")?
|
||||||
|
.arg("-c")
|
||||||
|
.arg(config_dir)
|
||||||
|
.args(["sync", "-vvvv", "-u"])
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,6 +338,7 @@ mod integrated_test {
|
||||||
.arg(config_dir_2.path())
|
.arg(config_dir_2.path())
|
||||||
.arg("sync")
|
.arg("sync")
|
||||||
.arg("-vvvv")
|
.arg("-vvvv")
|
||||||
|
.arg("-u")
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
.stderr(predicate::str::contains("successfully pushed"));
|
.stderr(predicate::str::contains("successfully pushed"));
|
||||||
|
@ -391,8 +401,8 @@ mod integrated_test {
|
||||||
std::fs::read_to_string(config_dir_1.join("storages.yml"))?.contains("parent: gdrive1")
|
std::fs::read_to_string(config_dir_1.join("storages.yml"))?.contains("parent: gdrive1")
|
||||||
);
|
);
|
||||||
|
|
||||||
run_sync_cmd(&config_dir_1)?;
|
run_sync_cmd(&config_dir_1, false)?;
|
||||||
run_sync_cmd(&config_dir_2)?;
|
run_sync_cmd(&config_dir_2, false)?;
|
||||||
|
|
||||||
// bind
|
// bind
|
||||||
//
|
//
|
||||||
|
@ -606,8 +616,8 @@ mod integrated_test {
|
||||||
.and(predicate::str::contains("foodoc").not()),
|
.and(predicate::str::contains("foodoc").not()),
|
||||||
);
|
);
|
||||||
|
|
||||||
run_sync_cmd(&config_dir_2)?;
|
run_sync_cmd(&config_dir_2, true)?;
|
||||||
run_sync_cmd(&config_dir_1)?;
|
run_sync_cmd(&config_dir_1, true)?;
|
||||||
|
|
||||||
// bind
|
// bind
|
||||||
//
|
//
|
||||||
|
@ -722,8 +732,8 @@ mod integrated_test {
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
|
|
||||||
run_sync_cmd(&config_dir_1)?;
|
run_sync_cmd(&config_dir_1, false)?;
|
||||||
run_sync_cmd(&config_dir_2)?;
|
run_sync_cmd(&config_dir_2, false)?;
|
||||||
|
|
||||||
// backup add
|
// backup add
|
||||||
//
|
//
|
||||||
|
|
Loading…
Add table
Reference in a new issue