diff --git a/Cargo.toml b/Cargo.toml index 409760d..41c6614 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] -members = ["cast_checks", "convert", "macro"] +members = ["cast_checks", "ci", "convert", "macro"] +default-members = ["cast_checks", "convert", "macro"] resolver = "2" [workspace.lints.rust.unexpected_cfgs] diff --git a/cast_checks/Cargo.toml b/cast_checks/Cargo.toml index 4816d6b..b14be65 100644 --- a/cast_checks/Cargo.toml +++ b/cast_checks/Cargo.toml @@ -13,7 +13,6 @@ cast_checks_macro = { version = "=0.1.6", path = "../macro" } [dev-dependencies] assert_cmd = "2.0" -ctor = "0.12" predicates = "3.0" regex = "1.9" tempfile = "3.8" diff --git a/cast_checks/tests/ci.rs b/cast_checks/tests/ci.rs deleted file mode 100644 index 49affa4..0000000 --- a/cast_checks/tests/ci.rs +++ /dev/null @@ -1,107 +0,0 @@ -use assert_cmd::Command; -use regex::Regex; -use std::{ - env::{remove_var, set_current_dir}, - path::Path, -}; -use tempfile::tempdir; - -#[ctor::ctor] -fn initialize() { - remove_var("CARGO_TERM_COLOR"); - set_current_dir("..").unwrap(); -} - -#[test] -fn clippy() { - Command::new("cargo") - .args([ - "clippy", - "--all-features", - "--all-targets", - "--", - "--deny=warnings", - "--warn=clippy::pedantic", - "--allow=clippy::let-underscore-untyped", - "--allow=clippy::missing-panics-doc", - ]) - .assert() - .success(); -} - -#[test] -fn format() { - Command::new("rustup") - .args(["run", "nightly", "cargo", "fmt", "--check"]) - .assert() - .success(); -} - -#[test] -fn license() { - let re = Regex::new(r"^[^:]*\b(Apache-2.0|BSD-3-Clause|MIT|Zlib)\b").unwrap(); - - for line in std::str::from_utf8( - &Command::new("cargo") - .arg("license") - .assert() - .success() - .get_output() - .stdout, - ) - .unwrap() - .lines() - { - assert!(re.is_match(line), "{line:?} does not match"); - } -} - -#[test] -fn markdown_link_check() { - let tempdir = tempdir().unwrap(); - - Command::new("npm") - .args(["install", "markdown-link-check"]) - .current_dir(&tempdir) - .assert() - .success(); - - let readme_md = Path::new(env!("CARGO_MANIFEST_DIR")).join("../README.md"); - - Command::new("npx") - .args(["markdown-link-check", &readme_md.to_string_lossy()]) - .current_dir(&tempdir) - .assert() - .success(); -} - -#[test] -fn prettier() { - let tempdir = tempdir().unwrap(); - - Command::new("npm") - .args(["install", "prettier"]) - .current_dir(&tempdir) - .assert() - .success(); - - Command::new("npx") - .args([ - "prettier", - "--check", - &format!("{}/../**/*.md", env!("CARGO_MANIFEST_DIR")), - &format!("{}/../**/*.yml", env!("CARGO_MANIFEST_DIR")), - &format!("!{}/../target/**", env!("CARGO_MANIFEST_DIR")), - ]) - .current_dir(&tempdir) - .assert() - .success(); -} - -#[test] -fn sort() { - Command::new("cargo") - .args(["sort", "--check"]) - .assert() - .success(); -} diff --git a/cast_checks/tests/inner_attribute_example.rs b/cast_checks/tests/inner_attribute_example.rs index 3990aac..f0ad223 100644 --- a/cast_checks/tests/inner_attribute_example.rs +++ b/cast_checks/tests/inner_attribute_example.rs @@ -1,12 +1,6 @@ use assert_cmd::Command; -use std::env::remove_var; use tempfile::tempdir; -#[ctor::ctor] -fn initialize() { - remove_var("CARGO_TERM_COLOR"); -} - #[test] fn build() { run_command("build", |mut command| { @@ -46,6 +40,7 @@ fn run_command(subcommand: &str, f: fn(Command)) { &tempdir.path().to_string_lossy(), ]) .current_dir("../inner_attribute_example") + .env_remove("CARGO_TERM_COLOR") .env_remove("RUSTUP_TOOLCHAIN"); f(command); diff --git a/ci/Cargo.toml b/ci/Cargo.toml new file mode 100644 index 0000000..d419693 --- /dev/null +++ b/ci/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "ci" +version = "0.1.0" +edition = "2024" +license = "MIT OR Apache-2.0" +publish = false + +[dependencies] +anyhow = "1.0" +cargo_metadata = "0.23" + +[dev-dependencies] +assert_cmd = "2.0" +regex = "1.9" +tempfile = "3.8" + +[lints] +workspace = true diff --git a/ci/src/main.rs b/ci/src/main.rs new file mode 100644 index 0000000..fdc168b --- /dev/null +++ b/ci/src/main.rs @@ -0,0 +1,159 @@ +use anyhow::{Result, anyhow, bail}; +use cargo_metadata::{Message, camino::Utf8PathBuf}; +use std::{path::Path, process::Command}; + +fn main() { + let executable = test_executable().unwrap(); + let status = Command::new(executable) + .current_dir(workspace_root()) + .status() + .unwrap(); + assert!(status.success()); +} + +fn test_executable() -> Result { + let mut command = Command::new("cargo"); + let output = command + .args([ + "build", + "--manifest-path", + "ci/Cargo.toml", + "--tests", + "--message-format=json", + ]) + .current_dir(workspace_root()) + .output()?; + if !output.status.success() { + bail!("command failed: {command:?}"); + } + let messages = + Message::parse_stream(output.stdout.as_slice()).collect::, _>>()?; + let executables = messages + .into_iter() + .filter_map(|message| { + let Message::CompilerArtifact(artifact) = message else { + return None; + }; + if artifact.target.name != "ci" || !artifact.target.is_bin() || !artifact.profile.test { + return None; + } + artifact.executable + }) + .collect::>(); + if executables.len() >= 2 { + bail!("found multiple test executables: {executables:?}"); + } + executables + .into_iter() + .next() + .ok_or_else(|| anyhow!("found no test executables")) +} + +fn workspace_root() -> &'static Path { + Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .expect("ci crate should be under workspace root") +} + +#[cfg(test)] +mod tests { + use assert_cmd::Command; + use regex::Regex; + use std::path::Path; + use tempfile::tempdir; + + #[test] + fn clippy() { + Command::new("cargo") + .args([ + "clippy", + "--all-features", + "--all-targets", + "--", + "--deny=warnings", + "--warn=clippy::pedantic", + "--allow=clippy::let-underscore-untyped", + "--allow=clippy::missing-panics-doc", + ]) + .assert() + .success(); + } + + #[test] + fn format() { + Command::new("rustup") + .args(["run", "nightly", "cargo", "fmt", "--check"]) + .assert() + .success(); + } + + #[test] + fn license() { + let re = Regex::new(r"^[^:]*\b(Apache-2.0|BSD-3-Clause|MIT|Zlib)\b").unwrap(); + + for line in std::str::from_utf8( + &Command::new("cargo") + .arg("license") + .env_remove("CARGO_TERM_COLOR") + .assert() + .success() + .get_output() + .stdout, + ) + .unwrap() + .lines() + { + assert!(re.is_match(line), "{line:?} does not match"); + } + } + + #[test] + fn markdown_link_check() { + let tempdir = tempdir().unwrap(); + + Command::new("npm") + .args(["install", "markdown-link-check"]) + .current_dir(&tempdir) + .assert() + .success(); + + let readme_md = Path::new(env!("CARGO_MANIFEST_DIR")).join("../README.md"); + + Command::new("npx") + .args(["markdown-link-check", &readme_md.to_string_lossy()]) + .current_dir(&tempdir) + .assert() + .success(); + } + + #[test] + fn prettier() { + let tempdir = tempdir().unwrap(); + + Command::new("npm") + .args(["install", "prettier"]) + .current_dir(&tempdir) + .assert() + .success(); + + Command::new("npx") + .args([ + "prettier", + "--check", + &format!("{}/../**/*.md", env!("CARGO_MANIFEST_DIR")), + &format!("{}/../**/*.yml", env!("CARGO_MANIFEST_DIR")), + &format!("!{}/../target/**", env!("CARGO_MANIFEST_DIR")), + ]) + .current_dir(&tempdir) + .assert() + .success(); + } + + #[test] + fn sort() { + Command::new("cargo") + .args(["sort", "--check"]) + .assert() + .success(); + } +}