summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMica White <botahamec@outlook.com>2026-04-03 19:45:17 -0400
committerMica White <botahamec@outlook.com>2026-04-03 19:45:17 -0400
commit03d6d4846ffcd29e589fdecaddbc749217761bfe (patch)
tree9f9a829cbb883c8b3f960c748c99a4d3152fbad8
parent45b2f8c6c5ff28d9f0b952f213c5372db2f937b0 (diff)
Queue autosaves
-rw-r--r--Cargo.lock80
-rw-r--r--Cargo.toml1
-rw-r--r--src/bin/git-autosave-daemon.rs101
3 files changed, 174 insertions, 8 deletions
diff --git a/Cargo.lock b/Cargo.lock
index fee88d8..ecdfdfa 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -443,6 +443,7 @@ dependencies = [
"log",
"notify",
"notify-debouncer-full",
+ "ping",
"serde",
"thiserror",
"uuid",
@@ -1017,6 +1018,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
+name = "ping"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "044b1fa4f259f4df9ad5078e587b208f5d288a25407575fcddb9face30c7c692"
+dependencies = [
+ "rand",
+ "socket2",
+ "thiserror",
+]
+
+[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1047,6 +1059,15 @@ dependencies = [
]
[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1087,6 +1108,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
+name = "rand"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
+dependencies = [
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
+dependencies = [
+ "getrandom 0.3.4",
+]
+
+[[package]]
name = "redox_syscall"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1280,6 +1330,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
+name = "socket2"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
name = "stable_deref_trait"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1949,6 +2009,26 @@ dependencies = [
]
[[package]]
+name = "zerocopy"
+version = "0.8.48"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.48"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "zerofrom"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 4bf7bdd..95a7808 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,3 +20,4 @@ colog = "1"
hostname = "0.4"
inquire = "0.9"
auth-git2 = "0.5"
+ping = "0.7"
diff --git a/src/bin/git-autosave-daemon.rs b/src/bin/git-autosave-daemon.rs
index 80edb34..d8c23fc 100644
--- a/src/bin/git-autosave-daemon.rs
+++ b/src/bin/git-autosave-daemon.rs
@@ -1,4 +1,8 @@
-use std::{collections::HashSet, time::Duration};
+use std::collections::HashSet;
+use std::path::PathBuf;
+use std::sync::mpsc::Receiver;
+use std::sync::mpsc::Sender;
+use std::time::Duration;
use auth_git2::GitAuthenticator;
use git_autosave::{Config, authenticate::Inquirer, commit_autosave, push_autosaves};
@@ -12,7 +16,67 @@ struct ConfigWatcher<Cache: FileIdCache + 'static> {
repo_watcher: &'static mut Debouncer<INotifyWatcher, Cache>,
}
-struct Watcher(&'static Mutex<Config>);
+struct RepoWatcher {
+ config: &'static Mutex<Config>,
+ push_queue: Sender<PathBuf>,
+}
+
+fn push_queue(
+ consumer: Receiver<PathBuf>,
+ config: &'static Mutex<Config>,
+ mut key: ThreadKey,
+) -> ! {
+ let mut queue = HashSet::new();
+
+ loop {
+ while ping::new("8.8.8.8".parse().unwrap())
+ .socket_type(ping::SocketType::DGRAM)
+ .timeout(Duration::from_secs(60))
+ .ttl(128)
+ .send()
+ .is_err()
+ {
+ let mut added_item = false;
+ while let Ok(workdir) = consumer.try_recv() {
+ added_item = true;
+ queue.insert(workdir);
+ }
+ if added_item {
+ log::warn!(
+ "There is no Internet connection, so pushes to the repository have been queued"
+ );
+ }
+ std::thread::yield_now();
+ }
+
+ while let Ok(workdir) = consumer.try_recv() {
+ queue.insert(workdir);
+ }
+
+ config.scoped_lock(&mut key, |config| {
+ for workdir in &queue {
+ log::info!("Pushing {}...", workdir.to_string_lossy());
+ let Ok(repository) = Repository::open(workdir) else {
+ log::error!("Failed to open repository: {}", workdir.to_string_lossy());
+ continue;
+ };
+ let Ok(gitconfig) = repository.config() else {
+ log::error!("Failed to load gitconfig for repository: {:?}", workdir);
+ return;
+ };
+ let auth = GitAuthenticator::new().set_prompter(Inquirer(&*config));
+ let mut callbacks = RemoteCallbacks::new();
+ callbacks.credentials(auth.credentials(&gitconfig));
+ if let Err(e) = push_autosaves(&repository, callbacks) {
+ log::error!("Failed to push autosaves: {e}");
+ }
+
+ log::info!("Successfully pushed {}", workdir.to_string_lossy());
+ }
+ });
+ queue.clear();
+ }
+}
impl<Cache: FileIdCache + Send + 'static> DebounceEventHandler for ConfigWatcher<Cache> {
fn handle_event(&mut self, events: DebounceEventResult) {
@@ -68,7 +132,7 @@ impl<Cache: FileIdCache + Send + 'static> DebounceEventHandler for ConfigWatcher
}
}
-impl DebounceEventHandler for Watcher {
+impl DebounceEventHandler for RepoWatcher {
fn handle_event(&mut self, events: DebounceEventResult) {
let Some(mut key) = ThreadKey::get() else {
log::error!("Failed to acquire thread key when autosaving repository. This is a bug!");
@@ -134,7 +198,7 @@ impl DebounceEventHandler for Watcher {
workdirs_to_autosave.insert(workdir.to_path_buf());
repositories_to_autosave.push(repository);
}
- self.0.scoped_lock(&mut key, |config| {
+ self.config.scoped_lock(&mut key, |config| {
for repository in repositories_to_autosave {
let workdir = repository
.workdir()
@@ -152,8 +216,11 @@ impl DebounceEventHandler for Watcher {
if let Err(e) = commit_autosave(&repository) {
log::error!("Failed to commit autosave: {e}");
}
- if let Err(e) = push_autosaves(&repository, callbacks) {
- log::error!("Failed to push autosaves: {e}");
+ if let Err(e) = self
+ .push_queue
+ .send(repository.workdir().unwrap().to_path_buf())
+ {
+ log::error!("Failed to add repository to push queue: {e}");
}
log::info!("Successfully autosaved {:?}", workdir);
@@ -163,18 +230,36 @@ impl DebounceEventHandler for Watcher {
}
fn main() -> Result<(), anyhow::Error> {
- let key = ThreadKey::get().expect("Could not get ThreadKey on startup. This is a bug!");
+ let mut key = ThreadKey::get().expect("Could not get ThreadKey on startup. This is a bug!");
colog::init();
log::info!("Loading autosave config...");
let config: &'static Mutex<Config> = Box::leak(Box::new(Mutex::new(Config::load()?)));
log::info!("Loaded autosave config");
+ log::info!("Starting push queue...");
+ let (sender, receiver) = std::sync::mpsc::channel();
+ std::thread::spawn(move || {
+ let key = ThreadKey::get().unwrap();
+ push_queue(receiver, config, key);
+ });
+ config.scoped_lock(&mut key, |config| {
+ for repository in config.repositories() {
+ if let Err(e) = sender.send(repository.clone()) {
+ log::error!("Failed to queue {}: {e}", repository.to_string_lossy());
+ }
+ }
+ });
+ log::info!("Started push queue");
+
log::info!("Starting repository watcher...");
let repo_watcher = Box::leak(Box::new(notify_debouncer_full::new_debouncer(
Duration::from_secs(5),
None,
- Watcher(config),
+ RepoWatcher {
+ config,
+ push_queue: sender,
+ },
)?));
config.scoped_lock(key, |config| {
log::info!("Adding repositories to watch...");