mirror of
https://github.com/rustic-rs/rustic.git
synced 2025-10-26 11:18:51 +00:00
feat: Add environment variables to the hooks (#1518)
closes #1516 --------- Co-authored-by: Romain de Laage <romain.delaage@rdelaage.ovh> Co-authored-by: Alexander Weiss <alex@weissfam.de>
This commit is contained in:
parent
33ed143f33
commit
a937ae6469
@ -1,5 +1,7 @@
|
||||
//! `backup` subcommand
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use std::path::PathBuf;
|
||||
use std::{collections::BTreeMap, env};
|
||||
|
||||
@ -18,6 +20,7 @@ use clap::ValueHint;
|
||||
use comfy_table::Cell;
|
||||
use conflate::Merge;
|
||||
use log::{debug, error, info, warn};
|
||||
use rustic_core::StringList;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::serde_as;
|
||||
|
||||
@ -197,7 +200,12 @@ impl BackupCmd {
|
||||
}
|
||||
.to_indexed_ids()?;
|
||||
|
||||
let hooks = config.backup.hooks.with_context("backup");
|
||||
let hooks = self.hooks(
|
||||
&config.backup.hooks,
|
||||
"backup",
|
||||
itertools::join(&config.backup.sources, ","),
|
||||
);
|
||||
|
||||
hooks.use_with(|| -> Result<_> {
|
||||
let config_snapshot_sources: Vec<_> = snapshot_opts
|
||||
.iter()
|
||||
@ -260,6 +268,35 @@ impl BackupCmd {
|
||||
})
|
||||
}
|
||||
|
||||
fn hooks(&self, hooks: &Hooks, action: &str, source: impl Display) -> Hooks {
|
||||
let mut hooks_variables =
|
||||
HashMap::from([("RUSTIC_ACTION".to_string(), action.to_string())]);
|
||||
|
||||
if let Some(label) = &self.snap_opts.label {
|
||||
let _ = hooks_variables.insert("RUSTIC_BACKUP_LABEL".to_string(), label.to_string());
|
||||
}
|
||||
|
||||
let source = source.to_string();
|
||||
if !source.is_empty() {
|
||||
let _ = hooks_variables.insert("RUSTIC_BACKUP_SOURCES".to_string(), source.clone());
|
||||
}
|
||||
|
||||
let mut tags = StringList::default();
|
||||
tags.add_all(self.snap_opts.tags.clone());
|
||||
let tags = tags.to_string();
|
||||
if !tags.is_empty() {
|
||||
let _ = hooks_variables.insert("RUSTIC_BACKUP_TAGS".to_string(), tags);
|
||||
}
|
||||
|
||||
let hooks = if action == "backup" {
|
||||
hooks.with_context("backup")
|
||||
} else {
|
||||
hooks.with_context(&format!("backup {source}"))
|
||||
};
|
||||
|
||||
hooks.with_env(&hooks_variables)
|
||||
}
|
||||
|
||||
fn backup_snapshot<P: ProgressBars, S: IndexedIds>(
|
||||
mut self,
|
||||
source: PathList,
|
||||
@ -284,12 +321,14 @@ impl BackupCmd {
|
||||
}
|
||||
}
|
||||
|
||||
// use the correct source-specific hooks
|
||||
let hooks = self.hooks.with_context(&format!("backup {source}"));
|
||||
// use hooks definition before merging "backup" section
|
||||
let hooks = self.hooks.clone();
|
||||
|
||||
// merge "backup" section from config file, if given
|
||||
self.merge(config.backup.clone());
|
||||
|
||||
let hooks = self.hooks(&hooks, "source-specific-backup", &source);
|
||||
|
||||
let backup_opts = BackupOptions::default()
|
||||
.stdin_filename(self.stdin_filename)
|
||||
.stdin_command(self.stdin_command)
|
||||
|
||||
@ -12,6 +12,8 @@
|
||||
//! - backup hooks
|
||||
//! - specific source-related hooks
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::Result;
|
||||
use conflate::Merge;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -40,6 +42,10 @@ pub struct Hooks {
|
||||
#[serde(skip)]
|
||||
#[merge(skip)]
|
||||
pub context: String,
|
||||
|
||||
#[serde(skip)]
|
||||
#[merge(skip)]
|
||||
pub env: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Hooks {
|
||||
@ -49,28 +55,46 @@ impl Hooks {
|
||||
hooks
|
||||
}
|
||||
|
||||
fn run_all(cmds: &[CommandInput], context: &str, what: &str) -> Result<()> {
|
||||
pub fn with_env(&self, env: &HashMap<String, String>) -> Self {
|
||||
let mut hooks = self.clone();
|
||||
hooks.env = HashMap::<String, String>::new();
|
||||
for (key, val) in env.iter() {
|
||||
_ = hooks.env.insert(key.clone(), val.clone());
|
||||
}
|
||||
hooks
|
||||
}
|
||||
|
||||
fn run_all(
|
||||
cmds: &[CommandInput],
|
||||
context: &str,
|
||||
what: &str,
|
||||
env: &HashMap<String, String>,
|
||||
) -> Result<()> {
|
||||
let mut env = env.clone();
|
||||
|
||||
let _ = env.insert("RUSTIC_HOOK_TYPE".to_string(), what.to_string());
|
||||
|
||||
for cmd in cmds {
|
||||
cmd.run(context, what, None::<(&str, &str)>)?;
|
||||
cmd.run(context, what, &env)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_before(&self) -> Result<()> {
|
||||
Self::run_all(&self.run_before, &self.context, "run-before")
|
||||
Self::run_all(&self.run_before, &self.context, "run-before", &self.env)
|
||||
}
|
||||
|
||||
pub fn run_after(&self) -> Result<()> {
|
||||
Self::run_all(&self.run_after, &self.context, "run-after")
|
||||
Self::run_all(&self.run_after, &self.context, "run-after", &self.env)
|
||||
}
|
||||
|
||||
pub fn run_failed(&self) -> Result<()> {
|
||||
Self::run_all(&self.run_failed, &self.context, "run-failed")
|
||||
Self::run_all(&self.run_failed, &self.context, "run-failed", &self.env)
|
||||
}
|
||||
|
||||
pub fn run_finally(&self) -> Result<()> {
|
||||
Self::run_all(&self.run_finally, &self.context, "run-finally")
|
||||
Self::run_all(&self.run_finally, &self.context, "run-finally", &self.env)
|
||||
}
|
||||
|
||||
/// Run the given closure using the specified hooks.
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
//! application's configuration file and/or command-line options
|
||||
//! for specifying it.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Deref;
|
||||
|
||||
@ -51,22 +52,34 @@ pub type RusticIndexedRepo<P> = Repository<P, IndexedStatus<FullIndex, OpenStatu
|
||||
pub type CliIndexedRepo = RusticIndexedRepo<ProgressOptions>;
|
||||
|
||||
impl AllRepositoryOptions {
|
||||
fn repository<P>(&self, po: P) -> Result<RusticRepo<P>> {
|
||||
pub fn repository<P>(&self, po: P) -> Result<RusticRepo<P>> {
|
||||
let backends = self.be.to_backends()?;
|
||||
let repo = Repository::new_with_progress(&self.repo, &backends, po)?;
|
||||
Ok(RusticRepo(repo))
|
||||
}
|
||||
|
||||
pub fn run<T>(&self, f: impl FnOnce(CliRepo) -> Result<T>) -> Result<T> {
|
||||
let hooks = self.hooks.with_context("repository");
|
||||
let po = RUSTIC_APP.config().global.progress_options;
|
||||
pub fn run_with_progress<P: Clone + ProgressBars, T>(
|
||||
&self,
|
||||
po: P,
|
||||
f: impl FnOnce(RusticRepo<P>) -> Result<T>,
|
||||
) -> Result<T> {
|
||||
let hooks = self
|
||||
.hooks
|
||||
.with_env(&HashMap::from([(
|
||||
"RUSTIC_ACTION".to_string(),
|
||||
"repository".to_string(),
|
||||
)]))
|
||||
.with_context("repository");
|
||||
hooks.use_with(|| f(self.repository(po)?))
|
||||
}
|
||||
|
||||
pub fn run_open<T>(&self, f: impl FnOnce(CliOpenRepo) -> Result<T>) -> Result<T> {
|
||||
let hooks = self.hooks.with_context("repository");
|
||||
pub fn run<T>(&self, f: impl FnOnce(CliRepo) -> Result<T>) -> Result<T> {
|
||||
let po = RUSTIC_APP.config().global.progress_options;
|
||||
hooks.use_with(|| f(self.repository(po)?.open()?))
|
||||
self.run_with_progress(po, f)
|
||||
}
|
||||
|
||||
pub fn run_open<T>(&self, f: impl FnOnce(CliOpenRepo) -> Result<T>) -> Result<T> {
|
||||
self.run(|repo| f(repo.open()?))
|
||||
}
|
||||
|
||||
pub fn run_open_or_init_with<T: Clone>(
|
||||
@ -75,13 +88,7 @@ impl AllRepositoryOptions {
|
||||
init: impl FnOnce(CliRepo) -> Result<CliOpenRepo>,
|
||||
f: impl FnOnce(CliOpenRepo) -> Result<T>,
|
||||
) -> Result<T> {
|
||||
let hooks = self.hooks.with_context("repository");
|
||||
let po = RUSTIC_APP.config().global.progress_options;
|
||||
hooks.use_with(|| {
|
||||
f(self
|
||||
.repository(po)?
|
||||
.open_or_init_repository_with(do_init, init)?)
|
||||
})
|
||||
self.run(|repo| f(repo.open_or_init_repository_with(do_init, init)?))
|
||||
}
|
||||
|
||||
pub fn run_indexed_with_progress<P: Clone + ProgressBars, T>(
|
||||
@ -89,13 +96,11 @@ impl AllRepositoryOptions {
|
||||
po: P,
|
||||
f: impl FnOnce(RusticIndexedRepo<P>) -> Result<T>,
|
||||
) -> Result<T> {
|
||||
let hooks = self.hooks.with_context("repository");
|
||||
hooks.use_with(|| f(self.repository(po)?.indexed()?))
|
||||
self.run_with_progress(po, |repo| f(repo.indexed()?))
|
||||
}
|
||||
|
||||
pub fn run_indexed<T>(&self, f: impl FnOnce(CliIndexedRepo) -> Result<T>) -> Result<T> {
|
||||
let po = RUSTIC_APP.config().global.progress_options;
|
||||
self.run_indexed_with_progress(po, f)
|
||||
self.run(|repo| f(repo.indexed()?))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ RusticConfig {
|
||||
run_failed: [],
|
||||
run_finally: [],
|
||||
context: "",
|
||||
env: {},
|
||||
},
|
||||
env: {},
|
||||
prometheus: None,
|
||||
@ -52,6 +53,7 @@ RusticConfig {
|
||||
run_failed: [],
|
||||
run_finally: [],
|
||||
context: "",
|
||||
env: {},
|
||||
},
|
||||
},
|
||||
snapshot_filter: SnapshotFilter {
|
||||
@ -142,6 +144,7 @@ RusticConfig {
|
||||
run_failed: [],
|
||||
run_finally: [],
|
||||
context: "",
|
||||
env: {},
|
||||
},
|
||||
snapshots: [],
|
||||
sources: [],
|
||||
|
||||
@ -19,6 +19,7 @@ RusticConfig {
|
||||
run_failed: [],
|
||||
run_finally: [],
|
||||
context: "",
|
||||
env: {},
|
||||
},
|
||||
env: {
|
||||
"KEY0": "VALUE0",
|
||||
@ -63,6 +64,7 @@ RusticConfig {
|
||||
run_failed: [],
|
||||
run_finally: [],
|
||||
context: "",
|
||||
env: {},
|
||||
},
|
||||
},
|
||||
snapshot_filter: SnapshotFilter {
|
||||
@ -153,6 +155,7 @@ RusticConfig {
|
||||
run_failed: [],
|
||||
run_finally: [],
|
||||
context: "",
|
||||
env: {},
|
||||
},
|
||||
snapshots: [],
|
||||
sources: [],
|
||||
|
||||
Loading…
Reference in New Issue
Block a user