From 50a8d7c2d790ce7a9e61608b416a8e34b186e6e6 Mon Sep 17 00:00:00 2001 From: Alex Kladov Date: Mon, 23 Dec 2024 13:15:20 +0000 Subject: [PATCH] v0.3.0-pre.1 --- .github/workflows/ci.yaml | 2 +- CHANGELOG.md | 18 +++++++++++++++++- Cargo.toml | 6 +++--- README.md | 10 +++++----- examples/ci.rs | 2 +- examples/clone_and_publish.rs | 2 +- src/lib.rs | 27 +++++++++++---------------- tests/it/env.rs | 8 ++++---- tests/it/main.rs | 30 +++++++++++++++--------------- xshell-macros/Cargo.toml | 4 ++-- 10 files changed, 60 insertions(+), 49 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bfc272f..c337881 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 - - run: rustup default 1.63.0 # Check _only_ MSRV for simplicity. + - run: rustup default 1.73.0 # Check _only_ MSRV for simplicity. - run: rustup component add rustfmt - run: cargo run --example ci diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b257dc..107c121 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,22 @@ # Changelog -## Unreleased +## 0.3.0-pre.1 + +A major release with significant changes to the API: + +- Interior mutability and references are removed. Both `Cmd` and `Shell` are now values. +- `pushd`-style API are removed in favor of `Shell`-returning functional builders like + `with_current_dir`. +- Split `copy_file` into `copy_file` and `copy_file_to_dir`, removing auto-magical directory + detection. +- Remove fine-grained control of stdin streams from `Cmd`. Instead: + - There's a menu of `run`, `run_echo`, `run_interactive`, `read` that generally try to do the + right thing. + - The error messages now carry last 128KiB of stdout/stderr and print them on error. + - There's `to_command` method for converting to `std::process::Command` which does allow for fine + grained control. +- Support for timeouts. +- MSRV is raised to 1.73.0. ## 0.2.7 diff --git a/Cargo.toml b/Cargo.toml index b58fff1..7740c76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,19 +2,19 @@ name = "xshell" description = "Utilities for quick shell scripting in Rust" categories = ["development-tools::build-utils", "filesystem"] -version = "0.2.7" # also update xshell-macros/Cargo.toml and CHANGELOG.md +version = "0.3.0-pre.1" # also update xshell-macros/Cargo.toml and CHANGELOG.md license = "MIT OR Apache-2.0" repository = "https://github.com/matklad/xshell" authors = ["Alex Kladov "] edition = "2021" -rust-version = "1.63" +rust-version = "1.73" exclude = [".github/", "bors.toml", "rustfmt.toml", "cbench", "mock_bin/"] [workspace] [dependencies] -xshell-macros = { version = "=0.2.7", path = "./xshell-macros" } +xshell-macros = { version = "=0.3.0-pre.1", path = "./xshell-macros" } [dev-dependencies] anyhow = "1.0.56" diff --git a/README.md b/README.md index aec78e6..693cbb5 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,11 @@ fn main() -> anyhow::Result<()> { let user = "matklad"; let repo = "xshell"; - cmd!(sh, "git clone https://github.com/{user}/{repo}.git").run()?; + cmd!(sh, "git clone https://github.com/{user}/{repo}.git").run_echo()?; sh.set_current_dir(repo); let test_args = ["-Zunstable-options", "--report-time"]; - cmd!(sh, "cargo test -- {test_args...}").run()?; + cmd!(sh, "cargo test -- {test_args...}").run_echo()?; let manifest = sh.read_file("Cargo.toml")?; let version = manifest @@ -27,10 +27,10 @@ fn main() -> anyhow::Result<()> { .map(|it| it.0) .ok_or_else(|| anyhow::format_err!("can't find version field in the manifest"))?; - cmd!(sh, "git tag {version}").run()?; + cmd!(sh, "git tag {version}").run_echo()?; - let dry_run = if sh.env_var("CI").is_ok() { None } else { Some("--dry-run") }; - cmd!(sh, "cargo publish {dry_run...}").run()?; + let dry_run = if sh.var("CI").is_ok() { None } else { Some("--dry-run") }; + cmd!(sh, "cargo publish {dry_run...}").run_echo()?; Ok(()) } diff --git a/examples/ci.rs b/examples/ci.rs index ac8b696..d56541e 100644 --- a/examples/ci.rs +++ b/examples/ci.rs @@ -57,7 +57,7 @@ fn publish(sh: &Shell) -> Result<()> { if current_branch == "master" && !tag_exists { // Could also just use `CARGO_REGISTRY_TOKEN` environmental variable. - let token = sh.env_var("CRATES_IO_TOKEN").unwrap_or("DUMMY_TOKEN".to_string()); + let token = sh.var("CRATES_IO_TOKEN").unwrap_or("DUMMY_TOKEN".to_string()); cmd!(sh, "git tag v{version}").run_echo()?; cmd!(sh, "cargo publish --token {token} --package xshell-macros").run_echo()?; cmd!(sh, "cargo publish --token {token} --package xshell").run_echo()?; diff --git a/examples/clone_and_publish.rs b/examples/clone_and_publish.rs index 7e971db..6803b7d 100644 --- a/examples/clone_and_publish.rs +++ b/examples/clone_and_publish.rs @@ -21,7 +21,7 @@ fn main() -> anyhow::Result<()> { cmd!(sh, "git tag {version}").run()?; - let dry_run = if sh.env_var("CI").is_ok() { None } else { Some("--dry-run") }; + let dry_run = if sh.var("CI").is_ok() { None } else { Some("--dry-run") }; cmd!(sh, "cargo publish {dry_run...}").run()?; Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 5794dec..c1fd6ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -173,7 +173,7 @@ //! //! ```no_run //! # use xshell::{Shell, cmd}; let sh = Shell::new().unwrap(); -//! let dry_run = if sh.env_var("CI").is_ok() { None } else { Some("--dry-run") }; +//! let dry_run = if sh.var("CI").is_ok() { None } else { Some("--dry-run") }; //! cmd!(sh, "cargo publish {dry_run...}").run_echo()?; //! # Ok::<(), xshell::Error>(()) //! ``` @@ -203,7 +203,7 @@ //! //! cmd!(sh, "git tag {version}").run_echo()?; //! -//! let dry_run = if sh.env_var("CI").is_ok() { None } else { Some("--dry-run") }; +//! let dry_run = if sh.var("CI").is_ok() { None } else { Some("--dry-run") }; //! cmd!(sh, "cargo publish {dry_run...}").run()?; //! //! Ok(()) @@ -217,8 +217,7 @@ //! //! ## Maintenance //! -//! Minimum Supported Rust Version: 1.63.0. MSRV bump is not considered semver breaking. MSRV is -//! updated conservatively. +//! MSRV bump is not considered semver breaking. MSRV is updated conservatively. //! //! The crate isn't comprehensive yet, but this is a goal. You are hereby encouraged to submit PRs //! with missing functionality! @@ -426,10 +425,10 @@ impl Shell { /// /// Environment of the [`Shell`] affects all commands spawned via this /// shell. - pub fn env_var(&self, key: impl AsRef) -> Result { + pub fn var(&self, key: impl AsRef) -> Result { fn inner(sh: &Shell, key: &OsStr) -> Result { let env_os = sh - .env_var_os(key) + .var_os(key) .ok_or(VarError::NotPresent) .map_err(|err| Error::new_var(err, key.to_os_string()))?; env_os @@ -444,7 +443,7 @@ impl Shell { /// /// Environment of the [`Shell`] affects all commands spawned via this /// shell. - pub fn env_var_os(&self, key: impl AsRef) -> Option { + pub fn var_os(&self, key: impl AsRef) -> Option { fn inner(sh: &Shell, key: &OsStr) -> Option { sh.env.get(key).map(OsString::from).or_else(|| env::var_os(key)) } @@ -457,7 +456,7 @@ impl Shell { /// /// Environment of the [`Shell`] affects all commands spawned via this /// shell. - pub fn env_vars_os(&self) -> HashMap { + pub fn vars_os(&self) -> HashMap { let mut result: HashMap = Default::default(); result.extend(env::vars_os()); result.extend(self.env.iter().map(|(k, v)| (OsString::from(k), OsString::from(v)))); @@ -467,7 +466,7 @@ impl Shell { /// Sets the value of `key` environment variable for this [`Shell`] to `value`. /// /// Note that this doesn't affect [`std::env::var`]. - pub fn set_env_var(&mut self, key: impl AsRef, value: impl AsRef) { + pub fn set_var(&mut self, key: impl AsRef, value: impl AsRef) { fn inner(sh: &mut Shell, key: &OsStr, value: &OsStr) { Arc::make_mut(&mut sh.env).insert(key.into(), value.into()); } @@ -477,7 +476,7 @@ impl Shell { /// Returns a new [`Shell`] with environmental variable `key` set to `value`. /// /// Note that this doesn't affect [`std::env::var`]. - pub fn with_env_var(&self, key: impl AsRef, value: impl AsRef) -> Shell { + pub fn with_var(&self, key: impl AsRef, value: impl AsRef) -> Shell { fn inner(sh: &Shell, key: &OsStr, value: &OsStr) -> Shell { let mut env = Arc::clone(&sh.env); Arc::make_mut(&mut env).insert(key.into(), value.into()); @@ -523,11 +522,7 @@ impl Shell { /// Creates a `dst` file with the same contents as `src` #[doc(alias = "cp")] - pub fn copy_file_to_path( - &self, - src_file: impl AsRef, - dst_file: impl AsRef, - ) -> Result<()> { + pub fn copy_file(&self, src_file: impl AsRef, dst_file: impl AsRef) -> Result<()> { fn inner(sh: &Shell, src: &Path, dst: &Path) -> Result<()> { let src = sh.path(src); let dst = sh.path(dst); @@ -554,7 +549,7 @@ impl Shell { let Some(file_name) = src.file_name() else { return Err(Error::new_copy_file(io::ErrorKind::InvalidData.into(), src, dst)); }; - sh.copy_file_to_path(&src, &dst.join(file_name)) + sh.copy_file(&src, &dst.join(file_name)) } inner(self, src_file.as_ref(), dst_dir.as_ref()) } diff --git a/tests/it/env.rs b/tests/it/env.rs index 9d5d2bb..2c73333 100644 --- a/tests/it/env.rs +++ b/tests/it/env.rs @@ -34,8 +34,8 @@ fn test_env() { ); } - let _g1 = sh.set_env_var(v1, "foobar"); - let _g2 = sh.set_env_var(v2, "quark"); + let _g1 = sh.set_var(v1, "foobar"); + let _g2 = sh.set_var(v2, "quark"); assert_env(cmd!(sh, "xecho -$ {v1} {v2}"), &[(v1, Some("foobar")), (v2, Some("quark"))]); assert_env(cmd!(cloned_sh, "xecho -$ {v1} {v2}"), &[(v1, None), (v2, None)]); @@ -79,8 +79,8 @@ fn test_env_clear() { &[(v1, Some("789")), (v2, None)], ); - let _g1 = sh.set_env_var(v1, "foobar"); - let _g2 = sh.set_env_var(v2, "quark"); + let _g1 = sh.set_var(v1, "foobar"); + let _g2 = sh.set_var(v2, "quark"); assert_env(cmd!(sh, "{xecho} -$ {v1} {v2}").env_clear(), &[(v1, None), (v2, None)]); assert_env( diff --git a/tests/it/main.rs b/tests/it/main.rs index aef9b51..f7b3525 100644 --- a/tests/it/main.rs +++ b/tests/it/main.rs @@ -24,7 +24,7 @@ fn setup() -> Shell { .unwrap_or_else(|err| panic!("failed to install binaries from mock_bin: {}", err)); }); - sh.set_env_var("PATH", target_dir); + sh.set_var("PATH", target_dir); sh } @@ -318,22 +318,22 @@ const VAR: &str = "SPICA"; fn test_subshells_env() { let sh = setup(); - let e1 = sh.env_var_os(VAR); + let e1 = sh.var_os(VAR); { let mut sh = sh.clone(); - sh.set_env_var(VAR, "1"); - let e2 = sh.env_var_os(VAR); + sh.set_var(VAR, "1"); + let e2 = sh.var_os(VAR); assert_eq!(e2.as_deref(), Some("1".as_ref())); { let mut sh = sh.clone(); - let _e = sh.set_env_var(VAR, "2"); - let e3 = sh.env_var_os(VAR); + let _e = sh.set_var(VAR, "2"); + let e3 = sh.var_os(VAR); assert_eq!(e3.as_deref(), Some("2".as_ref())); } - let e4 = sh.env_var_os(VAR); + let e4 = sh.var_os(VAR); assert_eq!(e4, e2); } - let e5 = sh.env_var_os(VAR); + let e5 = sh.var_os(VAR); assert_eq!(e5, e1); } @@ -341,17 +341,17 @@ fn test_subshells_env() { fn test_push_env_and_set_env_var() { let sh = setup(); - let e1 = sh.env_var_os(VAR); + let e1 = sh.var_os(VAR); { let mut sh = sh.clone(); - sh.set_env_var(VAR, "1"); - let e2 = sh.env_var_os(VAR); + sh.set_var(VAR, "1"); + let e2 = sh.var_os(VAR); assert_eq!(e2.as_deref(), Some("1".as_ref())); - sh.set_env_var(VAR, "2"); - let e3 = sh.env_var_os(VAR); + sh.set_var(VAR, "2"); + let e3 = sh.var_os(VAR); assert_eq!(e3.as_deref(), Some("2".as_ref())); } - let e5 = sh.env_var_os(VAR); + let e5 = sh.var_os(VAR); assert_eq!(e5, e1); } @@ -369,7 +369,7 @@ fn test_copy_file() { sh.write_file(&foo, "hello world").unwrap(); sh.create_dir(&dir).unwrap(); - sh.copy_file_to_path(&foo, &bar).unwrap(); + sh.copy_file(&foo, &bar).unwrap(); assert_eq!(sh.read_file(&bar).unwrap(), "hello world"); sh.copy_file_to_dir(&foo, &dir).unwrap(); diff --git a/xshell-macros/Cargo.toml b/xshell-macros/Cargo.toml index 6644f54..796cc2a 100644 --- a/xshell-macros/Cargo.toml +++ b/xshell-macros/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "xshell-macros" description = "Private implementation detail of xshell crate" -version = "0.2.7" +version = "0.3.0-pre.1" license = "MIT OR Apache-2.0" repository = "https://github.com/matklad/xshell" authors = ["Aleksey Kladov "] edition = "2021" -rust-version = "1.59" +rust-version = "1.73" [lib] proc-macro = true