summaryrefslogtreecommitdiffhomepage
path: root/mullvad-update/mullvad-release/src/artifacts.rs
blob: f70cd35ec7709cc69d22f31d4ed2781675ed8705 (plain)
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
//! Generate metadata for installer artifacts

use anyhow::Context;
use std::path::Path;
use tokio::{fs, io::BufReader};

use mullvad_update::{format, hash};

/// Generate `format::Installer` for a given `artifact`.
///
/// The presence of the files relative to `base_urls` is not verified.
/// See [crate::config::Config::base_urls] for the assumptions made.
pub async fn generate_installer_details(
    architecture: format::Architecture,
    version: &mullvad_version::Version,
    base_urls: &[String],
    artifact: &Path,
) -> anyhow::Result<format::Installer> {
    let file = fs::File::open(artifact)
        .await
        .with_context(|| format!("Failed to open file at {}", artifact.display()))?;
    let metadata = file
        .metadata()
        .await
        .context("Failed to retrieve file metadata")?;
    let file_size = metadata.len();
    let file = BufReader::new(file);

    println!("Generating checksum for {}", artifact.display());

    let checksum = hash::checksum(file)
        .await
        .context("Failed to compute checksum")?;

    // Construct URLs from base URLs
    let filename = artifact
        .file_name()
        .and_then(|f| f.to_str())
        .context("Unexpected filename")?;
    let urls = derive_urls(base_urls, version, filename);

    Ok(format::Installer {
        architecture,
        urls,
        size: file_size.try_into().context("Invalid file size")?,
        sha256: hex::encode(checksum),
    })
}

fn derive_urls(
    base_urls: &[String],
    version: &mullvad_version::Version,
    filename: &str,
) -> Vec<String> {
    base_urls
        .iter()
        .map(|base_url| {
            let url = base_url.strip_suffix("/").unwrap_or(base_url);
            format!("{url}/{version}/{filename}")
        })
        .collect()
}

#[cfg(test)]
mod test {
    use super::*;

    /// Test derivation of URLs from base URLs
    #[tokio::test]
    pub async fn test_urls() {
        let base_urls = vec![
            "https://fake1.fake/".to_string(),
            "https://fake2.fake".to_string(),
        ];

        assert_eq!(
            &derive_urls(&base_urls, &"2025.1".parse().unwrap(), "test.exe"),
            &[
                "https://fake1.fake/2025.1/test.exe",
                "https://fake2.fake/2025.1/test.exe"
            ],
        );
    }
}