Introduce the first working version of the tool

This commit is contained in:
Kerollmops
2025-12-02 11:38:51 +01:00
parent 82adabc5a0
commit 56673fee56
7 changed files with 143 additions and 18 deletions

1
Cargo.lock generated
View File

@@ -7806,6 +7806,7 @@ dependencies = [
"futures-core",
"futures-util",
"reqwest",
"semver",
"serde",
"serde_json",
"sha2",

View File

@@ -22,6 +22,7 @@ reqwest = { version = "0.12.24", features = [
"json",
"rustls-tls",
], default-features = false }
semver = "1.0.27"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.145"
sha2 = "0.10.9"

View File

@@ -23,7 +23,7 @@ pub fn default_dashboard_url() -> String {
/// Run benchmarks from a workload
#[derive(Parser, Debug)]
pub struct BenchDeriveArgs {
pub struct BenchArgs {
/// Common arguments shared with other commands
#[command(flatten)]
common: CommonArgs,
@@ -59,7 +59,7 @@ pub struct BenchDeriveArgs {
binary_path: Option<PathBuf>,
}
pub fn run(args: BenchDeriveArgs) -> anyhow::Result<()> {
pub fn run(args: BenchArgs) -> anyhow::Result<()> {
setup_logs(&args.common.log_filter)?;
// fetch environment and build info

View File

@@ -12,7 +12,7 @@ use tokio::task::JoinHandle;
use uuid::Uuid;
use super::dashboard::DashboardClient;
use super::BenchDeriveArgs;
use super::BenchArgs;
use crate::common::assets::{self, Asset};
use crate::common::client::Client;
use crate::common::command::{run_commands, Command};
@@ -40,7 +40,7 @@ async fn run_workload_commands(
meili_client: &Arc<Client>,
workload_uuid: Uuid,
workload: &BenchWorkload,
args: &BenchDeriveArgs,
args: &BenchArgs,
run_number: u16,
) -> anyhow::Result<JoinHandle<anyhow::Result<File>>> {
let report_folder = &args.report_folder;
@@ -95,7 +95,7 @@ pub async fn execute(
invocation_uuid: Uuid,
master_key: Option<&str>,
workload: BenchWorkload,
args: &BenchDeriveArgs,
args: &BenchArgs,
binary_path: Option<&Path>,
) -> anyhow::Result<()> {
assets::fetch_assets(assets_client, &workload.assets, &args.common.asset_folder).await?;
@@ -143,7 +143,7 @@ async fn execute_run(
workload_uuid: Uuid,
master_key: Option<&str>,
workload: &BenchWorkload,
args: &BenchDeriveArgs,
args: &BenchArgs,
binary_path: Option<&Path>,
run_number: u16,
) -> anyhow::Result<tokio::task::JoinHandle<anyhow::Result<std::fs::File>>> {

View File

@@ -1,16 +1,34 @@
use std::collections::HashSet;
use std::{collections::HashSet, process::Stdio};
use anyhow::Context;
use clap::Parser;
use xtask::{bench::BenchDeriveArgs, test::TestDeriveArgs};
use semver::{Prerelease, Version};
use xtask::{bench::BenchArgs, test::TestArgs};
/// This is the version of the crate but also the current Meilisearch version
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
/// List features available in the workspace
#[derive(Parser, Debug)]
struct ListFeaturesDeriveArgs {
struct ListFeaturesArgs {
/// Feature to exclude from the list. Use a comma to separate multiple features.
#[arg(short, long, value_delimiter = ',')]
exclude_feature: Vec<String>,
}
/// Create a git tag for the current version
///
/// The tag will of the form prototype-v<version>-<name>.<increment>
#[derive(Parser, Debug)]
struct PrototypeArgs {
/// Name of the prototype to generate
name: String,
/// If true refuses to increment the tag if it already exists
/// else refuses to generate new tag and expect the tag to exist.
#[arg(long)]
generate_new: bool,
}
/// Utilitary commands
#[derive(Parser, Debug)]
#[command(author, version, about, long_about)]
@@ -18,9 +36,10 @@ struct ListFeaturesDeriveArgs {
#[command(bin_name = "cargo xtask")]
#[allow(clippy::large_enum_variant)] // please, that's enough...
enum Command {
ListFeatures(ListFeaturesDeriveArgs),
Bench(BenchDeriveArgs),
Test(TestDeriveArgs),
ListFeatures(ListFeaturesArgs),
Bench(BenchArgs),
GeneratePrototype(PrototypeArgs),
Test(TestArgs),
}
fn main() -> anyhow::Result<()> {
@@ -28,12 +47,13 @@ fn main() -> anyhow::Result<()> {
match args {
Command::ListFeatures(args) => list_features(args),
Command::Bench(args) => xtask::bench::run(args)?,
Command::GeneratePrototype(args) => generate_prototype(args)?,
Command::Test(args) => xtask::test::run(args)?,
}
Ok(())
}
fn list_features(args: ListFeaturesDeriveArgs) {
fn list_features(args: ListFeaturesArgs) {
let exclude_features: HashSet<_> = args.exclude_feature.into_iter().collect();
let metadata = cargo_metadata::MetadataCommand::new().no_deps().exec().unwrap();
let features: Vec<String> = metadata
@@ -46,3 +66,106 @@ fn list_features(args: ListFeaturesDeriveArgs) {
let features = features.join(" ");
println!("{features}")
}
fn generate_prototype(args: PrototypeArgs) -> anyhow::Result<()> {
let PrototypeArgs { name, generate_new: create_new } = args;
if name.rsplit_once(['.', '-']).filter(|(_, t)| t.chars().all(char::is_numeric)).is_some() {
anyhow::bail!(
"The increment must not be part of the name and will be rather incremented by this command."
);
}
// 1. Fetch the crate version
let version = Version::parse(VERSION).context("while semver-parsing the crate version")?;
// 2. Pull tags from remote and retrieve last prototype tag
std::process::Command::new("git")
.arg("fetch")
.arg("--tags")
.stderr(Stdio::inherit())
.stdout(Stdio::inherit())
.status()?;
let output = std::process::Command::new("git")
.arg("tag")
.args(["--list", "prototype-v*"])
.stderr(Stdio::inherit())
.output()?;
let output =
String::try_from(output.stdout).context("while converting the tag list into a string")?;
let mut highest_increment = None;
for tag in output.lines() {
let Some(version) = tag.strip_prefix("prototype-v") else {
continue;
};
let Ok(version) = Version::parse(version) else {
continue;
};
let Ok(proto) = PrototypePrerelease::from_str(version.pre.as_str()) else {
continue;
};
if proto.name() == name {
highest_increment = match highest_increment {
Some(last) if last < proto.increment() => Some(proto.increment()),
Some(last) => Some(last),
None => Some(proto.increment()),
};
}
}
// 3. Generate the new tag name (without git, just a string)
let increment = match (create_new, highest_increment) {
(true, None) => 0,
(true, Some(increment)) => anyhow::bail!(
"A prototype with the name `{name}` already exists with increment `{increment}`"
),
(false, None) => anyhow::bail!(
"Prototype `{name}` is missing and must exist to be incremented.\n\
Use the --generate-new flag to create a new prototype with an increment at 0."
),
(false, Some(increment)) => {
increment.checked_add(1).context("While incrementing by one the increment")?
}
};
// Note that we cannot have leading zeros in the increment
let pre = format!("{name}.{increment}").parse().context("while parsing pre-release name")?;
let tag_name = Version { pre, ..version };
println!("prototype-v{tag_name}");
Ok(())
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct PrototypePrerelease {
pre: Prerelease,
}
impl PrototypePrerelease {
fn from_str(s: &str) -> anyhow::Result<Self> {
Prerelease::new(s)
.map_err(Into::into)
.and_then(|pre| {
if pre.rsplit_once('.').is_some() {
Ok(pre)
} else {
Err(anyhow::anyhow!("Invalid prototype name, missing name or increment"))
}
})
.map(|pre| PrototypePrerelease { pre })
}
fn name(&self) -> &str {
self.pre.rsplit_once('.').expect("Missing prototype name").0
}
fn increment(&self) -> u32 {
self.pre
.as_str()
.rsplit_once('.')
.map(|(_, tail)| tail.parse().expect("Invalid increment"))
.expect("Missing increment")
}
}

View File

@@ -17,7 +17,7 @@ pub use workload::TestWorkload;
/// Run tests from a workload
#[derive(Parser, Debug)]
pub struct TestDeriveArgs {
pub struct TestArgs {
/// Common arguments shared with other commands
#[command(flatten)]
common: CommonArgs,
@@ -31,7 +31,7 @@ pub struct TestDeriveArgs {
pub add_missing_responses: bool,
}
pub fn run(args: TestDeriveArgs) -> anyhow::Result<()> {
pub fn run(args: TestArgs) -> anyhow::Result<()> {
let rt = tokio::runtime::Builder::new_current_thread().enable_io().enable_time().build()?;
let _scope = rt.enter();
@@ -40,7 +40,7 @@ pub fn run(args: TestDeriveArgs) -> anyhow::Result<()> {
Ok(())
}
async fn run_inner(args: TestDeriveArgs) -> anyhow::Result<()> {
async fn run_inner(args: TestArgs) -> anyhow::Result<()> {
setup_logs(&args.common.log_filter)?;
// setup clients

View File

@@ -12,7 +12,7 @@ use crate::common::command::{run_commands, Command};
use crate::common::instance::Binary;
use crate::common::process::{self, delete_db, kill_meili};
use crate::common::workload::Workload;
use crate::test::TestDeriveArgs;
use crate::test::TestArgs;
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
@@ -81,7 +81,7 @@ pub struct TestWorkload {
impl TestWorkload {
pub async fn run(
mut self,
args: &TestDeriveArgs,
args: &TestArgs,
assets_client: &Client,
meili_client: &Arc<Client>,
asset_folder: &'static str,