1use crate::store::Store;
5use std::path::Path;
6use std::process::Command;
7
8fn git_sha(repo: &Path, rev: &str) -> Option<String> {
10 let out = Command::new("git")
11 .args(["rev-parse", rev])
12 .current_dir(repo)
13 .output()
14 .ok()?;
15 if !out.status.success() {
16 return None;
17 }
18 let sha = String::from_utf8_lossy(&out.stdout).trim().to_string();
19 crate::tick::is_40_lower_hex(&sha).then_some(sha)
20}
21
22pub fn resolve(repo: &Path, store: &Store, policy: &str, offline: bool) -> Option<String> {
25 if offline {
26 return store.read_origin_sha();
27 }
28 match policy {
29 "none" => None,
30 "local-head" => git_sha(repo, "HEAD"),
31 _ => {
32 let sha = git_sha(repo, "@{upstream}");
34 match &sha {
35 Some(s) => {
36 let _ = store.write_origin_sha(s);
37 }
38 None => eprintln!(
39 "warning: cannot resolve the live-origin staleness reference (no upstream?) — sha-staleness skipped"
40 ),
41 }
42 sha
43 }
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50 use crate::store::Store;
51
52 fn git_store() -> (std::path::PathBuf, Store, String) {
54 use std::sync::atomic::{AtomicU64, Ordering};
55 static N: AtomicU64 = AtomicU64::new(0);
56 let p = std::env::temp_dir().join(format!(
57 "ev-staleness-{}-{}",
58 std::process::id(),
59 N.fetch_add(1, Ordering::Relaxed)
60 ));
61 let _ = std::fs::remove_dir_all(&p);
62 std::fs::create_dir_all(&p).unwrap();
63 for args in [
64 ["init"].as_slice(),
65 ["config", "user.email", "t@e.st"].as_slice(),
66 ["config", "user.name", "Tester"].as_slice(),
67 ["commit", "--allow-empty", "-m", "init"].as_slice(),
68 ] {
69 Command::new("git")
70 .args(args)
71 .current_dir(&p)
72 .output()
73 .unwrap();
74 }
75 let head = String::from_utf8(
76 Command::new("git")
77 .args(["rev-parse", "HEAD"])
78 .current_dir(&p)
79 .output()
80 .unwrap()
81 .stdout,
82 )
83 .unwrap()
84 .trim()
85 .to_string();
86 let s = Store::at(&p);
87 s.init().unwrap();
88 (p, s, head)
89 }
90
91 #[test]
92 fn git_sha_should_return_the_head_when_run_in_a_git_repo() {
93 let (p, _s, head) = git_store();
95
96 let sha = git_sha(&p, "HEAD");
98
99 assert_eq!(sha.as_deref(), Some(head.as_str()));
101 }
102
103 #[test]
104 fn resolve_should_be_none_when_the_policy_is_none() {
105 let (p, s, _head) = git_store();
107
108 let sha = resolve(&p, &s, "none", false);
110
111 assert!(sha.is_none());
113 }
114
115 #[test]
116 fn resolve_should_be_the_local_head_when_the_policy_is_local_head() {
117 let (p, s, head) = git_store();
119
120 let sha = resolve(&p, &s, "local-head", false);
122
123 assert_eq!(sha.as_deref(), Some(head.as_str()));
125 }
126
127 #[test]
128 fn resolve_should_use_the_cached_origin_when_offline() {
129 let (p, s, _head) = git_store();
131 std::fs::write(
132 s.root.join("results").join("origin-sha"),
133 "d308afac1b2c3d4e5f60718293a4b5c6d7e8f901",
134 )
135 .unwrap();
136
137 let sha = resolve(&p, &s, "live-origin", true);
139
140 assert_eq!(
142 sha.as_deref(),
143 Some("d308afac1b2c3d4e5f60718293a4b5c6d7e8f901")
144 );
145 }
146}