Skip to main content

repro_env/
lockfile.rs

1use crate::errors::*;
2use serde::{Deserialize, Serialize};
3use std::path::Path;
4use tokio::fs;
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
7pub struct Lockfile {
8    pub container: ContainerLock,
9    #[serde(default, rename = "package", skip_serializing_if = "Vec::is_empty")]
10    pub packages: Vec<PackageLock>,
11}
12
13impl Lockfile {
14    pub fn deserialize(buf: &str) -> Result<Self> {
15        let lockfile = toml::from_str(buf)?;
16        Ok(lockfile)
17    }
18
19    pub fn serialize(&self) -> Result<String> {
20        let toml = toml::to_string_pretty(self)?;
21        Ok(toml)
22    }
23
24    pub async fn read_from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
25        let path = path.as_ref();
26        let buf = fs::read_to_string(&path)
27            .await
28            .with_context(|| anyhow!("Failed to read dependency lockfile: {path:?}"))?;
29        let lockfile = Self::deserialize(&buf)?;
30        trace!("Loaded dependency lockfile from file: {lockfile:?}");
31        Ok(lockfile)
32    }
33}
34
35#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
36pub struct ContainerLock {
37    pub image: String,
38}
39
40#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
41pub struct PackageLock {
42    pub name: String,
43    pub version: String,
44    pub system: String,
45    pub url: String,
46    #[serde(default, skip_serializing_if = "Vec::is_empty")]
47    pub provides: Vec<String>,
48    pub sha256: String,
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub signature: Option<String>,
51    /// If true, this package is already present in the container and does not
52    /// need to be installed. It's only in the lockfile to make the
53    /// repro-env.lock diff easier to read and help git's delta-compression.
54    #[serde(default, skip_serializing_if = "is_false")]
55    pub installed: bool,
56}
57
58fn is_false(value: &bool) -> bool {
59    !value
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    pub fn test_serialize_archlinux() -> Result<()> {
68        let lockfile = Lockfile {
69            container: ContainerLock {
70                image:
71                    "docker.io/library/archlinux@sha256:6568d3f1f278827a4a7d8537f80c2ae36982829a0c6bccff4cec081774025472"
72                        .to_string(),
73            },
74            packages: vec![
75                PackageLock {
76                    name: "archlinux-keyring".to_string(),
77                    version: "20230704-1".to_string(),
78                    system: "archlinux".to_string(),
79                    url: "https://archive.archlinux.org/packages/a/archlinux-keyring/archlinux-keyring-20230704-1-any.pkg.tar.zst".to_string(),
80                    provides: vec![],
81                    sha256: "6a3d2acaa396c4bd72fe3f61a3256d881e3fc2cf326113cf331f168e36dd9a3c".to_string(),
82                    signature: Some(
83"iHUEABYIAB0WIQQEKYl95fO9rFN6MGltQr3RFuAGjwUCZKPPXgAKCRBtQr3RFuAGj9oXAP94RQ1sKD53/RxVYlVEEOjKHvOmrWvDkt1veMYygnlnIgD+MLg/TT6d71kE8F08+JH+EcnG7wQow5Xr/qBo1VPLdgQ=".to_string()),
84                    installed: false,
85                },
86                PackageLock {
87                    name: "binutils".to_string(),
88                    version: "2.40-6".to_string(),
89                    system: "archlinux".to_string(),
90                    url: "https://archive.archlinux.org/packages/b/binutils/binutils-2.40-6-x86_64.pkg.tar.zst".to_string(),
91                    provides: vec![],
92                    sha256: "b65fd16001578e10b602e577a8031cbfffc1164caf47ed9ba00c60d804519430".to_string(),
93                    signature: Some(
94"iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZG6Rg18UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaCge2AQD/LGBeHRaeO8xh4E/bAYfqd1O/OFqk2DrQBJ73cdKl2gD9EC8p4U/cXQK8V774m6LSS50usH5pxcQWEq/H0SF+FgM=".to_string()),
95                    installed: false,
96                }
97            ],
98        };
99
100        let toml = lockfile.serialize()?;
101
102        assert_eq!(
103            toml,
104            r#"[container]
105image = "docker.io/library/archlinux@sha256:6568d3f1f278827a4a7d8537f80c2ae36982829a0c6bccff4cec081774025472"
106
107[[package]]
108name = "archlinux-keyring"
109version = "20230704-1"
110system = "archlinux"
111url = "https://archive.archlinux.org/packages/a/archlinux-keyring/archlinux-keyring-20230704-1-any.pkg.tar.zst"
112sha256 = "6a3d2acaa396c4bd72fe3f61a3256d881e3fc2cf326113cf331f168e36dd9a3c"
113signature = "iHUEABYIAB0WIQQEKYl95fO9rFN6MGltQr3RFuAGjwUCZKPPXgAKCRBtQr3RFuAGj9oXAP94RQ1sKD53/RxVYlVEEOjKHvOmrWvDkt1veMYygnlnIgD+MLg/TT6d71kE8F08+JH+EcnG7wQow5Xr/qBo1VPLdgQ="
114
115[[package]]
116name = "binutils"
117version = "2.40-6"
118system = "archlinux"
119url = "https://archive.archlinux.org/packages/b/binutils/binutils-2.40-6-x86_64.pkg.tar.zst"
120sha256 = "b65fd16001578e10b602e577a8031cbfffc1164caf47ed9ba00c60d804519430"
121signature = "iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZG6Rg18UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaCge2AQD/LGBeHRaeO8xh4E/bAYfqd1O/OFqk2DrQBJ73cdKl2gD9EC8p4U/cXQK8V774m6LSS50usH5pxcQWEq/H0SF+FgM="
122"#
123        );
124
125        let deserialized = Lockfile::deserialize(&toml)?;
126        assert_eq!(deserialized, lockfile);
127
128        Ok(())
129    }
130
131    #[test]
132    pub fn test_serialize_debian() -> Result<()> {
133        let lockfile = Lockfile {
134            container: ContainerLock {
135                image:
136                    "debian@sha256:3d868b5eb908155f3784317b3dda2941df87bbbbaa4608f84881de66d9bb297b"
137                        .to_string(),
138            },
139            packages: vec![
140                PackageLock {
141                    name: "binutils".to_string(),
142                    version: "2.40-2".to_string(),
143                    system: "debian".to_string(),
144                    url: "https://snapshot.debian.org/archive/debian/20230115T211934Z/pool/main/b/binutils/binutils_2.40-2_amd64.deb".to_string(),
145                    provides: vec![],
146                    sha256: "83c3e20b53e1fbd84d764c3ba27d26a0376e361ae5d7fb37120196934dd87424".to_string(),
147                    signature: None,
148                    installed: false,
149                },
150                PackageLock {
151                    name: "binutils-common".to_string(),
152                    version: "2.40-2".to_string(),
153                    system: "debian".to_string(),
154                    url: "https://snapshot.debian.org/archive/debian/20230115T211934Z/pool/main/b/binutils/binutils-common_2.40-2_amd64.deb".to_string(),
155                    provides: vec![],
156                    sha256: "ab314134f43a0891a48f69a9bc33d825da748fa5e0ba2bebb7a5c491b026f1a0".to_string(),
157                    signature: None,
158                    installed: false,
159                }
160            ],
161        };
162
163        let toml = lockfile.serialize()?;
164
165        assert_eq!(
166            toml,
167            r#"[container]
168image = "debian@sha256:3d868b5eb908155f3784317b3dda2941df87bbbbaa4608f84881de66d9bb297b"
169
170[[package]]
171name = "binutils"
172version = "2.40-2"
173system = "debian"
174url = "https://snapshot.debian.org/archive/debian/20230115T211934Z/pool/main/b/binutils/binutils_2.40-2_amd64.deb"
175sha256 = "83c3e20b53e1fbd84d764c3ba27d26a0376e361ae5d7fb37120196934dd87424"
176
177[[package]]
178name = "binutils-common"
179version = "2.40-2"
180system = "debian"
181url = "https://snapshot.debian.org/archive/debian/20230115T211934Z/pool/main/b/binutils/binutils-common_2.40-2_amd64.deb"
182sha256 = "ab314134f43a0891a48f69a9bc33d825da748fa5e0ba2bebb7a5c491b026f1a0"
183"#
184        );
185
186        let deserialized = Lockfile::deserialize(&toml)?;
187        assert_eq!(deserialized, lockfile);
188
189        Ok(())
190    }
191}