auth_tarball_from_git/
git.rs1use crate::errors::*;
2use std::path::Path;
3use std::process::Stdio;
4use tokio::fs;
5use tokio::process::Command;
6
7pub async fn clone(folder: &Path, url: &str, tag: &str) -> Result<()> {
8 let cmd = Command::new("git")
9 .arg("clone")
10 .arg("-q")
11 .arg("--bare")
12 .arg("--depth=1")
13 .arg("--branch")
14 .arg(tag)
15 .arg("--")
16 .arg(url)
17 .arg(folder)
18 .spawn()
19 .context("Failed to run git clone")?;
20
21 let out = cmd.wait_with_output().await?;
22 if !out.status.success() {
23 bail!("Process (git clone) exited with error: {:?}", out.status);
24 }
25
26 Ok(())
27}
28
29pub struct Tag {
30 data: Vec<u8>,
31 sig_pos: Option<usize>,
32}
33
34impl Tag {
35 pub fn data(&self) -> &[u8] {
36 if let Some(pos) = self.sig_pos {
37 &self.data[..pos]
38 } else {
39 &self.data
40 }
41 }
42
43 pub fn sig(&self) -> Option<&[u8]> {
44 if let Some(pos) = self.sig_pos {
45 Some(&self.data[pos..])
46 } else {
47 None
48 }
49 }
50
51 pub async fn verify(&self, keyrings: &[&Path]) -> Result<()> {
52 let obj = self.data();
53 let sig = self
54 .sig()
55 .ok_or_else(|| anyhow!("Failed to find signature in tag"))?;
56
57 let tmp_dir = tempfile::Builder::new()
58 .prefix("auth-from-git-")
59 .tempdir()?;
60 let path = tmp_dir.path();
61 let obj_path = path.join("obj");
62 let sig_path = path.join("sig");
63
64 fs::write(&obj_path, obj).await?;
65 fs::write(&sig_path, sig).await?;
66
67 let mut cmd = Command::new("sqv");
68
69 for keyring in keyrings {
70 cmd.arg("--keyring");
71 cmd.arg(keyring);
72 }
73
74 let cmd = cmd
75 .arg("--")
76 .arg(sig_path)
77 .arg(obj_path)
78 .stdout(Stdio::null())
79 .spawn()
80 .context("Failed to run sqv")?;
81
82 let out = cmd.wait_with_output().await?;
83 if !out.status.success() {
84 bail!("Process (sqv) exited with error: {:?}", out.status);
85 }
86
87 Ok(())
88 }
89}
90
91pub async fn read_tag(folder: &Path, tag: &str) -> Result<Tag> {
92 let tag_bytes = cat_tag(folder, tag).await?;
93 let needle = b"-----BEGIN PGP SIGNATURE-----\n";
94 let pos = tag_bytes
95 .windows(needle.len())
96 .position(|window| window == needle);
97
98 Ok(Tag {
99 data: tag_bytes,
100 sig_pos: pos,
101 })
102}
103
104pub async fn verify_tag(folder: &Path, tag: &str, keyring: &[&Path]) -> Result<()> {
105 let tag = read_tag(folder, tag).await?;
106 tag.verify(keyring).await
107}
108
109pub async fn cat_tag(folder: &Path, tag: &str) -> Result<Vec<u8>> {
110 let cmd = Command::new("git")
111 .arg("cat-file")
112 .arg("--")
113 .arg("tag")
114 .arg(tag)
115 .stdout(Stdio::piped())
116 .current_dir(folder)
117 .spawn()
118 .context("Failed to run git cat-file")?;
119
120 let out = cmd.wait_with_output().await?;
121 if !out.status.success() {
122 bail!("Process (git cat-file) exited with error: {:?}", out.status);
123 }
124
125 Ok(out.stdout)
126}
127
128pub async fn archive(path: &Path, prefix: &str, tag: &str, format: &str) -> Result<Vec<u8>> {
129 let cmd = Command::new("git")
130 .args(&["archive", "--format", format, "--prefix", prefix, "--", tag])
131 .stdout(Stdio::piped())
132 .current_dir(path)
133 .spawn()
134 .context("Failed to run git archive")?;
135
136 let out = cmd.wait_with_output().await?;
137 if !out.status.success() {
138 bail!("Process (git archive) exited with error: {:?}", out.status);
139 }
140
141 Ok(out.stdout)
142}