1#[cfg(test)]
2mod tidy;
3
4use std::time::Instant;
5
6use xshell::{cmd, Shell};
7
8const MSRV: &str = "1.56.0";
9
10fn main() -> xshell::Result<()> {
11    let sh = Shell::new()?;
12
13    let _e = push_toolchain(&sh, "stable")?;
14    let _e = sh.push_env("CARGO", "");
15
16    {
17        let _s = section("BUILD");
18        cmd!(sh, "cargo test --workspace --no-run").run()?;
19    }
20
21    {
22        let _s = section("TEST");
23
24        for &release in &[None, Some("--release")] {
25            cmd!(sh, "cargo test --features unstable {release...}").run()?;
26            cmd!(
27                sh,
28                "cargo test --no-default-features --features unstable,std,parking_lot {release...}"
29            )
30            .run()?;
31        }
32
33        // Skip doctests for no_std tests as those don't work
34        cmd!(sh, "cargo test --no-default-features --features unstable --test it").run()?;
35        cmd!(sh, "cargo test --no-default-features --features unstable,alloc --test it").run()?;
36
37        cmd!(sh, "cargo test --no-default-features --features critical-section").run()?;
38        cmd!(sh, "cargo test --features critical-section").run()?;
39    }
40
41    {
42        let _s = section("TEST_BETA");
43        let _e = push_toolchain(&sh, "beta")?;
44        cmd!(sh, "cargo test --features unstable").run()?;
45    }
46
47    {
48        let _s = section("TEST_MSRV");
49        let _e = push_toolchain(&sh, MSRV)?;
50        sh.copy_file("Cargo.lock.msrv", "Cargo.lock")?;
51        cmd!(sh, "cargo build").run()?;
52    }
53
54    {
55        let _s = section("TEST_MIRI");
56        let miri_nightly= cmd!(sh, "curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri").read()?;
57        let _e = push_toolchain(&sh, &format!("nightly-{}", miri_nightly))?;
58
59        sh.remove_path("./target")?;
60
61        cmd!(sh, "rustup component add miri").run()?;
62        cmd!(sh, "cargo miri setup").run()?;
63        cmd!(sh, "cargo miri test --features unstable").run()?;
64    }
65
66    {
67        let _s = section("PUBLISH");
68
69        let version = cmd!(sh, "cargo pkgid").read()?.rsplit_once('#').unwrap().1.to_string();
70        let tag = format!("v{version}");
71
72        let current_branch = cmd!(sh, "git branch --show-current").read()?;
73        let has_tag = cmd!(sh, "git tag --list").read()?.lines().any(|it| it.trim() == tag);
74        let dry_run = sh.var("CI").is_err() || has_tag || current_branch != "master";
75        eprintln!("Publishing{}!", if dry_run { " (dry run)" } else { "" });
76
77        let dry_run_arg = if dry_run { Some("--dry-run") } else { None };
78        cmd!(sh, "cargo publish {dry_run_arg...}").run()?;
79        if dry_run {
80            eprintln!("{}", cmd!(sh, "git tag {tag}"));
81            eprintln!("{}", cmd!(sh, "git push --tags"));
82        } else {
83            cmd!(sh, "git tag {tag}").run()?;
84            cmd!(sh, "git push --tags").run()?;
85        }
86    }
87    Ok(())
88}
89
90fn push_toolchain<'a>(
91    sh: &'a xshell::Shell,
92    toolchain: &str,
93) -> xshell::Result<xshell::PushEnv<'a>> {
94    cmd!(sh, "rustup toolchain install {toolchain} --no-self-update").run()?;
95    let res = sh.push_env("RUSTUP_TOOLCHAIN", toolchain);
96    cmd!(sh, "rustc --version").run()?;
97    Ok(res)
98}
99
100fn section(name: &'static str) -> impl Drop {
101    println!("::group::{name}");
102    let start = Instant::now();
103    defer(move || {
104        let elapsed = start.elapsed();
105        eprintln!("{name}: {elapsed:.2?}");
106        println!("::endgroup::");
107    })
108}
109
110fn defer<F: FnOnce()>(f: F) -> impl Drop {
111    struct D<F: FnOnce()>(Option<F>);
112    impl<F: FnOnce()> Drop for D<F> {
113        fn drop(&mut self) {
114            if let Some(f) = self.0.take() {
115                f()
116            }
117        }
118    }
119    D(Some(f))
120}
121