add_determinism/
setup.rs

1/* SPDX-License-Identifier: GPL-3.0-or-later */
2
3use anyhow::{anyhow, bail, Result};
4use chrono::{TimeZone, Utc};
5use log::{debug, log, warn, Level};
6use std::env;
7use std::path::{Path, PathBuf};
8use std::time;
9
10pub fn source_date_epoch() -> Result<Option<i64>> {
11    let mut source_date_epoch = match env::var("SOURCE_DATE_EPOCH") {
12        Ok(val) => Some(val.parse::<i64>()?),
13        Err(_) => None,
14    };
15
16    if let Some(v) = source_date_epoch {
17        let now = time::SystemTime::now();
18        let now_sec = now.duration_since(time::UNIX_EPOCH).unwrap().as_secs();
19
20        let neg = v < 0;
21        let pos = v > 0 && v as u64 > now_sec;
22
23        log!(if neg || pos { Level::Warn } else { Level::Debug },
24             "SOURCE_DATE_EPOCH timestamp: {v} ({})",
25             Utc.timestamp_opt(v, 0).unwrap());
26        if neg {
27            warn!("SOURCE_DATE_EPOCH timestamp is negative, ignoring: {v}");
28            source_date_epoch = None;
29        } else if pos {
30            warn!("SOURCE_DATE_EPOCH timestamp is in the future: {v} > {now_sec}");
31        }
32    } else {
33        debug!("SOURCE_DATE_EPOCH timestamp: {}", "(unset)");
34    }
35
36    Ok(source_date_epoch)
37}
38
39pub fn brp_check(
40    build_root: Option<String>,
41    inputs: &Vec<PathBuf>,
42) -> Result<PathBuf> {
43
44    let build_root = build_root.map_or_else(
45        || env::var("RPM_BUILD_ROOT")
46            .map_err(|e| anyhow!("$RPM_BUILD_ROOT is not set correctly: {e}")),
47        Ok,
48    )?;
49
50    if build_root.is_empty() {
51        bail!("Empty $RPM_BUILD_ROOT is not allowed");
52    }
53
54    // Canonicalize the path, removing duplicate or trailing slashes
55    // and intermediate dot components, but not double dots.
56    let build_root = PathBuf::from_iter(Path::new(&build_root).iter());
57
58    if build_root == Path::new("/") {
59        bail!("RPM_BUILD_ROOT={build_root:?} is not allowed");
60    }
61
62    for arg in inputs {
63        if !arg.starts_with(&build_root) {
64            bail!("Path {arg:?} does not start with RPM_BUILD_ROOT={build_root:?}");
65        }
66    }
67
68    Ok(build_root)
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn test_brp_check() {
77        let inputs = vec![
78            "/var/tmp/foo/bar".into(),
79            "/var/tmp/foo/./bar".into(),
80            // Sic, this is allowed.
81            "/var/tmp/foo/./bar/../asdf".into(),
82            "/var/tmp/foo/./bar/../../../asdf".into(),
83        ];
84
85        assert!(brp_check(Some("".to_string()), &inputs).is_err());
86        assert!(brp_check(Some("///.///".to_string()), &inputs).is_err());
87        assert!(brp_check(Some("/var/tmp/foo2".to_string()), &inputs).is_err());
88        assert!(brp_check(Some("/var/tmp/foo///./".to_string()), &inputs).is_ok());
89    }
90}