diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 2042f924..71b7e1d7 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -61,9 +61,9 @@ jobs: AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }} run: | $version = "$env:GITHUB_SHA".Substring(0, 9) - aws s3 cp .\target\release\moon.exe "s3://${{ secrets.AWS_BUCKET_NAME }}/bleeding-moon/$version/$(uname -s)-$(uname -m)/" - aws s3 cp .\target\release\moonrun.exe "s3://${{ secrets.AWS_BUCKET_NAME }}/bleeding-moonrun/$version/$(uname -s)-$(uname -m)/" - aws s3 cp .\target\release\moonrun.exe "s3://cli.moonbitlang.com/moon-ci/$(uname -s)-$(uname -m)/" + aws s3 cp .\target\release\moon.exe "s3://${{ secrets.AWS_BUCKET_NAME }}/bleeding-moon/$version/Windows-x86_64/" + aws s3 cp .\target\release\moonrun.exe "s3://${{ secrets.AWS_BUCKET_NAME }}/bleeding-moonrun/$version/Windows-x86_64/" + aws s3 cp .\target\release\moonrun.exe "s3://cli.moonbitlang.com/moon-ci/Windows-x86_64/" - name: Bleeding Release (macos-latest) if: ${{ matrix.os == 'macos-latest' }} diff --git a/crates/moon/tests/test_cases/cond_comp.in/moon.test b/crates/moon/tests/test_cases/cond_comp.in/moon.test index fcd18171..f7f67678 100644 --- a/crates/moon/tests/test_cases/cond_comp.in/moon.test +++ b/crates/moon/tests/test_cases/cond_comp.in/moon.test @@ -1,54 +1,54 @@ $ moon build --dry-run --sort-input --nostd - moonc build-package ./src/lib/all.mbt ./src/lib/not_js.mbt -o ./target/wasm-gc/release/build/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target wasm-gc + moonc build-package ./src/lib/all.mbt ./src/lib/js_or_wasm.mbt ./src/lib/not_js.mbt -o ./target/wasm-gc/release/build/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target wasm-gc moonc build-package ./src/main/main.mbt -o ./target/wasm-gc/release/build/main/main.core -pkg username/hello/main -is-main -i ./target/wasm-gc/release/build/lib/lib.mi:lib -pkg-sources username/hello/main:./src/main -target wasm-gc moonc link-core ./target/wasm-gc/release/build/lib/lib.core ./target/wasm-gc/release/build/main/main.core -main username/hello/main -o ./target/wasm-gc/release/build/main/main.wasm -pkg-sources username/hello/lib:./src/lib -pkg-sources username/hello/main:./src/main -target wasm-gc $ moon build --target wasm --dry-run --sort-input --nostd - moonc build-package ./src/lib/all.mbt ./src/lib/not_js.mbt -o ./target/wasm/release/build/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target wasm + moonc build-package ./src/lib/all.mbt ./src/lib/js_or_wasm.mbt ./src/lib/not_js.mbt -o ./target/wasm/release/build/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target wasm moonc build-package ./src/main/main.mbt -o ./target/wasm/release/build/main/main.core -pkg username/hello/main -is-main -i ./target/wasm/release/build/lib/lib.mi:lib -pkg-sources username/hello/main:./src/main -target wasm moonc link-core ./target/wasm/release/build/lib/lib.core ./target/wasm/release/build/main/main.core -main username/hello/main -o ./target/wasm/release/build/main/main.wasm -pkg-sources username/hello/lib:./src/lib -pkg-sources username/hello/main:./src/main -target wasm $ moon build --target wasm-gc --dry-run --sort-input --nostd - moonc build-package ./src/lib/all.mbt ./src/lib/not_js.mbt -o ./target/wasm-gc/release/build/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target wasm-gc + moonc build-package ./src/lib/all.mbt ./src/lib/js_or_wasm.mbt ./src/lib/not_js.mbt -o ./target/wasm-gc/release/build/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target wasm-gc moonc build-package ./src/main/main.mbt -o ./target/wasm-gc/release/build/main/main.core -pkg username/hello/main -is-main -i ./target/wasm-gc/release/build/lib/lib.mi:lib -pkg-sources username/hello/main:./src/main -target wasm-gc moonc link-core ./target/wasm-gc/release/build/lib/lib.core ./target/wasm-gc/release/build/main/main.core -main username/hello/main -o ./target/wasm-gc/release/build/main/main.wasm -pkg-sources username/hello/lib:./src/lib -pkg-sources username/hello/main:./src/main -target wasm-gc $ moon build --dry-run --target js --sort-input --nostd - moonc build-package ./src/lib/all.mbt ./src/lib/js_and_release.mbt ./src/lib/only_js.mbt -o ./target/js/release/build/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target js + moonc build-package ./src/lib/all.mbt ./src/lib/js_and_release.mbt ./src/lib/js_or_wasm.mbt ./src/lib/only_js.mbt -o ./target/js/release/build/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target js moonc build-package ./src/main/main.mbt -o ./target/js/release/build/main/main.core -pkg username/hello/main -is-main -i ./target/js/release/build/lib/lib.mi:lib -pkg-sources username/hello/main:./src/main -target js moonc link-core ./target/js/release/build/lib/lib.core ./target/js/release/build/main/main.core -main username/hello/main -o ./target/js/release/build/main/main.js -pkg-sources username/hello/lib:./src/lib -pkg-sources username/hello/main:./src/main -target js $ moon build --dry-run --target wasm --sort-input --nostd --debug - moonc build-package ./src/lib/all.mbt ./src/lib/not_js.mbt ./src/lib/only_debug.mbt -o ./target/wasm/debug/build/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target wasm -g + moonc build-package ./src/lib/all.mbt ./src/lib/js_or_wasm.mbt ./src/lib/not_js.mbt ./src/lib/only_debug.mbt -o ./target/wasm/debug/build/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target wasm -g moonc build-package ./src/main/main.mbt -o ./target/wasm/debug/build/main/main.core -pkg username/hello/main -is-main -i ./target/wasm/debug/build/lib/lib.mi:lib -pkg-sources username/hello/main:./src/main -target wasm -g moonc link-core ./target/wasm/debug/build/lib/lib.core ./target/wasm/debug/build/main/main.core -main username/hello/main -o ./target/wasm/debug/build/main/main.wasm -pkg-sources username/hello/lib:./src/lib -pkg-sources username/hello/main:./src/main -target wasm -g $ moon build --dry-run --target wasm-gc --sort-input --nostd --debug - moonc build-package ./src/lib/all.mbt ./src/lib/not_js.mbt ./src/lib/only_debug.mbt -o ./target/wasm-gc/debug/build/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target wasm-gc -g -source-map + moonc build-package ./src/lib/all.mbt ./src/lib/js_or_wasm.mbt ./src/lib/not_js.mbt ./src/lib/only_debug.mbt -o ./target/wasm-gc/debug/build/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target wasm-gc -g -source-map moonc build-package ./src/main/main.mbt -o ./target/wasm-gc/debug/build/main/main.core -pkg username/hello/main -is-main -i ./target/wasm-gc/debug/build/lib/lib.mi:lib -pkg-sources username/hello/main:./src/main -target wasm-gc -g -source-map moonc link-core ./target/wasm-gc/debug/build/lib/lib.core ./target/wasm-gc/debug/build/main/main.core -main username/hello/main -o ./target/wasm-gc/debug/build/main/main.wasm -pkg-sources username/hello/lib:./src/lib -pkg-sources username/hello/main:./src/main -target wasm-gc -g -source-map $ moon build --dry-run --target js --sort-input --nostd --debug - moonc build-package ./src/lib/all.mbt ./src/lib/only_debug.mbt ./src/lib/only_js.mbt -o ./target/js/debug/build/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target js -g -source-map + moonc build-package ./src/lib/all.mbt ./src/lib/js_or_wasm.mbt ./src/lib/only_debug.mbt ./src/lib/only_js.mbt -o ./target/js/debug/build/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target js -g -source-map moonc build-package ./src/main/main.mbt -o ./target/js/debug/build/main/main.core -pkg username/hello/main -is-main -i ./target/js/debug/build/lib/lib.mi:lib -pkg-sources username/hello/main:./src/main -target js -g -source-map moonc link-core ./target/js/debug/build/lib/lib.core ./target/js/debug/build/main/main.core -main username/hello/main -o ./target/js/debug/build/main/main.js -pkg-sources username/hello/lib:./src/lib -pkg-sources username/hello/main:./src/main -target js -g -source-map $ moon check --dry-run --target js --sort-input --nostd - moonc check ./src/lib/all.mbt ./src/lib/js_and_release.mbt ./src/lib/only_js.mbt -o ./target/js/release/check/lib/lib.mi -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target js + moonc check ./src/lib/all.mbt ./src/lib/js_and_release.mbt ./src/lib/js_or_wasm.mbt ./src/lib/only_js.mbt -o ./target/js/release/check/lib/lib.mi -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target js moonc check ./src/main/main.mbt -o ./target/js/release/check/main/main.mi -pkg username/hello/main -is-main -i ./target/js/release/check/lib/lib.mi:lib -pkg-sources username/hello/main:./src/main -target js moonc check ./src/lib/hello_test.mbt ./src/lib/js_only_test.mbt -o ./target/js/release/check/lib/lib.blackbox_test.mi -pkg username/hello/lib_blackbox_test -i ./target/js/release/check/lib/lib.mi:lib -pkg-sources username/hello/lib_blackbox_test:./src/lib -target js $ moon test --dry-run --target js --sort-input --nostd moon generate-test-driver --source-dir . --target-dir ./target/js/debug/test --package username/hello/lib --sort-input --target js --driver-kind blackbox - moonc build-package ./src/lib/all.mbt ./src/lib/only_debug.mbt ./src/lib/only_js.mbt -o ./target/js/debug/test/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target js -g + moonc build-package ./src/lib/all.mbt ./src/lib/js_or_wasm.mbt ./src/lib/only_debug.mbt ./src/lib/only_js.mbt -o ./target/js/debug/test/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./src/lib -target js -g moonc build-package ./src/lib/hello_test.mbt ./src/lib/js_only_test.mbt ./target/js/debug/test/lib/__generated_driver_for_blackbox_test.mbt -o ./target/js/debug/test/lib/lib.blackbox_test.core -pkg username/hello/lib_blackbox_test -is-main -i ./target/js/debug/test/lib/lib.mi:lib -pkg-sources username/hello/lib_blackbox_test:./src/lib -target js -g moonc link-core ./target/js/debug/test/lib/lib.core ./target/js/debug/test/lib/lib.blackbox_test.core -main username/hello/lib_blackbox_test -o ./target/js/debug/test/lib/lib.blackbox_test.js -test-mode -pkg-sources username/hello/lib:./src/lib -pkg-sources username/hello/lib_blackbox_test:./src/lib -exported_functions execute -js-format cjs -target js -g moon generate-test-driver --source-dir . --target-dir ./target/js/debug/test --package username/hello/lib --sort-input --target js --driver-kind internal - moonc build-package ./src/lib/all.mbt ./src/lib/only_debug.mbt ./src/lib/only_js.mbt ./target/js/debug/test/lib/__generated_driver_for_internal_test.mbt -o ./target/js/debug/test/lib/lib.internal_test.core -pkg username/hello/lib -is-main -pkg-sources username/hello/lib:./src/lib -target js -g + moonc build-package ./src/lib/all.mbt ./src/lib/js_or_wasm.mbt ./src/lib/only_debug.mbt ./src/lib/only_js.mbt ./target/js/debug/test/lib/__generated_driver_for_internal_test.mbt -o ./target/js/debug/test/lib/lib.internal_test.core -pkg username/hello/lib -is-main -pkg-sources username/hello/lib:./src/lib -target js -g moonc link-core ./target/js/debug/test/lib/lib.internal_test.core -main username/hello/lib -o ./target/js/debug/test/lib/lib.internal_test.js -test-mode -pkg-sources username/hello/lib:./src/lib -exported_functions execute -js-format cjs -target js -g $ moon bundle --dry-run --target js --sort-input --nostd - moonc build-package ./src/lib/all.mbt ./src/lib/js_and_release.mbt ./src/lib/only_js.mbt -o ./target/js/release/bundle/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./lib -target js + moonc build-package ./src/lib/all.mbt ./src/lib/js_and_release.mbt ./src/lib/js_or_wasm.mbt ./src/lib/only_js.mbt -o ./target/js/release/bundle/lib/lib.core -pkg username/hello/lib -pkg-sources username/hello/lib:./lib -target js moonc build-package ./src/main/main.mbt -o ./target/js/release/bundle/main/main.core -pkg username/hello/main -is-main -i ./target/js/release/bundle/lib/lib.mi:lib -pkg-sources username/hello/main:./main -target js moonc bundle-core ./target/js/release/bundle/lib/lib.core ./target/js/release/bundle/main/main.core -o ./target/js/release/bundle/hello.core diff --git a/crates/moon/tests/test_cases/cond_comp.in/src/lib/js_or_wasm.mbt b/crates/moon/tests/test_cases/cond_comp.in/src/lib/js_or_wasm.mbt new file mode 100644 index 00000000..e69de29b diff --git a/crates/moon/tests/test_cases/cond_comp.in/src/lib/moon.pkg.json b/crates/moon/tests/test_cases/cond_comp.in/src/lib/moon.pkg.json index 6a8ede18..78712aa7 100644 --- a/crates/moon/tests/test_cases/cond_comp.in/src/lib/moon.pkg.json +++ b/crates/moon/tests/test_cases/cond_comp.in/src/lib/moon.pkg.json @@ -1,9 +1,10 @@ { "targets": { - "only_js.mbt": ["and", "js"], + "only_js.mbt": ["js"], "not_js.mbt": ["not", "js"], - "only_debug.mbt": ["and", "debug"], - "js_and_release.mbt": ["and", ["or", "js"], ["or", "release"]], - "js_only_test.mbt": ["and", "js"] + "only_debug.mbt": ["debug"], + "js_and_release.mbt": ["and", ["js"], ["release"]], + "js_only_test.mbt": ["js"], + "js_and_wasm.mbt": ["js", "wasm"] } } \ No newline at end of file diff --git a/crates/moon/tests/test_cases/mod.rs b/crates/moon/tests/test_cases/mod.rs index cc047c39..0dcf8b81 100644 --- a/crates/moon/tests/test_cases/mod.rs +++ b/crates/moon/tests/test_cases/mod.rs @@ -6215,3 +6215,57 @@ fn test_failed_to_fill_whole_buffer() { "#]], ); } + +#[test] +fn test_moon_update_failed() { + if std::env::var("CI").is_err() { + return; + } + let tmp = tempfile::tempdir().unwrap(); + let dir = tmp.path(); + let moon_home = dir; + let out = std::process::Command::new(moon_bin()) + .current_dir(dir) + .env("MOON_HOME", moon_home) + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()) + .args(["update"]) + .output() + .unwrap(); + let out = String::from_utf8(out.stderr).unwrap(); + check( + &out, + expect![[r#" + Registry index cloned successfully + "#]], + ); + + let _ = std::process::Command::new("git") + .args([ + "-C", + dir.join("registry").join("index").to_str().unwrap(), + "remote", + "set-url", + "origin", + "whatever", + ]) + .output() + .unwrap(); + + let out = std::process::Command::new(moon_bin()) + .current_dir(dir) + .env("MOON_HOME", moon_home) + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()) + .args(["update"]) + .output() + .unwrap(); + let out = String::from_utf8(out.stderr).unwrap(); + check( + &out, + expect![[r#" + Registry index is not cloned from the same URL, re-cloning + Registry index re-cloned successfully + "#]], + ); +} diff --git a/crates/moonbuild/src/new.rs b/crates/moonbuild/src/new.rs index d744a5da..2df092b2 100644 --- a/crates/moonbuild/src/new.rs +++ b/crates/moonbuild/src/new.rs @@ -141,8 +141,8 @@ fn common( ) -> anyhow::Result { std::fs::create_dir_all(target_dir).context("failed to create target directory")?; - if !is_in_git_repo(target_dir) { - git_init_repo(target_dir); + if !is_in_git_repo(target_dir)? { + git_init_repo(target_dir)?; } { diff --git a/crates/mooncake/src/update.rs b/crates/mooncake/src/update.rs index 21188cb3..4743f0e8 100644 --- a/crates/mooncake/src/update.rs +++ b/crates/mooncake/src/update.rs @@ -18,97 +18,204 @@ use std::path::Path; -use anyhow::bail; use colored::Colorize; -use moonutil::mooncakes::RegistryConfig; +use moonutil::{ + git::{GitCommandError, Stdios}, + mooncakes::RegistryConfig, +}; + +#[derive(Debug, thiserror::Error)] +#[error("failed to clone registry index")] +struct CloneRegistryIndexError { + #[source] + source: CloneRegistryIndexErrorKind, +} + +#[derive(Debug, thiserror::Error)] +enum CloneRegistryIndexErrorKind { + #[error(transparent)] + GitCommandError(#[from] GitCommandError), + + #[error(transparent)] + IO(#[from] std::io::Error), + + #[error("non-zero exit code: {0}")] + NonZeroExitCode(std::process::ExitStatus), +} fn clone_registry_index( registry_config: &RegistryConfig, target_dir: &Path, -) -> anyhow::Result { - let output = std::process::Command::new("git") - .arg("clone") - .arg(®istry_config.index) - .arg(target_dir) - .spawn()? - .wait(); - match output { - Ok(status) => { - if !status.success() { - bail!("Failed to clone registry index"); - } - Ok(0) - } - Err(e) => { - eprintln!("Failed to clone registry index: {}", e); - bail!("Failed to clone registry index"); - } +) -> Result<(), CloneRegistryIndexError> { + let mut child = moonutil::git::git_command( + &[ + "clone", + ®istry_config.index, + target_dir.to_str().unwrap(), + ], + Stdios::npp(), + ) + .map_err(|e| CloneRegistryIndexError { + source: CloneRegistryIndexErrorKind::GitCommandError(e), + })?; + + let status = child.wait().map_err(|e| CloneRegistryIndexError { + source: CloneRegistryIndexErrorKind::IO(e), + })?; + if !status.success() { + return Err(CloneRegistryIndexError { + source: CloneRegistryIndexErrorKind::NonZeroExitCode(status), + }); } + Ok(()) +} + +#[derive(Debug, thiserror::Error)] +#[error("failed to pull latest registry index")] +struct PullLatestRegistryIndexError { + #[source] + source: PullLatestRegistryIndexErrorKind, +} + +#[derive(Debug, thiserror::Error)] +enum PullLatestRegistryIndexErrorKind { + #[error(transparent)] + GitCommandError(GitCommandError), + + #[error(transparent)] + IO(#[from] std::io::Error), + + #[error("non-zero exit code: {0}")] + NonZeroExitCode(std::process::ExitStatus), } fn pull_latest_registry_index( _registry_config: &RegistryConfig, target_dir: &Path, -) -> anyhow::Result { - let output = std::process::Command::new("git") - .arg("pull") - .arg("origin") - .arg("main") - .current_dir(target_dir) - .spawn()? - .wait()?; - - match output.code() { - Some(code) => { - if code != 0 { - bail!("Failed to pull registry index"); - } - Ok(0) - } - None => { - eprintln!("Failed to pull registry index"); - bail!("Failed to pull registry index"); - } +) -> Result<(), PullLatestRegistryIndexError> { + let mut child = moonutil::git::git_command( + &["-C", target_dir.to_str().unwrap(), "pull", "origin", "main"], + Stdios::npp(), + ) + .map_err(|e| PullLatestRegistryIndexError { + source: PullLatestRegistryIndexErrorKind::GitCommandError(e), + })?; + let status = child.wait().map_err(|e| PullLatestRegistryIndexError { + source: PullLatestRegistryIndexErrorKind::IO(e), + })?; + if !status.success() { + return Err(PullLatestRegistryIndexError { + source: PullLatestRegistryIndexErrorKind::NonZeroExitCode(status), + }); } + Ok(()) +} + +#[derive(Debug, thiserror::Error)] +#[error("update failed")] +struct UpdateError { + #[source] + source: UpdateErrorKind, +} + +#[derive(Debug, thiserror::Error)] +enum UpdateErrorKind { + #[error(transparent)] + CloneRegistryIndexError(#[from] CloneRegistryIndexError), + + #[error(transparent)] + PullLatestRegistryIndexError(#[from] PullLatestRegistryIndexError), + + #[error(transparent)] + GetRemoteUrlError(#[from] GetRemoteUrlError), + + #[error(transparent)] + IO(#[from] std::io::Error), +} + +#[derive(Debug, thiserror::Error)] +#[error("failed to get remote url")] +struct GetRemoteUrlError { + #[source] + source: GetRemoteUrlErrorKind, +} + +#[derive(Debug, thiserror::Error)] +enum GetRemoteUrlErrorKind { + #[error(transparent)] + GitCommandError(#[from] GitCommandError), + + #[error(transparent)] + IO(#[from] std::io::Error), +} + +fn get_remote_url(target_dir: &Path) -> Result { + let output = moonutil::git::git_command( + &[ + "-C", + target_dir.to_str().unwrap(), + "remote", + "get-url", + "origin", + ], + Stdios::npp(), + ) + .map_err(|e| GetRemoteUrlError { + source: GetRemoteUrlErrorKind::GitCommandError(e), + })? + .wait_with_output() + .map_err(|e| GetRemoteUrlError { + source: GetRemoteUrlErrorKind::IO(e), + })?; + let url = String::from_utf8_lossy(&output.stdout).trim().to_string(); + Ok(url) } pub fn update(target_dir: &Path, registry_config: &RegistryConfig) -> anyhow::Result { if target_dir.exists() { - let output = std::process::Command::new("git") - .arg("remote") - .arg("get-url") - .arg("origin") - .current_dir(target_dir) - .output()?; - - let url = String::from_utf8_lossy(&output.stdout).trim().to_string(); + let url = get_remote_url(target_dir).map_err(|e| UpdateError { + source: UpdateErrorKind::GetRemoteUrlError(e), + })?; if url == registry_config.index { let result = pull_latest_registry_index(registry_config, target_dir); - if result.is_err() { - eprintln!( - "Failed to update registry, {}", - "re-cloning".bold().yellow() - ); - std::fs::remove_dir_all(target_dir)?; - clone_registry_index(registry_config, target_dir)?; - eprintln!("{}", "Registry index re-cloned successfully".bold().green()); - Ok(0) - } else { - eprintln!("{}", "Registry index updated successfully".bold().green()); - Ok(0) + match result { + Err(_) => { + eprintln!( + "failed to update registry, {}", + "re-cloning".bold().yellow() + ); + std::fs::remove_dir_all(target_dir).map_err(|e| UpdateError { + source: UpdateErrorKind::IO(e), + })?; + clone_registry_index(registry_config, target_dir).map_err(|e| UpdateError { + source: UpdateErrorKind::CloneRegistryIndexError(e), + })?; + eprintln!("{}", "Registry index re-cloned successfully".bold().green()); + Ok(0) + } + Ok(()) => { + eprintln!("{}", "Registry index updated successfully".bold().green()); + Ok(0) + } } } else { eprintln!( "Registry index is not cloned from the same URL, {}", "re-cloning".yellow().bold() ); - std::fs::remove_dir_all(target_dir)?; - clone_registry_index(registry_config, target_dir)?; + std::fs::remove_dir_all(target_dir).map_err(|e| UpdateError { + source: UpdateErrorKind::IO(e), + })?; + clone_registry_index(registry_config, target_dir).map_err(|e| UpdateError { + source: UpdateErrorKind::CloneRegistryIndexError(e), + })?; eprintln!("{}", "Registry index re-cloned successfully".bold().green()); Ok(0) } } else { - clone_registry_index(registry_config, target_dir)?; + clone_registry_index(registry_config, target_dir).map_err(|e| UpdateError { + source: UpdateErrorKind::CloneRegistryIndexError(e), + })?; eprintln!("{}", "Registry index cloned successfully".bold().green()); Ok(0) } diff --git a/crates/moonutil/src/cond_expr.rs b/crates/moonutil/src/cond_expr.rs index e5a61811..c08dd72f 100644 --- a/crates/moonutil/src/cond_expr.rs +++ b/crates/moonutil/src/cond_expr.rs @@ -228,11 +228,26 @@ fn parse_cond_expr(file: &Path, value: &StringOrArray) -> Result, ParseCondExprError> = - iter.map(|x| parse_cond_expr(file, x)).collect(); - Ok(CondExpr::Condition(logic_op, sub_exprs?)) + match logic_op { + Ok(logic_op) => { + let sub_exprs: Result, ParseCondExprError> = + iter.map(|x| parse_cond_expr(file, x)).collect(); + Ok(CondExpr::Condition(logic_op, sub_exprs?)) + } + Err(_) => { + let atom = parse_cond_target(op).map_err(|e| ParseCondExprError { + file: file.to_path_buf(), + source: ParseCondExprErrorKind::ParseCondAtomError(e), + })?; + let sub_exprs: Result, ParseCondExprError> = + iter.map(|x| parse_cond_expr(file, x)).collect(); + let mut sub_exprs = sub_exprs?; + sub_exprs.insert(0, atom); + Ok(CondExpr::Condition(LogicOp::Or, sub_exprs)) + } + } } _ => Err(ParseCondExprError { file: file.to_path_buf(), diff --git a/crates/moonutil/src/git.rs b/crates/moonutil/src/git.rs index eed0a69e..02024669 100644 --- a/crates/moonutil/src/git.rs +++ b/crates/moonutil/src/git.rs @@ -18,33 +18,128 @@ use std::path::Path; -use colored::Colorize; - -pub fn is_in_git_repo(path: &Path) -> bool { - let output = std::process::Command::new("git") - .args(["rev-parse", "--is-inside-work-tree"]) - .stdout(std::process::Stdio::null()) - .stderr(std::process::Stdio::null()) - .current_dir(path) - .status(); - match output { - Ok(out) => out.success(), - _ => false, +#[derive(Debug, thiserror::Error)] +#[error("git command failed: `{cmd}`")] +pub struct GitCommandError { + cmd: String, + + #[source] + source: GitCommandErrorKind, +} + +#[derive(Debug, thiserror::Error)] +pub enum GitCommandErrorKind { + #[error(transparent)] + IO(#[from] std::io::Error), + + #[error("non-zero exit code: {0}")] + ExitStatus(i32), + + #[error("unknown exit code")] + UnknownExitCode, +} + +pub struct Stdios { + stdin: std::process::Stdio, + stdout: std::process::Stdio, + stderr: std::process::Stdio, +} + +impl Stdios { + pub fn inherit() -> Self { + Self { + stdin: std::process::Stdio::inherit(), + stdout: std::process::Stdio::inherit(), + stderr: std::process::Stdio::inherit(), + } + } + + pub fn npp() -> Self { + Self { + stdin: std::process::Stdio::null(), + stdout: std::process::Stdio::piped(), + stderr: std::process::Stdio::piped(), + } + } +} +pub fn git_command(args: &[&str], stdios: Stdios) -> Result { + std::process::Command::new("git") + .args(args) + .stdin(stdios.stdin) + .stdout(stdios.stdout) + .stderr(stdios.stderr) + .spawn() + .map_err(|e| GitCommandError { + cmd: format!("git {}", args.join(" ")), + source: GitCommandErrorKind::IO(e), + }) +} + +pub fn is_in_git_repo(path: &Path) -> Result { + let args = [ + "-C", + path.to_str().unwrap(), + "rev-parse", + "--is-inside-work-tree", + ]; + let mut output = git_command(&args, Stdios::npp())?; + let status = output.wait(); + match status { + Ok(status) => Ok(status.success()), + Err(e) => Err(GitCommandError { + cmd: format!("git {}", args.join(" ")), + source: GitCommandErrorKind::IO(e), + }), } } -pub fn git_init_repo(path: &Path) { - let git_init = std::process::Command::new("git") - .arg("init") - .current_dir(path) - .status(); - match git_init { - Ok(o) => if o.success() {}, - _ => { - eprintln!( - "{}: git init failed, make sure you have git in PATH", - "Warning".yellow().bold() - ); +pub fn git_init_repo(path: &Path) -> Result<(), GitCommandError> { + let args = ["-C", path.to_str().unwrap(), "init"]; + let mut git_init = git_command(&args, Stdios::inherit())?; + let status = git_init.wait().map_err(|e| GitCommandError { + cmd: format!("git {}", args.join(" ")), + source: GitCommandErrorKind::IO(e), + })?; + if !status.success() { + match status.code() { + Some(code) => { + return Err(GitCommandError { + cmd: format!("git {}", args.join(" ")), + source: GitCommandErrorKind::ExitStatus(code), + }); + } + None => { + return Err(GitCommandError { + cmd: format!("git {}", args.join(" ")), + source: GitCommandErrorKind::UnknownExitCode, + }) + } } } + Ok(()) +} + +#[test] +fn test_bad_git_command() { + pub fn fake_git_command( + args: &[&str], + stdios: Stdios, + ) -> Result { + std::process::Command::new("fake_git") + .args(args) + .stdin(stdios.stdin) + .stdout(stdios.stdout) + .stderr(stdios.stderr) + .spawn() + .map_err(|e| GitCommandError { + cmd: format!("git {}", args.join(" ")), + source: GitCommandErrorKind::IO(e), + }) + } + let child = fake_git_command(&["pull"], Stdios::inherit()); + let e = child.unwrap_err(); + let err = anyhow::anyhow!(e); + let err_msg = format!("{:?}", err); + assert!(err_msg.contains("git command failed: `git pull`")); + assert!(err_msg.contains("Caused by:")); }