use axoasset::LocalAsset;
use axoproject::platforms::triple_to_display_name;
use camino::Utf8PathBuf;
use std::collections::BTreeMap;
use crate::config::Config;
use crate::data::artifacts::{File, FileIdx, InstallMethod, InstallerIdx, TargetTriple};
use crate::data::{Context, Release};
use crate::errors::*;
use crate::site::javascript;
use serde::Serialize;
type DownloadableFiles = Vec<(FileIdx, File, Vec<String>)>;
type Platforms = BTreeMap<TargetTriple, Vec<InstallerIdx>>;
const JSON_VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Serialize, Debug, Clone)]
pub struct Platform {
target: Vec<TargetTriple>,
display_name: String,
installers: Vec<InstallerIdx>,
}
#[derive(Serialize, Debug)]
pub struct ArtifactsJson {
pub format_version: String,
#[serde(flatten)]
pub inner: ArtifactsContext,
}
#[derive(Serialize, Debug, Clone)]
pub struct ArtifactsContext {
tag: String,
formatted_date: Option<String>,
platforms_with_downloads: Vec<Platform>,
downloadable_files: DownloadableFiles,
release: Release,
os_script: String,
has_checksum_files: bool,
}
pub fn template_context(context: &Context, config: &Config) -> Result<Option<ArtifactsContext>> {
let Some(release) = context.latest() else {
return Ok(None);
};
let os_script = javascript::build_os_script_path(&config.build.path_prefix);
let platforms_with_downloads = filter_platforms(release)
.into_iter()
.map(|(target, installers)| Platform {
display_name: triple_to_display_name(&target)
.map(|s| s.to_string())
.unwrap_or_else(|| target.clone()),
target: vec![target],
installers,
})
.collect::<Vec<_>>();
let mut downloadable_files: Vec<_> = release
.artifacts
.installers()
.filter_map(|(_, installer)| {
let InstallMethod::Download { file } = installer.method else {
return None;
};
Some((
file,
release.artifacts.file(file).clone(),
installer
.targets
.keys()
.map(|s| {
triple_to_display_name(s)
.map(|s| s.to_string())
.unwrap_or_else(|| s.clone())
})
.collect::<Vec<_>>(),
))
})
.collect();
downloadable_files.sort_by_key(|(_, f, _)| f.name.clone());
if downloadable_files.is_empty() {
tracing::warn!("You seem to have release automation set up, but we didn't detect any releases. The install page and associated widget will be empty. To disable this, set `artifacts: false`");
}
let has_checksum_files = downloadable_files
.iter()
.any(|(_, f, _)| f.checksum_file.is_some());
Ok(Some(ArtifactsContext {
tag: release.source.version_tag().to_string(),
formatted_date: release.source.formatted_date(),
platforms_with_downloads,
release: release.to_owned(),
downloadable_files,
os_script,
has_checksum_files,
}))
}
pub fn write_artifacts_json(config: &Config, context: &ArtifactsContext) -> Result<()> {
let cloned = (*context).clone();
let json_struct = ArtifactsJson {
format_version: JSON_VERSION.to_string(),
inner: cloned,
};
let json_str = serde_json::to_string(&json_struct)?;
let mut path = Utf8PathBuf::from(config.build.dist_dir.clone());
path.push("artifacts.json");
LocalAsset::write_new_all(&json_str, path)?;
Ok(())
}
pub fn filter_platforms(release: &Release) -> Platforms {
let mut platforms = BTreeMap::new();
for (target, installer) in release.artifacts.installers_by_target().iter() {
let has_valid_installer = installer.iter().any(|i| {
let installer = release.artifacts.installer(i.to_owned());
matches!(installer.method, InstallMethod::Download { file: _ })
|| is_core_target(target)
});
if has_valid_installer {
platforms.insert(target.clone(), installer.to_vec());
}
}
if !platforms.is_empty() {
if let Some(entries) = platforms.get("x86_64-apple-darwin") {
if !platforms.contains_key("aarch64-apple-darwin") {
let entries = entries.clone();
platforms.insert("aarch64-apple-darwin".to_owned(), entries);
}
}
return platforms;
}
let mut universal_installers = vec![];
if let Some((_, installers)) = release.artifacts.installers_by_target().iter().next() {
for installer in installers {
if release
.artifacts
.installers_by_target()
.iter()
.all(|(_, installers)| installers.contains(installer))
{
universal_installers.push(*installer);
}
}
}
if !universal_installers.is_empty() {
let mut platforms = Platforms::default();
platforms.insert("all".to_owned(), universal_installers);
return platforms;
}
Platforms::default()
}
fn is_core_target(target: &TargetTriple) -> bool {
target == "x86_64-pc-windows-msvc"
|| target == "aarch64-apple-darwin"
|| target == "x86_64-apple-darwin"
|| target == "x86_64-unknown-linux-gnu"
}