diff --git a/.cargo/config.toml b/.cargo/config.toml index b2c9bde35..d43b6746f 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,5 @@ +[alias] +xtask = "run --package xtask --" + [doc.extern-map.registries] crates-io = "https://docs.rs/" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 967acdf48..eb8131373 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,14 +67,8 @@ jobs: - name: Clippy uses: actions-rs/cargo@v1 with: - command: clippy - args: --all-targets -- -D warnings - - - name: Clippy without default features - uses: actions-rs/cargo@v1 - with: - command: clippy - args: --all-targets --no-default-features --features native-tls,warp -- -D warnings + command: run + args: -p xtask -- ci clippy check-wasm: name: checking WASM builds diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e83d90897..7206d3996 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -24,6 +24,7 @@ jobs: - name: Load cache uses: Swatinem/rust-cache@v1 + # Keep in sync with xtask docs - name: Build docs uses: actions-rs/cargo@v1 env: diff --git a/Cargo.toml b/Cargo.toml index c66a4d73c..f1af4019e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,4 @@ [workspace] -members = ["crates/*"] +members = ["crates/*", "xtask"] +# xtask should only be compiled when invoked explicitly +default-members = ["crates/*"] diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 000000000..558dc2e23 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +clap = { version = "3.1.3", features = ["derive"] } +serde = { version = "1.0.136", features = ["derive"] } +serde_json = "1.0.79" +xshell = "0.1.17" diff --git a/xtask/src/ci.rs b/xtask/src/ci.rs new file mode 100644 index 000000000..e227f4c42 --- /dev/null +++ b/xtask/src/ci.rs @@ -0,0 +1,86 @@ +use std::{env, path::PathBuf}; + +use clap::{Args, Subcommand}; +use serde::Deserialize; +use xshell::{cmd, pushd}; + +use crate::{build_docs, DenyWarnings, Result}; + +#[derive(Args)] +pub struct CiArgs { + #[clap(subcommand)] + cmd: Option, +} + +#[derive(Subcommand)] +enum CiCommand { + /// Check style + Style, + /// Check for typos + Typos, + /// Check clippy lints + Clippy, + /// Check documentation + Docs, +} + +impl CiArgs { + pub fn run(self) -> Result<()> { + let _p = pushd(&workspace_root()?)?; + + match self.cmd { + Some(cmd) => match cmd { + CiCommand::Style => check_style(), + CiCommand::Typos => check_typos(), + CiCommand::Clippy => check_clippy(), + CiCommand::Docs => check_docs(), + }, + None => { + check_style()?; + check_clippy()?; + check_typos()?; + check_docs()?; + + Ok(()) + } + } + } +} + +fn check_style() -> Result<()> { + cmd!("rustup run nightly cargo fmt -- --check").run()?; + Ok(()) +} + +fn check_typos() -> Result<()> { + // FIXME: Print install instructions if command-not-found (needs an xshell + // change: https://github.com/matklad/xshell/issues/46) + cmd!("typos").run()?; + Ok(()) +} + +fn check_clippy() -> Result<()> { + cmd!("rustup run nightly cargo clippy --all-targets -- -D warnings").run()?; + cmd!( + "rustup run nightly cargo clippy --all-targets + --no-default-features --features native-tls,warp + -- -D warnings" + ) + .run()?; + Ok(()) +} + +fn check_docs() -> Result<()> { + build_docs([], DenyWarnings::Yes) +} + +fn workspace_root() -> Result { + #[derive(Deserialize)] + struct Metadata { + workspace_root: PathBuf, + } + + let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_owned()); + let metadata_json = cmd!("{cargo} metadata --no-deps --format-version 1").read()?; + Ok(serde_json::from_str::(&metadata_json)?.workspace_root) +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 000000000..a8745c9ac --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,55 @@ +mod ci; + +use ci::CiArgs; +use clap::{Parser, Subcommand}; +use xshell::cmd; + +type Result> = std::result::Result; + +#[derive(Parser)] +struct Xtask { + #[clap(subcommand)] + cmd: Command, +} + +#[derive(Subcommand)] +enum Command { + /// Run continuous integration checks + Ci(CiArgs), + /// Build the SDKs documentation + Doc { + /// Opens the docs in a browser after the operation + #[clap(long)] + open: bool, + }, +} + +fn main() -> Result<()> { + match Xtask::parse().cmd { + Command::Ci(ci) => ci.run(), + Command::Doc { open } => build_docs(open.then(|| "--open"), DenyWarnings::No), + } +} + +enum DenyWarnings { + Yes, + No, +} + +fn build_docs( + extra_args: impl IntoIterator, + deny_warnings: DenyWarnings, +) -> Result<()> { + let mut rustdocflags = "--enable-index-page -Zunstable-options --cfg docsrs".to_owned(); + if let DenyWarnings::Yes = deny_warnings { + rustdocflags += " -Dwarnings"; + } + + // Keep in sync with .github/workflows/docs.yml + cmd!("rustup run nightly cargo doc --no-deps --workspace --features docsrs -Zrustdoc-map") + .env("RUSTDOCFLAGS", rustdocflags) + .args(extra_args) + .run()?; + + Ok(()) +}