From 42ca1a8aa0b66ce444945305a321fcdcd3d386cb Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Tue, 17 Sep 2024 19:40:24 -0700 Subject: [PATCH] add xtask `tag` command Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- Cargo.lock | 26 ++-- cspell.json | 1 + xtask/Cargo.toml | 2 + xtask/src/main.rs | 364 ++++++++++++++++++++++++++++++---------------- 4 files changed, 259 insertions(+), 134 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1c9b09..ef53526 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -257,7 +257,7 @@ version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "syn 2.0.77", @@ -561,12 +561,6 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -1274,6 +1268,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "serde" version = "1.0.203" @@ -1476,9 +1476,9 @@ checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "strum" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ "phf 0.10.1", "strum_macros", @@ -1486,11 +1486,11 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", "rustversion", @@ -2160,6 +2160,7 @@ dependencies = [ "proc-macro2", "quote", "regex", + "semver", "serde", "serde_derive", "serde_ignored", @@ -2167,6 +2168,7 @@ dependencies = [ "serde_path_to_error", "serde_with", "stationeers_data", + "strum", "syn 2.0.77", "textwrap", "thiserror", diff --git a/cspell.json b/cspell.json index e8f8295..9041f29 100644 --- a/cspell.json +++ b/cspell.json @@ -76,6 +76,7 @@ "codegen", "columnated", "composter", + "consts", "conv", "Cryo", "cstyle", diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 27a4793..b5b21a6 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -29,3 +29,5 @@ proc-macro2 = "1.0.82" num-integer = "0.1.46" num = "0.4.3" uneval = "0.2.4" +semver = "1.0.23" +strum = { version = "0.26.3", features = ["derive"] } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index b5bdd6a..211e354 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,6 +1,10 @@ -use std::process::{Command, ExitStatus}; +use std::{ + ffi::OsString, + process::{Command, ExitStatus}, +}; -use clap::{Parser, Subcommand}; +use clap::{builder::TypedValueParser, Parser, Subcommand}; +use strum::VariantNames; mod enums; mod generate; @@ -23,7 +27,6 @@ struct Args { } const PACKAGES: &[&str] = &["ic10lsp_wasm", "ic10emu_wasm"]; -const VALID_VERSION_TYPE: &[&str] = &["patch", "minor", "major"]; const VALID_GENERATE_TYPE: &[&str] = &["enums", "instructions", "database"]; const DEFAULT_GENERATE: &[&str] = &["enums"]; fn parse_generate_modules(s: &str) -> Result { @@ -36,6 +39,18 @@ fn parse_generate_modules(s: &str) -> Result { Ok(s.to_string()) } +#[derive( + Debug, Clone, PartialEq, strum::EnumString, strum::Display, strum::VariantNames, strum::AsRefStr, +)] +enum VersionBumpType { + #[strum(serialize = "patch")] + Patch, + #[strum(serialize = "minor")] + Minor, + #[strum(serialize = "major")] + Major, +} + #[derive(Debug, Subcommand)] enum Task { /// Build the packages @@ -59,11 +74,12 @@ enum Task { Deploy {}, /// bump the cargo.toml and package,json versions Version { - #[arg(default_value = "patch", value_parser = clap::builder::PossibleValuesParser::new(VALID_VERSION_TYPE))] - version: String, + #[arg(default_value = "patch", value_parser = clap::builder::PossibleValuesParser::new(VersionBumpType::VARIANTS).map(|s| s.parse::().unwrap()))] + bump: VersionBumpType, }, - /// update changelog + /// Update changelog Changelog {}, + /// Generate modules and databases from extracted stationeers data Generate { #[arg(long, short = 'm', value_delimiter = ',', default_values = DEFAULT_GENERATE, value_parser = parse_generate_modules)] modules: Vec, @@ -74,6 +90,11 @@ enum Task { /// Can also point directly at a folder containing the two files. path: Option, }, + /// Update changelog and tag release, optionally bump the version at the same time + Tag { + #[arg(value_parser = clap::builder::PossibleValuesParser::new(VersionBumpType::VARIANTS).map(|s| s.parse::().unwrap()))] + bump: Option, + }, } #[derive(thiserror::Error)] @@ -130,116 +151,112 @@ fn main() -> color_eyre::Result<()> { build(&args, packages, *release, &workspace, rest)?; } Task::Start {} => { - pnpm_install(&args, &workspace)?; - eprintln!("Starting server"); - let mut cmd = Command::new(&args.manager); - cmd.current_dir(&workspace.join("www")); - cmd.args(["run", "start"]).status().map_err(|e| { - Error::Command(format!("{}", cmd.get_program().to_string_lossy()), e) - })?; + start_server(&args, &workspace)?; } Task::Deploy {} => { - pnpm_install(&args, &workspace)?; - eprintln!("Production Build"); - let mut cmd = Command::new(&args.manager); - cmd.current_dir(&workspace.join("www")); - cmd.args(["run", "build"]).status().map_err(|e| { - Error::Command(format!("{}", cmd.get_program().to_string_lossy()), e) - })?; + deploy_web(&args, &workspace)?; } - Task::Version { version } => { - let mut cmd = Command::new("cargo"); - cmd.current_dir(&workspace); - cmd.args(["set-version", "--bump", &version]) - .status() - .map_err(|e| { - Error::Command(format!("{}", cmd.get_program().to_string_lossy()), e) - })?; - let mut cmd = Command::new(&args.manager); - cmd.current_dir(&workspace.join("www")); - cmd.args(["version", &version]).status().map_err(|e| { - Error::Command(format!("{}", cmd.get_program().to_string_lossy()), e) - })?; + Task::Version { bump } => { + bump_version(&args, &workspace, bump)?; } Task::Changelog {} => { - let mut cmd = Command::new("git-changelog"); - cmd.current_dir(&workspace); - cmd.args([ - "-io", - "CHANGELOG.md", - "-t", - "path:CHANGELOG.md.jinja", - "-c", - "conventional", - "--bump", - VERSION.unwrap_or("auto"), - "--parse-refs", - "--trailers", - ]) - .status() - .map_err(|e| Error::Command(format!("{}", cmd.get_program().to_string_lossy()), e))?; + update_changelog(&workspace)?; } Task::Generate { modules, path } => { - let path = match path { - Some(path) => { - let mut path = std::path::PathBuf::from(path); - if path.exists() - && path - .parent() - .and_then(|p| p.file_name()) - .is_some_and(|p| p == "Stationeers") - && path.file_name().is_some_and(|name| { - (std::env::consts::OS == "windows" && name == "rocketstation.exe") - || (name == "rocketstation") - || (name == "rocketstation_Data") - }) - { - path = path.parent().unwrap().to_path_buf(); - } - if path.is_dir() - && path.file_name().is_some_and(|name| name == "Stationeers") - && path.join("Stationpedia").join("Stationpedia.json").exists() - { - path = path.join("Stationpedia"); - } - if path.is_file() - && path - .file_name() - .is_some_and(|name| name == "Stationpedia.json") - { - path = path.parent().unwrap().to_path_buf(); - } - path - } - None => { - let mut path = workspace.clone(); - if path.join("data").join("Stationpedia.json").exists() - && path.join("data").join("Enums.json").exists() - { - path = path.join("data") - } - - path - } - }; - - if path.is_dir() - && path.join("Stationpedia.json").exists() - && path.join("Enums.json").exists() - { - generate::generate( - &path, - &workspace, - &modules.iter().map(String::as_str).collect::>(), - )?; - } else { - return Err(Error::BadStationeersPath(path).into()); - } + generate_data(&workspace, modules, path)?; + } + Task::Tag { bump } => { + tag_release(&args, &workspace, bump.as_ref())?; } } Ok(()) } +fn build_command(program: S, args: I, working_dir: P) -> Command +where + S: AsRef, + A: AsRef, + I: IntoIterator, + P: AsRef, +{ + let mut cmd = Command::new(program); + cmd.current_dir(working_dir); + cmd.args(args); + cmd +} + +fn run_command( + program: S, + args: I, + working_dir: P, +) -> Result<(ExitStatus, Command), Error> +where + S: AsRef, + A: AsRef, + I: IntoIterator, + P: AsRef, +{ + let mut cmd = build_command(program, args, working_dir); + let status = cmd + .status() + .map_err(|e| Error::Command(format!("{}", cmd.get_program().to_string_lossy()), e))?; + Ok((status, cmd)) +} + +fn start_server(args: &Args, workspace: &std::path::Path) -> Result<(), Error> { + pnpm_install(&args, &workspace)?; + eprintln!("Starting server"); + run_command(&args.manager, ["run", "start"], &workspace.join("www"))?; + Ok(()) +} + +fn deploy_web(args: &Args, workspace: &std::path::Path) -> Result<(), Error> { + pnpm_install(&args, &workspace)?; + eprintln!("Production Build"); + run_command(&args.manager, ["run", "build"], &workspace.join("www"))?; + Ok(()) +} + +fn bump_version( + args: &Args, + workspace: &std::path::Path, + bump: &VersionBumpType, +) -> Result<(), Error> { + run_command( + "cargo", + ["set-version", "--bump", bump.as_ref()], + &workspace, + )?; + run_command( + &args.manager, + ["version", bump.as_ref()], + &workspace.join("www"), + )?; + Ok(()) +} + +fn update_changelog(workspace: &std::path::Path) -> Result<(), Error> { + run_command( + "git-changelog", + [ + "-io", + "CHANGELOG.md", + "-t", + "path:CHANGELOG.md.jinja", + "-c", + "conventional", + "--sections", + "chore,feat,fix,perf,revert", + "--bump", + VERSION.unwrap_or("auto"), + "--parse-refs", + "--trailers", + ], + &workspace, + )?; + Ok(()) +} + fn build + std::fmt::Debug + std::fmt::Display>( args: &Args, packages: &[P], @@ -260,19 +277,16 @@ fn build + std::fmt::Debug + std::fmt::Display>( package, rest.join(std::ffi::OsStr::new(" ")).to_string_lossy(), ); - let mut cmd = Command::new(&args.wasm_pack); - cmd.current_dir(workspace); - cmd.arg("build"); - if release { - cmd.arg("--release"); - } else { - cmd.arg("--dev"); - } - cmd.arg(package); - cmd.args(rest); - let status = cmd - .status() - .map_err(|e| Error::Command(format!("{}", cmd.get_program().to_string_lossy()), e))?; + let cmd_args: [std::ffi::OsString; 3] = [ + "build".into(), + if release { + "--release".into() + } else { + "--dev".into() + }, + package.into(), + ]; + let (status, cmd) = run_command(&args.wasm_pack, [&cmd_args, rest].concat(), workspace)?; if status.success() { eprintln!("{} built successfully", package); } else { @@ -288,9 +302,115 @@ fn build + std::fmt::Debug + std::fmt::Display>( fn pnpm_install(args: &Args, workspace: &std::path::Path) -> Result { eprintln!("Running `pnpm install`"); - let mut cmd = Command::new(&args.manager); - cmd.current_dir(&workspace.join("www")); - cmd.args(["install"]) - .status() - .map_err(|e| Error::Command(format!("{}", cmd.get_program().to_string_lossy()), e)) + let (status, _) = run_command(&args.manager, ["install"], &workspace.join("www"))?; + Ok(status) +} + +fn generate_data( + workspace: &std::path::Path, + modules: &Vec, + path: &Option, +) -> color_eyre::Result<()> { + let path = match path { + Some(path) => { + let mut path = std::path::PathBuf::from(path); + if path.exists() + && path + .parent() + .and_then(|p| p.file_name()) + .is_some_and(|p| p == "Stationeers") + && path.file_name().is_some_and(|name| { + (std::env::consts::OS == "windows" && name == "rocketstation.exe") + || (name == "rocketstation") + || (name == "rocketstation_Data") + }) + { + path = path.parent().unwrap().to_path_buf(); + } + if path.is_dir() + && path.file_name().is_some_and(|name| name == "Stationeers") + && path.join("Stationpedia").join("Stationpedia.json").exists() + { + path = path.join("Stationpedia"); + } + if path.is_file() + && path + .file_name() + .is_some_and(|name| name == "Stationpedia.json") + { + path = path.parent().unwrap().to_path_buf(); + } + path + } + None => { + let mut path = workspace.to_path_buf(); + if path.join("data").join("Stationpedia.json").exists() + && path.join("data").join("Enums.json").exists() + { + path = path.join("data") + } + + path + } + }; + + if path.is_dir() && path.join("Stationpedia.json").exists() && path.join("Enums.json").exists() + { + generate::generate( + &path, + &workspace, + &modules.iter().map(String::as_str).collect::>(), + )?; + } else { + return Err(Error::BadStationeersPath(path).into()); + } + Ok(()) +} + +fn tag_release( + args: &Args, + workspace: &std::path::Path, + bump: Option<&VersionBumpType>, +) -> Result<(), Error> { + let mut version = semver::Version::parse(VERSION.expect("package version to be set")) + .expect("package version to parse"); + if let Some(bump) = bump { + eprintln!("Bumping {} version", bump.as_ref()); + bump_version(args, workspace, bump)?; + match bump { + &VersionBumpType::Major => { + version.major += 1; + version.minor = 0; + version.patch = 0; + } + &VersionBumpType::Minor => { + version.minor += 1; + version.patch = 0; + } + &VersionBumpType::Patch => { + version.patch += 1; + } + } + } + update_changelog(workspace)?; + let tag_ver = format!("v{}", version); + run_command("git", ["add", "."], &workspace); + run_command( + "git", + ["commit", "-m", &format!("tagging version {}", &tag_ver)], + &workspace, + ); + run_command( + "git", + [ + "tag", + "-f", + "-s", + &tag_ver, + "-m", + &format!("Version {}", version), + ], + &workspace, + ); + Ok(()) }