mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-04-24 14:28:42 +00:00
feat: git::Repository
cancellation support (#1288)
feat: `git::Repository` support cancellation. To make sure users can cancel git operation via signal, e.g. when the git operation fail or users no longer want to install. Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
parent
ef99dd795f
commit
fbed317df5
7 changed files with 113 additions and 16 deletions
|
@ -17,6 +17,7 @@ binstalk-types = { version = "0.5.0", path = "../binstalk-types" }
|
|||
bytes = "1.4.0"
|
||||
bzip2 = "0.4.4"
|
||||
compact_str = "0.7.0"
|
||||
derive_destructure2 = "0.1"
|
||||
flate2 = { version = "1.0.26", default-features = false }
|
||||
futures-util = "0.3.28"
|
||||
generic-array = "0.14.7"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::{fmt, mem, num::NonZeroU32, path::Path, str::FromStr, sync::atomic::AtomicBool};
|
||||
|
||||
use compact_str::CompactString;
|
||||
use gix::{clone, create, open, remote, Url};
|
||||
use thiserror::Error as ThisError;
|
||||
use tracing::debug;
|
||||
|
@ -8,6 +7,9 @@ use tracing::debug;
|
|||
mod progress_tracing;
|
||||
use progress_tracing::TracingProgress;
|
||||
|
||||
mod cancellation_token;
|
||||
pub use cancellation_token::{GitCancelOnDrop, GitCancellationToken};
|
||||
|
||||
pub use gix::url::parse::Error as GitUrlParseError;
|
||||
|
||||
#[derive(Debug, ThisError)]
|
||||
|
@ -116,14 +118,21 @@ impl Repository {
|
|||
/// async context then you must wrap the call in [`tokio::task::spawn_blocking`].
|
||||
///
|
||||
/// WARNING: This function must be called after tokio runtime is initialized.
|
||||
pub fn shallow_clone_bare(url: GitUrl, path: &Path) -> Result<Self, GitError> {
|
||||
pub fn shallow_clone_bare(
|
||||
url: GitUrl,
|
||||
path: &Path,
|
||||
cancellation_token: Option<GitCancellationToken>,
|
||||
) -> Result<Self, GitError> {
|
||||
debug!("Shallow cloning {url} to {}", path.display());
|
||||
|
||||
Ok(Self(
|
||||
Self::prepare_fetch(url, path, create::Kind::Bare)?
|
||||
.fetch_only(
|
||||
&mut TracingProgress::new(CompactString::new("Cloning")),
|
||||
&AtomicBool::new(false),
|
||||
&mut TracingProgress::new("Cloning bare"),
|
||||
cancellation_token
|
||||
.as_ref()
|
||||
.map(GitCancellationToken::get_atomic)
|
||||
.unwrap_or(&AtomicBool::new(false)),
|
||||
)?
|
||||
.0
|
||||
.into(),
|
||||
|
@ -134,16 +143,26 @@ impl Repository {
|
|||
/// async context then you must wrap the call in [`tokio::task::spawn_blocking`].
|
||||
///
|
||||
/// WARNING: This function must be called after tokio runtime is initialized.
|
||||
pub fn shallow_clone(url: GitUrl, path: &Path) -> Result<Self, GitError> {
|
||||
pub fn shallow_clone(
|
||||
url: GitUrl,
|
||||
path: &Path,
|
||||
cancellation_token: Option<GitCancellationToken>,
|
||||
) -> Result<Self, GitError> {
|
||||
debug!("Shallow cloning {url} to {} with worktree", path.display());
|
||||
|
||||
let mut progress = TracingProgress::new(CompactString::new("Cloning"));
|
||||
let mut progress = TracingProgress::new("Cloning with worktree");
|
||||
|
||||
Ok(Self(
|
||||
Self::prepare_fetch(url, path, create::Kind::WithWorktree)?
|
||||
.fetch_then_checkout(&mut progress, &AtomicBool::new(false))?
|
||||
.0
|
||||
.main_worktree(&mut progress, &AtomicBool::new(false))?
|
||||
.main_worktree(
|
||||
&mut progress,
|
||||
cancellation_token
|
||||
.as_ref()
|
||||
.map(GitCancellationToken::get_atomic)
|
||||
.unwrap_or(&AtomicBool::new(false)),
|
||||
)?
|
||||
.0
|
||||
.into(),
|
||||
))
|
||||
|
|
44
crates/binstalk-downloader/src/git/cancellation_token.rs
Normal file
44
crates/binstalk-downloader/src/git/cancellation_token.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering::Relaxed},
|
||||
Arc,
|
||||
};
|
||||
|
||||
use derive_destructure2::destructure;
|
||||
|
||||
/// Token that can be used to cancel git operation.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct GitCancellationToken(Arc<AtomicBool>);
|
||||
|
||||
impl GitCancellationToken {
|
||||
/// Create a guard that cancel the git operation on drop.
|
||||
#[must_use = "You must assign the guard to a variable, \
|
||||
otherwise it is equivalent to `GitCancellationToken::cancel()`"]
|
||||
pub fn cancel_on_drop(self) -> GitCancelOnDrop {
|
||||
GitCancelOnDrop(self)
|
||||
}
|
||||
|
||||
/// Cancel the git operation.
|
||||
pub fn cancel(&self) {
|
||||
self.0.store(true, Relaxed)
|
||||
}
|
||||
|
||||
pub(super) fn get_atomic(&self) -> &AtomicBool {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Guard used to cancel git operation on drop
|
||||
#[derive(Debug, destructure)]
|
||||
pub struct GitCancelOnDrop(GitCancellationToken);
|
||||
|
||||
impl Drop for GitCancelOnDrop {
|
||||
fn drop(&mut self) {
|
||||
self.0.cancel()
|
||||
}
|
||||
}
|
||||
impl GitCancelOnDrop {
|
||||
/// Disarm the guard, return the token.
|
||||
pub fn disarm(self) -> GitCancellationToken {
|
||||
self.destructure().0
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@ const SEP: &str = "::";
|
|||
|
||||
impl TracingProgress {
|
||||
/// Create a new instanCompactce from `name`.
|
||||
pub fn new(name: CompactString) -> Self {
|
||||
pub fn new(name: &str) -> Self {
|
||||
let trigger = Arc::new(AtomicBool::new(true));
|
||||
tokio::spawn({
|
||||
let mut interval = time::interval(Duration::from_secs_f32(EMIT_LOG_EVERY_S));
|
||||
|
@ -43,7 +43,7 @@ impl TracingProgress {
|
|||
}
|
||||
});
|
||||
Self {
|
||||
name,
|
||||
name: CompactString::new(name),
|
||||
id: UNKNOWN,
|
||||
max: None,
|
||||
step: 0,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue