nimbus_cli/updater/
taskcluster.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5use serde::de::DeserializeOwned;
6use std::time::Duration;
7use update_informer::{
8    http_client::{GenericHttpClient, HeaderMap, HttpClient},
9    Check, Package, Registry, Result,
10};
11
12#[derive(serde::Deserialize)]
13struct Response {
14    version: String,
15}
16
17struct TaskClusterRegistry;
18
19impl Registry for TaskClusterRegistry {
20    const NAME: &'static str = "taskcluster";
21
22    fn get_latest_version<T: HttpClient>(
23        http_client: GenericHttpClient<T>,
24        pkg: &Package,
25    ) -> Result<Option<String>> {
26        let name = pkg.to_string();
27        let url = format!("https://firefox-ci-tc.services.mozilla.com/api/index/v1/task/project.application-services.v2.{name}.latest/artifacts/public%2Fbuild%2F{name}.json");
28        let resp = http_client.get::<Response>(&url)?;
29        Ok(Some(resp.version))
30    }
31}
32
33#[allow(dead_code)]
34pub struct ReqwestGunzippingHttpClient;
35
36impl HttpClient for ReqwestGunzippingHttpClient {
37    fn get<T: DeserializeOwned>(url: &str, timeout: Duration, headers: HeaderMap) -> Result<T> {
38        let mut req = reqwest::blocking::Client::builder()
39            .timeout(timeout)
40            // We couldn't use the out-the-box HttpClient
41            // because task-cluster uses gzip.
42            .gzip(true)
43            .build()?
44            .get(url);
45
46        for (key, value) in headers {
47            req = req.header(key, value);
48        }
49
50        let json = req.send()?.json()?;
51
52        Ok(json)
53    }
54}
55
56/// Check the specifically crafted JSON file for this package to see if there has been a change in version.
57/// This is done every hour.
58pub(crate) fn check_taskcluster_for_update<F>(message: F)
59where
60    F: Fn(&str, &str),
61{
62    let name = env!("CARGO_PKG_NAME");
63    let version = env!("CARGO_PKG_VERSION");
64    let interval = Duration::from_secs(60 * 60);
65
66    #[cfg(not(test))]
67    let informer = update_informer::new(TaskClusterRegistry, name, version)
68        .http_client(ReqwestGunzippingHttpClient)
69        .interval(interval);
70
71    #[cfg(test)]
72    let informer =
73        update_informer::fake(TaskClusterRegistry, name, version, "1.0.0").interval(interval);
74
75    if let Ok(Some(new_version)) = informer.check_version() {
76        message(&format!("v{version}"), &new_version.to_string());
77    }
78}