ci: add web client check job

This commit is contained in:
Benoît CORTIER 2023-05-18 16:33:47 -04:00 committed by Benoît Cortier
parent 124c3aa251
commit 493e8f13a4
13 changed files with 507 additions and 360 deletions

View file

@ -67,7 +67,7 @@ jobs:
cargo xtask wasm install
- name: Check
run: cargo xtask check wasm
run: cargo xtask wasm check
tests:
name: Tests [${{ matrix.os }}]
@ -124,3 +124,27 @@ jobs:
# Simply run all fuzz targets for a few seconds, just making there is nothing obviously wrong at a quick glance
- name: Fuzz
run: cargo xtask fuzz run
web:
name: Web Client
runs-on: ubuntu-20.04
needs: formatting
steps:
- uses: actions/checkout@v3
- name: Cache
uses: actions/cache@v3
with:
path: |
~/.cargo/registry/
~/.cargo/git/
./target/
./.cargo/local_root/
key: ${{ runner.os }}-${{ github.job }}-${{ hashFiles('fuzz/Cargo.lock') }}
- name: Prepare runner
run: cargo xtask web install
- name: Check
run: cargo xtask web check

View file

@ -33,7 +33,7 @@ Pay attention to the "**Architecture Invariant**" sections.
**Architectural Invariant**: no non-essential dependency is allowed.
**Architectural Invariant**: must be `#[no_std]`-compatible (eventually using the `alloc` crate). Usage of the standard
**Architectural Invariant**: must be `#[no_std]`-compatible (optionally using the `alloc` crate). Usage of the standard
library must be opt-in through a feature flag called `std` that is enabled by default. When the `alloc` crate is optional,
a feature flag called `alloc` must exist to enable its use.

11
fuzz/Cargo.lock generated
View file

