blob: 6deee5bcf7f2bf2ab5306e1ebd104472295877a8 [file] [log] [blame] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::{fs::File, path::Path};
use alchemist::analyze::source::PackageLocalSource;
use anyhow::Result;
use itertools::Itertools;
use serde::Serialize;
use tracing::instrument;
use super::common::{DistFileEntry, Package};
// Each entry here corresponds to a repository rule, and the fields in the
// struct must correspond to the parameters to that repository rule.
#[derive(Serialize, Debug)]
enum Repository {
CipdFile {
name: String,
downloaded_file_path: String,
url: String,
},
GsFile {
name: String,
downloaded_file_path: String,
url: String,
},
HttpFile {
name: String,
downloaded_file_path: String,
integrity: String,
urls: Vec<String>,
},
RepoRepository {
name: String,
project: String,
tree: String,
},
CrosChromeRepository {
name: String,
tag: String,
gclient: String,
},
}
pub fn generate_deps_file(packages: &[Package], out: &Path) -> Result<()> {
let repos = generate_deps(packages)?;
let mut file = File::create(out)?;
serde_json::to_writer(&mut file, &repos)?;
Ok(())
}
#[instrument(skip_all)]
fn generate_deps(packages: &[Package]) -> Result<Vec<Repository>> {
let joined_dists: Vec<DistFileEntry> = packages
.iter()
.flat_map(|package| {
package
.sources
.dist_sources
.iter()
.map(DistFileEntry::try_new)
})
.collect::<Result<_>>()?;
let unique_dists = joined_dists
.into_iter()
.sorted_by(|a, b| a.filename.cmp(&b.filename))
.dedup_by(|a, b| a.filename == b.filename)
.map(|dist| {
let url = &dist.urls[0];
if url.starts_with("cipd://") {
Repository::CipdFile {
name: dist.name,
downloaded_file_path: dist.filename,
url: url.to_string(),
}
} else if url.starts_with("gs://") {
Repository::GsFile {
name: dist.name,
downloaded_file_path: dist.filename,
url: url.to_string(),
}
} else {
Repository::HttpFile {
name: dist.name,
downloaded_file_path: dist.filename,
integrity: dist.integrity,
urls: dist.urls.clone(),
}
}
});
let repos = packages
.iter()
.flat_map(|package| &package.sources.repo_sources)
.unique_by(|source| &source.name)
.sorted_by(|a, b| a.name.cmp(&b.name))
.map(|repo| Repository::RepoRepository {
name: repo.name.clone(),
project: repo.project.clone(),
tree: repo.tree_hash.clone(),
});
let chrome = packages
.iter()
.flat_map(|package| &package.sources.local_sources)
.filter_map(|origin| match origin {
PackageLocalSource::Chrome(version) => Some(version),
_ => None,
})
.unique()
.sorted()
.map(|version| Repository::CrosChromeRepository {
name: format!("chrome-{}", version),
tag: version.clone(),
gclient: "@depot_tools//:gclient.wrapper.sh".to_string(),
});
Ok(unique_dists.chain(repos).chain(chrome).collect())
}
#[cfg(test)]
mod tests {
use std::collections::{HashMap, HashSet};
use alchemist::{
analyze::{
dependency::PackageDependencies,
source::{PackageDistSource, PackageSources},
},
bash::vars::BashVars,
data::Slot,
ebuild::PackageDetails,
};
use pretty_assertions::assert_eq;
use url::Url;
use version::Version;
use super::*;
#[test]
fn generate_deps_succeeds() -> Result<()> {
let hashes = HashMap::from([("SHA256".to_string(), "012346".to_string())]);
let cipd_sources = PackageSources {
local_sources: vec![],
repo_sources: vec![],
dist_sources: vec![PackageDistSource {
urls: vec![Url::parse("cipd://skia/tools/goldctl/linux-amd64:0ov3TU").unwrap()],
filename: "goldctl-2021.03.31-amd64.zip".to_owned(),
size: 100,
hashes: hashes.clone(),
}],
};
let gs_sources = PackageSources {
local_sources: vec![],
repo_sources: vec![],
dist_sources: vec![PackageDistSource {
urls: vec![Url::parse("gs://secret-bucket/secret-file.tar.gz").unwrap()],
filename: "secret-file.tar.gz".to_owned(),
size: 100,
hashes: hashes.clone(),
}],
};
let https_sources = PackageSources {
local_sources: vec![],
repo_sources: vec![],
dist_sources: vec![
PackageDistSource {
urls: vec![
Url::parse("https://commondatastorage.googleapis.com/chromeos-localmirror/distfiles/google-api-core-1.19.0.tar.gz").unwrap()
],
filename: "google-api-core-1.19.0.tar.gz".to_owned(),
size: 100,
hashes: hashes.clone(),
},
],
};
let dependencies = PackageDependencies {
build_deps: vec![],
runtime_deps: vec![],
post_deps: vec![],
build_host_deps: vec![],
install_host_deps: vec![],
};
let details_prototype = PackageDetails {
repo_name: "baz".to_owned(),
package_name: "prototype".to_owned(),
version: Version::try_new("1.0").unwrap(),
vars: BashVars::new(HashMap::new()),
slot: Slot::new("0"),
use_map: HashMap::new(),
accepted: true,
stable: true,
masked: false,
ebuild_path: "/somewhere/sys-apps/prototype-1.0.ebuild".into(),
inherited: HashSet::new(),
inherit_paths: vec![],
direct_build_target: None,
};
let mut details1 = details_prototype.clone();
details1.package_name = "sys-apps/p1".to_owned();
details1.ebuild_path = "/somewhere/sys-apps/p1-1.0.ebuild".into();
let mut details2 = details_prototype.clone();
details2.package_name = "sys-apps/p2".to_owned();
details2.ebuild_path = "/somewhere/sys-apps/p2-1.0.ebuild".into();
let mut details3 = details_prototype.clone();
details3.package_name = "sys-apps/p3".to_owned();
details3.ebuild_path = "/somewhere/sys-apps/p3-1.0.ebuild".into();
let packages = vec![
Package {
details: details1.into(),
dependencies: dependencies.clone(),
sources: cipd_sources,
install_set: vec![],
build_host_deps: vec![],
},
Package {
details: details2.into(),
dependencies: dependencies.clone(),
sources: gs_sources,
install_set: vec![],
build_host_deps: vec![],
},
Package {
details: details3.into(),
dependencies: dependencies.clone(),
sources: https_sources,
install_set: vec![],
build_host_deps: vec![],
},
];
let repos = generate_deps(&packages)?;
let actual = serde_json::to_string_pretty(&repos)?;
let expected = r#"[
{
"CipdFile": {
"name": "dist_goldctl-2021.03.31-amd64.zip",
"downloaded_file_path": "goldctl-2021.03.31-amd64.zip",
"url": "cipd://skia/tools/goldctl/linux-amd64:0ov3TU"
}
},
{
"HttpFile": {
"name": "dist_google-api-core-1.19.0.tar.gz",
"downloaded_file_path": "google-api-core-1.19.0.tar.gz",
"integrity": "sha256-ASNG",
"urls": [
"https://commondatastorage.googleapis.com/chromeos-localmirror/distfiles/google-api-core-1.19.0.tar.gz"
]
}
},
{
"GsFile": {
"name": "dist_secret-file.tar.gz",
"downloaded_file_path": "secret-file.tar.gz",
"url": "gs://secret-bucket/secret-file.tar.gz"
}
}
]"#;
assert_eq!(actual, expected);
Ok(())
}
}