diff --git a/.github/workflows/jobs.yml b/.github/workflows/jobs.yml new file mode 100644 index 0000000..fd8e599 --- /dev/null +++ b/.github/workflows/jobs.yml @@ -0,0 +1,56 @@ +name: CI + +on: + push: + branches: main + pull_request: + branches: main + +env: + CARGO_TERM_COLOR: always + +jobs: + test: + strategy: + fail-fast: false + matrix: + os: + - macos-latest + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@1.80.0 + with: + components: rustfmt, clippy + + - name: Cargo cache + uses: actions/cache@v4 + with: + key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }} + path: ~/.cargo/registry + + - name: install MoonBit + run: | + curl -fsSLv https://cli.moonbitlang.com/install/unix.sh | bash -s + echo "$HOME/.moon/bin" >> $GITHUB_PATH + - name: Version + run: moon version --all + - name: Build + run: | + cargo build --release + ./target/release/moon_dashboard --file repos.txt + + - name: Install duckdb + run: brew install duckdb + - name: Query + run: duckdb -c ".read query.sql" + + - name: Commit + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add data.csv + git commit -m "Update data.csv" || echo "No changes to commit" + + - name: Push changes + run: git push diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..626090e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,432 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "argh" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7af5ba06967ff7214ce4c7419c7d185be7ecd6cc4965a8f6e1d8ce0398aad219" +dependencies = [ + "argh_derive", + "argh_shared", +] + +[[package]] +name = "argh_derive" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56df0aeedf6b7a2fc67d06db35b09684c3e8da0c95f8f27685cb17e08413d87a" +dependencies = [ + "argh_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "argh_shared" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5693f39141bda5760ecc4111ab08da40565d1771038c4a0250f03457ec707531" +dependencies = [ + "serde", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cc" +version = "1.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "moon_dashboard" +version = "0.1.0" +dependencies = [ + "anyhow", + "argh", + "chrono", + "tempfile", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "serde" +version = "1.0.209" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.209" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9ee4605 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "moon_dashboard" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.86" +argh = "0.1.12" +chrono = "0.4.38" +tempfile = "3.12.0" diff --git a/data.csv b/data.csv new file mode 100644 index 0000000..e69de29 diff --git a/query.sql b/query.sql new file mode 100644 index 0000000..98c06d8 --- /dev/null +++ b/query.sql @@ -0,0 +1,12 @@ +CREATE TABLE statistics ( + repo TEXT NOT NULL, + rev TEXT NOT NULL, + command TEXT NOT NULL, + moon_version TEXT NOT NULL, + moonc_version TEXT NOT NULL, + elapsed INTERVAL, + start_time TIMESTAMP_MS +); + +COPY statistics FROM 'data.csv' (HEADER 0, DELIMITER ','); +select * from statistics; diff --git a/repos.txt b/repos.txt new file mode 100644 index 0000000..77c0604 --- /dev/null +++ b/repos.txt @@ -0,0 +1,3 @@ +https://github.com/moonbitlang/core +https://github.com/moonbitlang/x +https://github.com/yj-qin/regexp diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..40a707b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,252 @@ +use std::{ + io::Write, + path::{Path, PathBuf}, + time::{Duration, Instant}, +}; + +use argh::FromArgs; +use chrono::{FixedOffset, Local}; + +#[derive(FromArgs)] +#[argh(description = "...")] +pub struct Stat { + #[argh(option, description = "specify a repo")] + repo_url: Option, + + #[argh(option, short = 'f', description = "read repos from file")] + file: Option, +} + +#[derive(Debug)] +struct Statistics { + repo: String, + rev: String, + command: MoonCommand, + moon_version: String, + moonc_version: String, + elapsed: Option, // None for failed cases + start_time: String, +} + +impl Statistics { + fn to_csv(&self, sep: &str) -> String { + let command = format!("{:?}", self.command); + let elapsed = self + .elapsed + .map_or("".to_string(), |e| format!("{} MILLISECONDS", e)); + let line: [&str; 7] = [ + &self.repo, + &self.rev, + &command, + &self.moon_version, + &self.moonc_version, + &elapsed, + &self.start_time, + ]; + line.join(sep) + } +} + +fn run_moon(workdir: &Path, args: &[&str]) -> anyhow::Result { + let start = Instant::now(); + let mut cmd = std::process::Command::new("moon") + .current_dir(workdir) + .args(args) + .spawn() + .expect("failed to execute process"); + let exit = cmd.wait()?; + if !exit.success() { + return Err(anyhow::anyhow!("failed to execute")); + } + let elapsed = start.elapsed(); + println!( + "moon {}, elapsed: {}ms", + args.join(" "), + elapsed.as_millis() + ); + Ok(elapsed) +} + +#[derive(Debug, Clone, Copy)] +enum MoonCommand { + Check, + Build, + Test, + Bundle, +} + +impl MoonCommand { + fn args(&self) -> Vec<&str> { + match self { + MoonCommand::Check => vec!["check", "-q"], + MoonCommand::Build => vec!["build", "-q"], + MoonCommand::Test => vec!["test", "-q", "--build-only"], + MoonCommand::Bundle => vec!["bundle", "-q"], + } + } +} + +fn stat_moon( + workdir: &Path, + repo: &str, + rev: &str, + moon_version: &str, + moonc_version: &str, + cmd: MoonCommand, +) -> anyhow::Result> { + let mut ss = vec![]; + let mut durations: Vec> = vec![]; + for _ in 0..5 { + let _ = run_moon(workdir, &["clean"]); + let d = run_moon(workdir, &cmd.args()).ok(); + durations.push(d); + let start_time = Local::now() + .with_timezone(&FixedOffset::east_opt(8 * 3600).unwrap()) + .format("%Y-%m-%d %H:%M:%S.%3f") + .to_string(); + let elapsed = d.map(|d| d.as_millis() as u64); + let stat = Statistics { + repo: repo.to_string(), + rev: rev.to_string(), + command: cmd, + moon_version: moon_version.to_string(), + moonc_version: moonc_version.to_string(), + elapsed, + start_time, + }; + ss.push(stat); + } + Ok(ss) +} + +pub fn moon_version() -> anyhow::Result { + let output = std::process::Command::new("moon") + .args(["version"]) + .output()?; + let version = String::from_utf8(output.stdout)?; + Ok(version.trim().to_string()) +} + +pub fn moonc_version() -> anyhow::Result { + let output = std::process::Command::new("moonc").args(["-v"]).output()?; + let version = String::from_utf8(output.stdout)?; + Ok(version.trim().to_string()) +} + +pub fn get_branch_name(workdir: &Path) -> anyhow::Result { + let output = std::process::Command::new("git") + .current_dir(workdir) + .args(["rev-parse", "--abbrev-ref", "HEAD"]) + .output()?; + let branch_name = String::from_utf8(output.stdout)?.trim().to_string(); + Ok(branch_name) +} + +pub fn get_git_short_hash(workdir: &Path) -> anyhow::Result { + let output = std::process::Command::new("git") + .current_dir(workdir) + .args(["rev-parse", "--short", "HEAD"]) + .output()?; + let hash = String::from_utf8(output.stdout)?.trim().to_string(); + Ok(hash) +} + +pub fn git_clone_to(repo: &str, workdir: &Path, dst: &str) -> anyhow::Result<()> { + let mut cmd = std::process::Command::new("git") + .current_dir(workdir) + .args(["clone", repo, dst, "--depth", "1"]) + .spawn()?; + cmd.wait()?; + Ok(()) +} + +pub fn run(repo: &str) -> anyhow::Result<()> { + let tmp = tempfile::tempdir()?; + git_clone_to(repo, tmp.path(), "test")?; + + let workdir = tmp.path().join("test"); + let moon_version = moon_version()?; + let moonc_version = moonc_version()?; + let rev = get_git_short_hash(&workdir)?; + + let mut logs = vec![]; + + logs.extend(stat_moon( + &workdir, + repo, + &rev, + &moon_version, + &moonc_version, + MoonCommand::Check, + )?); + if repo != "https://github.com/moonbitlang/core" { + logs.extend(stat_moon( + &workdir, + repo, + &rev, + &moon_version, + &moonc_version, + MoonCommand::Build, + )?); + } + logs.extend(stat_moon( + &workdir, + repo, + &rev, + &moon_version, + &moonc_version, + MoonCommand::Test, + )?); + if repo == "https://github.com/moonbitlang/core" { + logs.extend(stat_moon( + &workdir, + repo, + &rev, + &moon_version, + &moonc_version, + MoonCommand::Bundle, + )?); + } + + let fp = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("data.csv")?; + let mut writer = std::io::BufWriter::new(fp); + for log in logs { + writeln!(writer, "{}", log.to_csv(","))?; + } + writer.flush()?; + Ok(()) +} + +fn main() -> anyhow::Result<()> { + let args: Stat = argh::from_env(); + // let result = run(&args.repo); + + let mut repo_list = vec![]; + if let Some(r) = args.repo_url { + repo_list.push(r); + } + + if let Some(file) = &args.file { + let content = std::fs::read_to_string(file)?; + for line in content.lines() { + let repo = line.trim(); + repo_list.push(repo.into()); + } + } + + let mut results = vec![]; + for r in repo_list { + results.push(run(&r)); + } + + for result in results { + match result { + Ok(()) => (), + Err(e) => eprintln!("Error processing repository: {}", e), + } + } + Ok(()) +}