@ -282,6 +282,7 @@ dependencies = [
"num-derive",
"num-integer",
"num-traits",
"pkcs1",
"sha1",
"tap",
"thiserror",
@ -398,6 +399,16 @@ version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "pkcs1"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
dependencies = [
"der",
"spki",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"

36
xtask/src/check.rs Normal file
View file

@ -0,0 +1,36 @@
use crate::prelude::*;
pub fn fmt(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("FORMATTING");
let output = cmd!(sh, "{CARGO} fmt --all -- --check").ignore_status().output()?;
if !output.status.success() {
anyhow::bail!("Bad formatting, please run 'cargo +stable fmt --all'");
}
println!("All good!");
Ok(())
}
pub fn lints(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("LINTS");
cmd!(sh, "{CARGO} clippy --workspace --locked -- -D warnings").run()?;
println!("All good!");
Ok(())
}
pub fn tests_compile(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("TESTS-COMPILE");
cmd!(sh, "{CARGO} test --workspace --locked --no-run").run()?;
println!("All good!");
Ok(())
}
pub fn tests_run(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("TESTS-RUN");
cmd!(sh, "{CARGO} test --workspace --locked").run()?;
println!("All good!");
Ok(())
}

19
xtask/src/clean.rs Normal file
View file

@ -0,0 +1,19 @@
use crate::prelude::*;
pub fn workspace(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("CLEAN");
cmd!(sh, "{CARGO} clean").run()?;
println!("Remove wasm package folder…");
sh.remove_path("./crates/ironrdp-web/pkg")?;
println!("Done.");
println!("Remove npm folders…");
sh.remove_path("./web-client/iron-remote-gui/node_modules")?;
sh.remove_path("./web-client/iron-remote-gui/dist")?;
sh.remove_path("./web-client/iron-svelte-client/node_modules")?;
println!("Done.");
Ok(())
}

View file

@ -8,22 +8,25 @@ FLAGS:
-h, --help Prints help information
TASKS:
check [all] Runs all checks
check fmt Checks formatting
check lints Checks lints
check tests Runs tests
check wasm Ensures wasm module is compatible for the web
ci Runs all checks required on CI
clean Clean workspace
coverage install Install dependencies required to generate the coverage report
coverage report Generate code-coverage data using tests and fuzz targets
fuzz corpus-fetch Minify fuzzing corpus
fuzz corpus-min Minify fuzzing corpus
fuzz corpus-push Minify fuzzing corpus
fuzz install Install dependencies required for fuzzing
fuzz run [--duration] [--target] Fuzz a specific target if any or all targets for a limited duration (default is 5s)
svelte-run Runs SvelteKit-based standalone Web Client
wasm install Install dependencies required to build the wasm target
check fmt Check formatting
check lints Check lints
check tests [--no-run] Compile tests and, unless specified otherwise, run them
check wasm Ensure WASM module is compatible for the web
ci Run all checks required on CI
clean Clean workspace
coverage install Install dependencies required to generate the coverage report
coverage report Generate code-coverage data using tests and fuzz targets
fuzz corpus-fetch Fetch fuzzing corpus from Azure storage
fuzz corpus-min Minify fuzzing corpus
fuzz corpus-push Push fuzzing corpus to Azure storage
fuzz install Install dependencies required for fuzzing
fuzz run [--duration <SECONDS>] [--target <NAME>]
Fuzz a specific target if any or all targets for a limited duration (default is 5s)
wasm check Ensure WASM module is compatible for the web
wasm install Install dependencies required to build the WASM target
web check Ensure Web Client is building without error
web install Install dependencies required to build and run Web Client
web run Run SvelteKit-based standalone Web Client
";
pub fn print_help() {
@ -31,14 +34,16 @@ pub fn print_help() {
}
pub enum Action {
CheckAll,
ShowHelp,
CheckFmt,
CheckLints,
CheckTests,
CheckWasm,
CheckTests {
no_run: bool,
},
Ci,
Clean,
CoverageInstall,
CoverageReport,
CovInstall,
CovReport,
FuzzCorpusFetch,
FuzzCorpusMin,
FuzzCorpusPush,
@ -47,9 +52,11 @@ pub enum Action {
duration: Option<u32>,
target: Option<String>,
},
ShowHelp,
SvelteRun,
WasmCheck,
WasmInstall,
WebCheck,
WebInstall,
WebRun,
}
pub fn parse_args() -> anyhow::Result<Action> {
@ -59,26 +66,28 @@ pub fn parse_args() -> anyhow::Result<Action> {
Action::ShowHelp
} else {
match args.subcommand()?.as_deref() {
Some("ci") => Action::CheckAll,
Some("check") => match args.subcommand()?.as_deref() {
Some("all") | None => Action::CheckAll,
Some("fmt") => Action::CheckFmt,
Some("lints") => Action::CheckLints,
Some("tests") => Action::CheckTests,
Some("wasm") => Action::CheckWasm,
Some(_) => anyhow::bail!("unknown check action"),
Some("tests") => Action::CheckTests {
no_run: args.contains("--no-run"),
},
Some(unknown) => anyhow::bail!("unknown check action: {unknown}"),
None => Action::ShowHelp,
},
Some("ci") => Action::Ci,
Some("clean") => Action::Clean,
Some("coverage") => match args.subcommand()?.as_deref() {
Some("install") => Action::CoverageInstall,
Some("report") => Action::CoverageReport,
Some(_) => anyhow::bail!("unknown coverage action"),
Some("cov") => match args.subcommand()?.as_deref() {
Some("install") => Action::CovInstall,
Some("report") => Action::CovReport,
Some(unknown) => anyhow::bail!("unknown coverage action: {unknown}"),
None => Action::ShowHelp,
},
Some("fuzz") => match args.subcommand()?.as_deref() {
Some("corpus-fetch") => Action::FuzzCorpusFetch,
Some("corpus-min") => Action::FuzzCorpusMin,
Some("corpus-push") => Action::FuzzCorpusPush,
Some("install") => Action::FuzzInstall,
Some("run") => Action::FuzzRun {
duration: args.opt_value_from_str("--duration")?,
target: args.opt_value_from_str("--target")?,
@ -87,13 +96,19 @@ pub fn parse_args() -> anyhow::Result<Action> {
duration: None,
target: None,
},
Some("install") => Action::FuzzInstall,
Some(_) => anyhow::bail!("unknown fuzz action"),
Some(unknown) => anyhow::bail!("unknown fuzz action: {unknown}"),
},
Some("svelte-run") => Action::SvelteRun,
Some("wasm") => match args.subcommand()?.as_deref() {
Some("check") => Action::WasmCheck,
Some("install") => Action::WasmInstall,
Some(_) => anyhow::bail!("unknown wasm action"),
Some(unknown) => anyhow::bail!("unknown wasm action: {unknown}"),
None => Action::ShowHelp,
},
Some("web") => match args.subcommand()?.as_deref() {
Some("check") => Action::WebCheck,
Some("install") => Action::WebInstall,
Some("run") => Action::WebRun,
Some(unknown) => anyhow::bail!("unknown web action: {unknown}"),
None => Action::ShowHelp,
},
None | Some(_) => Action::ShowHelp,

113
xtask/src/cov.rs Normal file
View file

@ -0,0 +1,113 @@
use crate::prelude::*;
pub fn install(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("COVERAGE-INSTALL");
cmd!(sh, "rustup install nightly --profile=minimal").run()?;
cmd!(sh, "rustup component add --toolchain nightly llvm-tools-preview").run()?;
cmd!(sh, "rustup component add llvm-tools-preview").run()?;
cmd!(
sh,
"{CARGO} install --debug --locked --root {LOCAL_CARGO_ROOT} cargo-fuzz@{CARGO_FUZZ_VERSION}"
)
.run()?;
cmd!(
sh,
"{CARGO} install --debug --locked --root {LOCAL_CARGO_ROOT} grcov@{GRCOV_VERSION}"
)
.run()?;
Ok(())
}
pub fn report(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("COVERAGE-REPORT");
println!("Remove leftovers");
sh.remove_path("./fuzz/coverage/")?;
sh.remove_path("./coverage/")?;
sh.create_dir("./coverage/binaries")?;
{
// Fuzz coverage
let _guard = sh.push_dir("./fuzz");
cmd!(sh, "{CARGO} clean").run()?;
for target in FUZZ_TARGETS {
cmd!(sh, "../{LOCAL_CARGO_ROOT}/bin/cargo-fuzz coverage {target}")
.env("RUSTUP_TOOLCHAIN", "nightly")
.run()?;
}
cmd!(sh, "cp -r ./target ../coverage/binaries/").run()?;
}
{
// Test coverage
cmd!(sh, "{CARGO} clean").run()?;
cmd!(sh, "rustup run nightly cargo test --workspace")
.env("CARGO_INCREMENTAL", "0")
.env("RUSTFLAGS", "-C instrument-coverage")
.env("LLVM_PROFILE_FILE", "./coverage/default-%m-%p.profraw")
.run()?;
cmd!(sh, "cp -r ./target/debug ./coverage/binaries/").run()?;
}
sh.create_dir("./docs")?;
cmd!(
sh,
"./{LOCAL_CARGO_ROOT}/bin/grcov . ./fuzz
--source-dir .
--binary-path ./coverage/binaries/
--output-type html
--branch
--ignore-not-existing
--ignore xtask/*
--ignore src/*
--ignore **/tests/*
--ignore crates/*-generators/*
--ignore crates/web/*
--ignore crates/client/*
--ignore crates/glutin-renderer/*
--ignore crates/glutin-client/*
--ignore crates/replay-client/*
--ignore crates/tls/*
--ignore fuzz/fuzz_targets/*
--ignore target/*
--ignore fuzz/target/*
--excl-start begin-no-coverage
--excl-stop end-no-coverage
-o ./docs/coverage"
)
.run()?;
println!("Code coverage report available in `./docs/coverage` folder");
println!("Clean up");
sh.remove_path("./coverage/")?;
sh.remove_path("./fuzz/coverage/")?;
sh.remove_path("./xtask/coverage/")?;
sh.read_dir("./crates")?
.into_iter()
.try_for_each(|crate_path| -> xshell::Result<()> {
for path in sh.read_dir(crate_path)? {
if path.ends_with("coverage") {
sh.remove_path(path)?;
}
}
Ok(())
})?;
Ok(())
}

93
xtask/src/fuzz.rs Normal file
View file

@ -0,0 +1,93 @@
use crate::prelude::*;
pub fn corpus_minify(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("FUZZ-CORPUS-MINIFY");
let _guard = sh.push_dir("./fuzz");
for target in FUZZ_TARGETS {
cmd!(sh, "../{LOCAL_CARGO_ROOT}/bin/cargo-fuzz cmin {target}")
.env("RUSTUP_TOOLCHAIN", "nightly")
.run()?;
}
Ok(())
}
pub fn corpus_fetch(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("FUZZ-CORPUS-FETCH");
cmd!(
sh,
"az storage blob download-batch --account-name fuzzingcorpus --source ironrdp --destination fuzz --output none"
)
.run()?;
Ok(())
}
pub fn corpus_push(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("FUZZ-CORPUS-PUSH");
cmd!(
sh,
"az storage blob sync --account-name fuzzingcorpus --container ironrdp --source fuzz/corpus --destination corpus --delete-destination true --output none"
)
.run()?;
cmd!(
sh,
"az storage blob sync --account-name fuzzingcorpus --container ironrdp --source fuzz/artifacts --destination artifacts --delete-destination true --output none"
)
.run()?;
Ok(())
}
pub fn install(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("FUZZ-INSTALL");
let cargo_fuzz_path: std::path::PathBuf = [LOCAL_CARGO_ROOT, "bin", "cargo-fuzz"].iter().collect();
if !sh.path_exists(cargo_fuzz_path) {
// Install in debug because it's faster to compile and we don't need execution speed anyway.
// cargo-fuzz version is pinned so we dont get different versions without intervention.
cmd!(
sh,
"{CARGO} install --debug --locked --root {LOCAL_CARGO_ROOT} cargo-fuzz@{CARGO_FUZZ_VERSION}"
)
.run()?;
}
cmd!(sh, "rustup install nightly --profile=minimal").run()?;
Ok(())
}
pub fn run(sh: &Shell, duration: Option<u32>, target: Option<String>) -> anyhow::Result<()> {
let _s = Section::new("FUZZ-RUN");
let _guard = sh.push_dir("./fuzz");
let duration = duration.unwrap_or(5).to_string();
let target_from_user = target.as_deref().map(|value| [value]);
let targets = if let Some(targets) = &target_from_user {
targets
} else {
FUZZ_TARGETS
};
for target in targets {
cmd!(
sh,
"../{LOCAL_CARGO_ROOT}/bin/cargo-fuzz run {target} -- -max_total_time={duration}"
)
.env("RUSTUP_TOOLCHAIN", "nightly")
.run()?;
}
println!("All good!");
Ok(())
}

View file

@ -1,6 +1,12 @@
mod check;
mod clean;
mod cli;
mod cov;
mod fuzz;
mod prelude;
mod section;
mod tasks;
mod wasm;
mod web;
use std::path::{Path, PathBuf};
@ -23,27 +29,38 @@ fn main() -> anyhow::Result<()> {
match action {
Action::ShowHelp => cli::print_help(),
Action::CheckAll => {
tasks::check_formatting(&sh)?;
tasks::run_tests(&sh)?;
tasks::check_lints(&sh)?;
tasks::check_wasm(&sh)?;
tasks::fuzz_run(&sh, None, None)?;
Action::CheckFmt => check::fmt(&sh)?,
Action::CheckLints => check::lints(&sh)?,
Action::CheckTests { no_run } => {
if no_run {
check::tests_compile(&sh)?;
} else {
check::tests_run(&sh)?;
}
}
Action::CheckFmt => tasks::check_formatting(&sh)?,
Action::CheckLints => tasks::check_lints(&sh)?,
Action::CheckTests => tasks::run_tests(&sh)?,
Action::CheckWasm => tasks::check_wasm(&sh)?,
Action::Clean => tasks::clean_workspace(&sh)?,
Action::CoverageInstall => tasks::coverage_install(&sh)?,
Action::CoverageReport => tasks::coverage_report(&sh)?,
Action::FuzzCorpusFetch => tasks::fuzz_corpus_fetch(&sh)?,
Action::FuzzCorpusMin => tasks::fuzz_corpus_minify(&sh)?,
Action::FuzzCorpusPush => tasks::fuzz_corpus_push(&sh)?,
Action::FuzzInstall => tasks::fuzz_install(&sh)?,
Action::FuzzRun { duration, target } => tasks::fuzz_run(&sh, duration, target)?,
Action::SvelteRun => tasks::svelte_run(&sh)?,
Action::WasmInstall => tasks::wasm_install(&sh)?,
Action::Ci => {
check::fmt(&sh)?;
check::tests_compile(&sh)?;
check::tests_run(&sh)?;
check::lints(&sh)?;
wasm::check(&sh)?;
fuzz::run(&sh, None, None)?;
web::install(&sh)?;
web::check(&sh)?;
}
Action::Clean => clean::workspace(&sh)?,
Action::CovInstall => cov::install(&sh)?,
Action::CovReport => cov::report(&sh)?,
Action::FuzzCorpusFetch => fuzz::corpus_fetch(&sh)?,
Action::FuzzCorpusMin => fuzz::corpus_minify(&sh)?,
Action::FuzzCorpusPush => fuzz::corpus_push(&sh)?,
Action::FuzzInstall => fuzz::install(&sh)?,
Action::FuzzRun { duration, target } => fuzz::run(&sh, duration, target)?,
Action::WasmCheck => wasm::check(&sh)?,
Action::WasmInstall => wasm::install(&sh)?,
Action::WebCheck => web::check(&sh)?,
Action::WebInstall => web::install(&sh)?,
Action::WebRun => web::run(&sh)?,
}
Ok(())

15
xtask/src/prelude.rs Normal file
View file

@ -0,0 +1,15 @@
pub use anyhow::Context as _;
pub use xshell::{cmd, Shell};
pub use crate::section::Section;
pub const CARGO: &str = env!("CARGO");
pub const LOCAL_CARGO_ROOT: &str = ".cargo/local_root/";
pub const CARGO_FUZZ_VERSION: &str = "0.11.2";
pub const GRCOV_VERSION: &str = "0.8.18";
pub const WASM_PACK_VERSION: &str = "0.11.1";
pub const WASM_PACKAGES: &[&str] = &["ironrdp-web"];
pub const FUZZ_TARGETS: &[&str] = &["pdu_decoding", "rle_decompression", "bitmap_stream"];

View file

@ -1,302 +1 @@
use anyhow::Context as _;
use xshell::{cmd, Shell};
use crate::section::Section;
const CARGO: &str = env!("CARGO");
const CARGO_FUZZ_VERSION: &str = "0.11.2";
const GRCOV_VERSION: &str = "0.8.18";
const LOCAL_CARGO_ROOT: &str = ".cargo/local_root/";
const WASM_PACKAGES: &[&str] = &["ironrdp-web"];
const FUZZ_TARGETS: &[&str] = &["pdu_decoding", "rle_decompression", "bitmap_stream"];
pub fn check_formatting(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("FORMATTING");
let output = cmd!(sh, "{CARGO} fmt --all -- --check").ignore_status().output()?;
if !output.status.success() {
anyhow::bail!("Bad formatting, please run 'cargo +stable fmt --all'");
}
println!("All good!");
Ok(())
}
pub fn run_tests(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("TESTS");
cmd!(sh, "{CARGO} test --workspace --locked").run()?;
println!("All good!");
Ok(())
}
pub fn check_lints(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("LINTS");
cmd!(sh, "{CARGO} clippy --workspace --locked -- -D warnings").run()?;
println!("All good!");
Ok(())
}
pub fn check_wasm(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("WASM-CHECK");
for package in WASM_PACKAGES {
println!("Check {package}");
cmd!(
sh,
"{CARGO} rustc --locked --target wasm32-unknown-unknown --package {package} --lib --crate-type cdylib"
)
.run()?;
// When building a library, `-` in the artifact name are replaced by `_`
let artifact_name = format!("{}.wasm", package.replace('-', "_"));
let output = cmd!(sh, "wasm2wat ./target/wasm32-unknown-unknown/debug/{artifact_name}").output()?;
let stdout = std::str::from_utf8(&output.stdout).context("wasm2wat output is not valid UTF-8")?;
if stdout.contains("import \"env\"") {
anyhow::bail!("Found undefined symbols in generated wasm file");
}
}
println!("All good!");
Ok(())
}
pub fn fuzz_run(sh: &Shell, duration: Option<u32>, target: Option<String>) -> anyhow::Result<()> {
let _s = Section::new("FUZZ-RUN");
let _guard = sh.push_dir("./fuzz");
let duration = duration.unwrap_or(5).to_string();
let target_from_user = target.as_deref().map(|value| [value]);
let targets = if let Some(targets) = &target_from_user {
targets
} else {
FUZZ_TARGETS
};
for target in targets {
cmd!(
sh,
"../{LOCAL_CARGO_ROOT}/bin/cargo-fuzz run {target} -- -max_total_time={duration}"
)
.env("RUSTUP_TOOLCHAIN", "nightly")
.run()?;
}
println!("All good!");
Ok(())
}
pub fn fuzz_corpus_minify(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("FUZZ-CORPUS-MINIFY");
let _guard = sh.push_dir("./fuzz");
for target in FUZZ_TARGETS {
cmd!(sh, "../{LOCAL_CARGO_ROOT}/bin/cargo-fuzz cmin {target}")
.env("RUSTUP_TOOLCHAIN", "nightly")
.run()?;
}
Ok(())
}
pub fn fuzz_corpus_fetch(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("FUZZ-CORPUS-FETCH");
cmd!(
sh,
"az storage blob download-batch --account-name fuzzingcorpus --source ironrdp --destination fuzz --output none"
)
.run()?;
Ok(())
}
pub fn fuzz_corpus_push(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("FUZZ-CORPUS-PUSH");
cmd!(
sh,
"az storage blob sync --account-name fuzzingcorpus --container ironrdp --source fuzz/corpus --destination corpus --delete-destination true --output none"
)
.run()?;
cmd!(
sh,
"az storage blob sync --account-name fuzzingcorpus --container ironrdp --source fuzz/artifacts --destination artifacts --delete-destination true --output none"
)
.run()?;
Ok(())
}
pub fn fuzz_install(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("FUZZ-INSTALL");
let cargo_fuzz_path: std::path::PathBuf = [LOCAL_CARGO_ROOT, "bin", "cargo-fuzz"].iter().collect();
if !sh.path_exists(cargo_fuzz_path) {
// Install in debug because it's faster to compile and we don't need execution speed anyway.
// cargo-fuzz version is pinned so we dont get different versions without intervention.
cmd!(
sh,
"{CARGO} install --debug --locked --root {LOCAL_CARGO_ROOT} cargo-fuzz@{CARGO_FUZZ_VERSION}"
)
.run()?;
}
cmd!(sh, "rustup install nightly --profile=minimal").run()?;
Ok(())
}
pub fn svelte_run(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("SVELTE-RUN");
{
let _guard = sh.push_dir("./web-client/iron-remote-gui");
cmd!(sh, "npm install").run()?;
}
{
let _guard = sh.push_dir("./web-client/iron-svelte-client");
cmd!(sh, "npm install").run()?;
cmd!(sh, "npm run dev-all").run()?;
}
Ok(())
}
pub fn coverage_install(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("COVERAGE-INSTALL");
cmd!(sh, "rustup install nightly --profile=minimal").run()?;
cmd!(sh, "rustup component add --toolchain nightly llvm-tools-preview").run()?;
cmd!(sh, "rustup component add llvm-tools-preview").run()?;
cmd!(
sh,
"{CARGO} install --debug --locked --root {LOCAL_CARGO_ROOT} cargo-fuzz@{CARGO_FUZZ_VERSION}"
)
.run()?;
cmd!(
sh,
"{CARGO} install --debug --locked --root {LOCAL_CARGO_ROOT} grcov@{GRCOV_VERSION}"
)
.run()?;
Ok(())
}
pub fn coverage_report(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("COVERAGE-REPORT");
println!("Remove leftovers");
sh.remove_path("./fuzz/coverage/")?;
sh.remove_path("./coverage/")?;
sh.create_dir("./coverage/binaries")?;
{
// Fuzz coverage
let _guard = sh.push_dir("./fuzz");
cmd!(sh, "{CARGO} clean").run()?;
for target in FUZZ_TARGETS {
cmd!(sh, "../{LOCAL_CARGO_ROOT}/bin/cargo-fuzz coverage {target}")
.env("RUSTUP_TOOLCHAIN", "nightly")
.run()?;
}
cmd!(sh, "cp -r ./target ../coverage/binaries/").run()?;
}
{
// Test coverage
cmd!(sh, "{CARGO} clean").run()?;
cmd!(sh, "rustup run nightly cargo test --workspace")
.env("CARGO_INCREMENTAL", "0")
.env("RUSTFLAGS", "-C instrument-coverage")
.env("LLVM_PROFILE_FILE", "./coverage/default-%m-%p.profraw")
.run()?;
cmd!(sh, "cp -r ./target/debug ./coverage/binaries/").run()?;
}
sh.create_dir("./docs")?;
cmd!(
sh,
"./{LOCAL_CARGO_ROOT}/bin/grcov . ./fuzz
--source-dir .
--binary-path ./coverage/binaries/
--output-type html
--branch
--ignore-not-existing
--ignore xtask/*
--ignore src/*
--ignore **/tests/*
--ignore crates/*-generators/*
--ignore crates/web/*
--ignore crates/client/*
--ignore crates/glutin-renderer/*
--ignore crates/glutin-client/*
--ignore crates/replay-client/*
--ignore crates/tls/*
--ignore fuzz/fuzz_targets/*
--ignore target/*
--ignore fuzz/target/*
--excl-start begin-no-coverage
--excl-stop end-no-coverage
-o ./docs/coverage"
)
.run()?;
println!("Code coverage report available in `./docs/coverage` folder");
println!("Clean up");
sh.remove_path("./coverage/")?;
sh.remove_path("./fuzz/coverage/")?;
sh.remove_path("./xtask/coverage/")?;
sh.read_dir("./crates")?
.into_iter()
.try_for_each(|crate_path| -> xshell::Result<()> {
for path in sh.read_dir(crate_path)? {
if path.ends_with("coverage") {
sh.remove_path(path)?;
}
}
Ok(())
})?;
Ok(())
}
pub fn clean_workspace(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("CLEAN");
cmd!(sh, "{CARGO} clean").run()?;
Ok(())
}
pub fn wasm_install(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("WASM-INSTALL");
cmd!(sh, "rustup target add wasm32-unknown-unknown").run()?;
Ok(())
}

37
xtask/src/wasm.rs Normal file
View file

@ -0,0 +1,37 @@
use crate::prelude::*;
pub fn check(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("WASM-CHECK");
for package in WASM_PACKAGES {
println!("Check {package}");
cmd!(
sh,
"{CARGO} rustc --locked --target wasm32-unknown-unknown --package {package} --lib --crate-type cdylib"
)
.run()?;
// When building a library, `-` in the artifact name are replaced by `_`
let artifact_name = format!("{}.wasm", package.replace('-', "_"));
let output = cmd!(sh, "wasm2wat ./target/wasm32-unknown-unknown/debug/{artifact_name}").output()?;
let stdout = std::str::from_utf8(&output.stdout).context("wasm2wat output is not valid UTF-8")?;
if stdout.contains("import \"env\"") {
anyhow::bail!("Found undefined symbols in generated wasm file");
}
}
println!("All good!");
Ok(())
}
pub fn install(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("WASM-INSTALL");
cmd!(sh, "rustup target add wasm32-unknown-unknown").run()?;
Ok(())
}

68
xtask/src/web.rs Normal file
View file

@ -0,0 +1,68 @@
use crate::prelude::*;
const IRON_REMOTE_GUI_PREFIX: &str = "./web-client/iron-remote-gui";
const IRON_SVELTE_CLIENT_PREFIX: &str = "./web-client/iron-svelte-client";
pub fn install(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("WEB-INSTALL");
cmd!(sh, "npm install --prefix {IRON_REMOTE_GUI_PREFIX}").run()?;
cmd!(sh, "npm install --prefix {IRON_SVELTE_CLIENT_PREFIX}").run()?;
let wasm_pack_path: std::path::PathBuf = [LOCAL_CARGO_ROOT, "bin", "wasm-pack"].iter().collect();
if !sh.path_exists(wasm_pack_path) {
// Install in debug because it's faster to compile and we don't need execution speed anyway.
// cargo-fuzz version is pinned so we dont get different versions without intervention.
cmd!(
sh,
"{CARGO} install
--debug --locked
--root {LOCAL_CARGO_ROOT}
--no-default-features
--features sys-openssl
wasm-pack@{WASM_PACK_VERSION}"
)
.run()?;
}
Ok(())
}
pub fn check(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("WEB-CHECK");
build(sh, true)?;
Ok(())
}
pub fn run(sh: &Shell) -> anyhow::Result<()> {
let _s = Section::new("WEB-RUN");
build(sh, false)?;
cmd!(sh, "npm run dev-no-wasm --prefix {IRON_SVELTE_CLIENT_PREFIX}").run()?;
Ok(())
}
fn build(sh: &Shell, wasm_pack_dev: bool) -> anyhow::Result<()> {
{
let _guard = sh.push_dir("./crates/ironrdp-web");
if wasm_pack_dev {
cmd!(sh, "../../{LOCAL_CARGO_ROOT}/bin/wasm-pack build --dev --target web").run()?;
} else {
cmd!(sh, "../../{LOCAL_CARGO_ROOT}/bin/wasm-pack build --target web").run()?;
}
}
cmd!(sh, "npm run check --prefix {IRON_REMOTE_GUI_PREFIX}").run()?;
cmd!(sh, "npm run build-alone --prefix {IRON_REMOTE_GUI_PREFIX}").run()?;
// cmd!(sh, "npm run check --prefix {IRON_SVELTE_CLIENT_PREFIX}").run()?; // FIXME: failing on master
cmd!(sh, "npm run build-no-wasm --prefix {IRON_SVELTE_CLIENT_PREFIX}").run()?;
Ok(())
}