1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//! `torii archive` — export a tree or commit as a tarball.
//!
//! Thin wrapper over `git archive`. Rationale: tar+zip+tar.gz are already
//! implemented correctly in git's contrib code, and adding the `tar` +
//! `flate2` crates as direct dependencies would balloon build time for
//! marginal gain. `git archive` is in every standard git install.
//!
//! Examples (resolved at the CLI layer):
//! torii archive HEAD -o release.tar.gz
//! torii archive v0.6.9 --prefix=gitorii-0.6.9/ -o gitorii-0.6.9.tar.gz
//! torii archive HEAD --format=zip -o release.zip
use crate::error::{Result, ToriiError};
use std::path::Path;
use std::process::Command;
#[derive(Debug, Default)]
pub struct Opts {
/// Output file. When `None`, archive is written to stdout (matches
/// `git archive` default; only useful in pipelines).
pub output: Option<String>,
/// Force a specific format. Without this, git infers from `--output`'s
/// extension (`.tar`, `.tar.gz`, `.zip`). Valid values: `tar`, `zip`,
/// `tar.gz`, `tgz`.
pub format: Option<String>,
/// Prepend `prefix/` to every entry. Common for release tarballs so
/// they unpack into a versioned subdirectory.
pub prefix: Option<String>,
}
pub fn archive(repo_path: &Path, revision: &str, opts: &Opts) -> Result<()> {
let mut args = vec!["archive".to_string()];
if let Some(fmt) = &opts.format {
args.push(format!("--format={fmt}"));
}
if let Some(prefix) = &opts.prefix {
args.push(format!("--prefix={prefix}"));
}
if let Some(out) = &opts.output {
args.push(format!("--output={out}"));
}
args.push(revision.to_string());
println!("📦 archive rev={revision}");
let status = Command::new("git")
.args(&args)
.current_dir(repo_path)
.status()
.map_err(|e| ToriiError::InvalidConfig(format!("invoke git archive: {e}")))?;
if !status.success() {
return Err(ToriiError::InvalidConfig(format!(
"git archive exited with {status}"
)));
}
if let Some(out) = &opts.output {
println!("✅ Written: {out}");
}
Ok(())
}