Files
meilisearch/crates/xtask/src/common/instance/release.rs
Louis Dureuil 792a72a23f Add missing cfg
2025-12-08 13:22:01 +01:00

195 lines
5.9 KiB
Rust

use std::collections::BTreeMap;
use std::fmt::Display;
use std::path::PathBuf;
use anyhow::Context;
use cargo_metadata::semver::Version;
use serde::{Deserialize, Serialize};
use super::Edition;
use crate::common::assets::{Asset, AssetFormat};
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct Release {
#[serde(default)]
pub edition: Edition,
pub version: Version,
}
impl Display for Release {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "v{}", self.version)?;
match self.edition {
Edition::Community => f.write_str(" Community Edition"),
Edition::Enterprise => f.write_str(" Enterprise Edition"),
}
}
}
impl Release {
pub fn binary_path(&self, asset_folder: &str) -> anyhow::Result<PathBuf> {
let mut asset_folder: PathBuf = asset_folder
.parse()
.with_context(|| format!("parsing asset folder `{asset_folder}` as a path"))?;
asset_folder.push(self.local_filename()?);
Ok(asset_folder)
}
fn local_filename(&self) -> anyhow::Result<String> {
let version = &self.version;
let arch = get_arch()?;
let base = self.edition.binary_base();
Ok(format!("{base}-{version}-{arch}"))
}
fn remote_filename(&self) -> anyhow::Result<String> {
let arch = get_arch()?;
let base = self.edition.binary_base();
Ok(format!("{base}-{arch}"))
}
async fn fetch_sha256(&self) -> anyhow::Result<String> {
let version = &self.version;
let asset_name = self.remote_filename()?;
// If version is lower than 1.15 there is no point in trying to get the sha256, GitHub didn't support it
if *version < Version::parse("1.15.0")? {
anyhow::bail!("version is lower than 1.15, sha256 not available");
}
#[derive(Deserialize)]
struct GithubReleaseAsset {
name: String,
digest: Option<String>,
}
#[derive(Deserialize)]
struct GithubRelease {
assets: Vec<GithubReleaseAsset>,
}
let url = format!(
"https://api.github.com/repos/meilisearch/meilisearch/releases/tags/v{version}"
);
let client = reqwest::Client::builder()
.user_agent("Meilisearch bench xtask")
.build()
.context("failed to build reqwest client")?;
let body = client.get(url).send().await?.text().await?;
let data: GithubRelease = serde_json::from_str(&body)?;
let digest = data
.assets
.into_iter()
.find(|asset| asset.name.as_str() == asset_name.as_str())
.with_context(|| format!("asset {asset_name} not found in release {self}"))?
.digest
.with_context(|| format!("asset {asset_name} has no digest"))?;
let sha256 = digest
.strip_prefix("sha256:")
.map(|s| s.to_string())
.context("invalid sha256 format")?;
Ok(sha256)
}
async fn add_asset(&self, assets: &mut BTreeMap<String, Asset>) -> anyhow::Result<()> {
let local_filename = self.local_filename()?;
let version = &self.version;
if assets.contains_key(&local_filename) {
return Ok(());
}
let remote_filename = self.remote_filename()?;
// Try to get the sha256 but it may fail if Github is rate limiting us
// We hardcode some values to speed up tests and avoid hitting Github
// Also, versions prior to 1.15 don't have sha256 available anyway
let sha256 = match local_filename.as_str() {
"meilisearch-1.12.0-macos-apple-silicon" => {
Some("3b384707a5df9edf66f9157f0ddb70dcd3ac84d4887149169cf93067d06717b7".into())
}
"meilisearch-1.12.0-linux-amd64" => {
Some("865a3fc222e3b3bd1f4b64346cb114b9669af691aae28d71fa68dbf39427abcf".into())
}
_ => match self.fetch_sha256().await {
Ok(sha256) => Some(sha256),
Err(err) => {
tracing::warn!("failed to get sha256 for release {self}: {err}");
None
}
},
};
let url = format!(
"https://github.com/meilisearch/meilisearch/releases/download/v{version}/{remote_filename}"
);
let asset = Asset {
local_location: Some(local_filename.clone()),
remote_location: Some(url),
format: AssetFormat::Raw,
sha256,
};
assets.insert(local_filename, asset);
Ok(())
}
}
pub fn get_arch() -> anyhow::Result<&'static str> {
// linux-aarch64
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
{
Ok("linux-aarch64")
}
// linux-amd64
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
{
Ok("linux-amd64")
}
// macos-amd64
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
{
Ok("macos-amd64")
}
// macos-apple-silicon
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
{
Ok("macos-apple-silicon")
}
// windows-amd64
#[cfg(all(target_os = "windows", target_arch = "x86_64"))]
{
Ok("windows-amd64")
}
#[cfg(not(all(target_os = "windows", target_arch = "x86_64")))]
#[cfg(not(all(target_os = "linux", target_arch = "aarch64")))]
#[cfg(not(all(target_os = "linux", target_arch = "x86_64")))]
#[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
#[cfg(not(all(target_os = "macos", target_arch = "x86_64")))]
anyhow::bail!("unsupported platform")
}
pub async fn add_releases_to_assets(
assets: &mut BTreeMap<String, Asset>,
releases: impl IntoIterator<Item = &Release>,
) -> anyhow::Result<()> {
for release in releases {
release.add_asset(assets).await?;
}
Ok(())
}