From a55302052255911b11a695e122312f9d459a3c2d Mon Sep 17 00:00:00 2001 From: Paul Soporan Date: Wed, 16 Jul 2025 00:20:02 +0300 Subject: [PATCH 1/6] feat: implement support for `--cwd` flag --- packages/zpm-switch/src/commands/proxy.rs | 6 ++---- .../zpm-switch/src/commands/switch/explicit.rs | 6 ++---- packages/zpm-switch/src/cwd.rs | 9 ++++++++- packages/zpm-switch/src/yarn.rs | 16 +++++++++++++++- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/packages/zpm-switch/src/commands/proxy.rs b/packages/zpm-switch/src/commands/proxy.rs index 950583c7..0894a829 100644 --- a/packages/zpm-switch/src/commands/proxy.rs +++ b/packages/zpm-switch/src/commands/proxy.rs @@ -3,7 +3,7 @@ use std::process::ExitStatus; use clipanion::cli; use zpm_utils::ToFileString; -use crate::{cwd::{get_fake_cwd, get_final_cwd}, errors::Error, manifest::{find_closest_package_manager, validate_package_manager}, yarn::get_default_yarn_version, yarn_enums::ReleaseLine}; +use crate::{cwd::{get_final_cwd, restore_args}, errors::Error, manifest::{find_closest_package_manager, validate_package_manager}, yarn::get_default_yarn_version, yarn_enums::ReleaseLine}; use super::switch::explicit::ExplicitCommand; @@ -39,9 +39,7 @@ impl ProxyCommand { = self.args.clone(); // Don't forget to add back the cwd parameter that was removed earlier on! - if let Some(cwd) = get_fake_cwd() { - args.insert(0, cwd.to_file_string()); - } + restore_args(&mut args); ExplicitCommand::run(&reference, &args).await } diff --git a/packages/zpm-switch/src/commands/switch/explicit.rs b/packages/zpm-switch/src/commands/switch/explicit.rs index 5a7833fa..b29f78ba 100644 --- a/packages/zpm-switch/src/commands/switch/explicit.rs +++ b/packages/zpm-switch/src/commands/switch/explicit.rs @@ -3,7 +3,7 @@ use std::process::{Command, ExitStatus, Stdio}; use clipanion::cli; use zpm_utils::ToFileString; -use crate::{cwd::{get_fake_cwd, get_final_cwd}, errors::Error, install::install_package_manager, manifest::{find_closest_package_manager, PackageManagerReference, VersionPackageManagerReference}, yarn::resolve_selector, yarn_enums::Selector}; +use crate::{cwd::{get_final_cwd, restore_args}, errors::Error, install::install_package_manager, manifest::{find_closest_package_manager, PackageManagerReference, VersionPackageManagerReference}, yarn::resolve_selector, yarn_enums::Selector}; #[cli::command(proxy)] #[cli::path("switch")] @@ -44,9 +44,7 @@ impl ExplicitCommand { = self.args.clone(); // Don't forget to add back the cwd parameter that was removed earlier on! - if let Some(cwd) = get_fake_cwd() { - args.insert(0, cwd.to_file_string()); - } + restore_args(&mut args); let version = resolve_selector(&self.selector).await?; diff --git a/packages/zpm-switch/src/cwd.rs b/packages/zpm-switch/src/cwd.rs index 1de4b474..16bf2f87 100644 --- a/packages/zpm-switch/src/cwd.rs +++ b/packages/zpm-switch/src/cwd.rs @@ -6,7 +6,7 @@ use std::sync::Mutex; -use zpm_utils::{Path, PathError}; +use zpm_utils::{Path, PathError, ToFileString}; static FAKE_CWD: Mutex> = Mutex::new(None); @@ -25,3 +25,10 @@ pub fn get_final_cwd() -> Result { Path::current_dir() } } + +pub fn restore_args(args: &mut Vec) { + if let Some(cwd) = get_fake_cwd() { + // We add an explicit `--cwd` so that both implicit and explicit cwd arguments are correctly forwarded. + args.insert(0, format!("--cwd={}", cwd.to_file_string())); + } +} diff --git a/packages/zpm-switch/src/yarn.rs b/packages/zpm-switch/src/yarn.rs index a652d5cc..fda6f96b 100644 --- a/packages/zpm-switch/src/yarn.rs +++ b/packages/zpm-switch/src/yarn.rs @@ -2,7 +2,7 @@ use serde::Deserialize; use serde_with::serde_as; use std::{collections::BTreeMap, str::FromStr}; use zpm_semver::{Range, Version}; -use zpm_utils::{ExplicitPath, FromFileString, Path, ToFileString}; +use zpm_utils::{ExplicitPath, FromFileString, Path, RawPath, ToFileString}; use crate::{errors::Error, http::fetch, manifest::{PackageManagerReference, VersionPackageManagerReference}, yarn_enums::{ChannelSelector, Selector}}; @@ -112,6 +112,20 @@ pub fn extract_bin_meta() -> BinMeta { .skip(1) .collect::>(); + if args.len() >= 2 && args[0] == "--cwd" { + let raw_path + = RawPath::from_str(&args[1]).unwrap(); + + cwd = Some(raw_path.path); + args.drain(..2); + } else if args.len() >= 1 && args[0].starts_with("--cwd=") { + let raw_path + = RawPath::from_str(&args[0][6..]).unwrap(); + + cwd = Some(raw_path.path); + args.remove(0); + } + if let Some(first_arg) = args.first() { let explicit_path = ExplicitPath::from_str(first_arg); From 27219fafc966a19821cda52c2eac6f27919e9934 Mon Sep 17 00:00:00 2001 From: Paul Soporan Date: Wed, 16 Jul 2025 00:35:41 +0300 Subject: [PATCH 2/6] chore: add todo --- packages/zpm-switch/src/yarn.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/zpm-switch/src/yarn.rs b/packages/zpm-switch/src/yarn.rs index fda6f96b..f5909e18 100644 --- a/packages/zpm-switch/src/yarn.rs +++ b/packages/zpm-switch/src/yarn.rs @@ -112,6 +112,7 @@ pub fn extract_bin_meta() -> BinMeta { .skip(1) .collect::>(); + // TODO: Use clipanion to error on incorrect placement of `--cwd` argument. if args.len() >= 2 && args[0] == "--cwd" { let raw_path = RawPath::from_str(&args[1]).unwrap(); From f01e8a9544cd6ebd41b149bca990e181fa899c06 Mon Sep 17 00:00:00 2001 From: Paul Soporan Date: Wed, 16 Jul 2025 01:06:05 +0300 Subject: [PATCH 3/6] refactor: don't unwrap --- packages/zpm-switch/src/commands/mod.rs | 2 +- packages/zpm-switch/src/yarn.rs | 24 +++++++++++++++--------- packages/zpm/src/commands/mod.rs | 2 +- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/packages/zpm-switch/src/commands/mod.rs b/packages/zpm-switch/src/commands/mod.rs index 283ddc7f..a2374ef1 100644 --- a/packages/zpm-switch/src/commands/mod.rs +++ b/packages/zpm-switch/src/commands/mod.rs @@ -29,7 +29,7 @@ pub async fn run_default() -> ExitCode { cwd, args, version, - } = extract_bin_meta(); + } = extract_bin_meta().unwrap(); if let Some(cwd) = cwd { set_fake_cwd(cwd); diff --git a/packages/zpm-switch/src/yarn.rs b/packages/zpm-switch/src/yarn.rs index f5909e18..66ad3176 100644 --- a/packages/zpm-switch/src/yarn.rs +++ b/packages/zpm-switch/src/yarn.rs @@ -2,7 +2,7 @@ use serde::Deserialize; use serde_with::serde_as; use std::{collections::BTreeMap, str::FromStr}; use zpm_semver::{Range, Version}; -use zpm_utils::{ExplicitPath, FromFileString, Path, RawPath, ToFileString}; +use zpm_utils::{ExplicitPath, FromFileString, Path, PathError, RawPath, ToFileString}; use crate::{errors::Error, http::fetch, manifest::{PackageManagerReference, VersionPackageManagerReference}, yarn_enums::{ChannelSelector, Selector}}; @@ -105,7 +105,7 @@ pub fn get_bin_version() -> String { .to_string() } -pub fn extract_bin_meta() -> BinMeta { +pub fn extract_bin_meta() -> Result { let mut cwd = None; let mut args = std::env::args() @@ -115,13 +115,13 @@ pub fn extract_bin_meta() -> BinMeta { // TODO: Use clipanion to error on incorrect placement of `--cwd` argument. if args.len() >= 2 && args[0] == "--cwd" { let raw_path - = RawPath::from_str(&args[1]).unwrap(); + = RawPath::from_str(&args[1])?; cwd = Some(raw_path.path); args.drain(..2); } else if args.len() >= 1 && args[0].starts_with("--cwd=") { let raw_path - = RawPath::from_str(&args[0][6..]).unwrap(); + = RawPath::from_str(&args[0][6..])?; cwd = Some(raw_path.path); args.remove(0); @@ -131,18 +131,24 @@ pub fn extract_bin_meta() -> BinMeta { let explicit_path = ExplicitPath::from_str(first_arg); - if let Ok(explicit_path) = explicit_path { - cwd = Some(explicit_path.raw_path.path); - args.remove(0); + match explicit_path { + Ok(explicit_path) => { + cwd = Some(explicit_path.raw_path.path); + args.remove(0); + }, + Err(PathError::InvalidExplicitPathParameter(_)) => { + // This is not a valid explicit path, so we don't modify `cwd`. + }, + Err(err) => return Err(err), } } let version = get_bin_version(); - BinMeta { + Ok(BinMeta { cwd, args, version, - } + }) } diff --git a/packages/zpm/src/commands/mod.rs b/packages/zpm/src/commands/mod.rs index 63bbcffd..256f4576 100644 --- a/packages/zpm/src/commands/mod.rs +++ b/packages/zpm/src/commands/mod.rs @@ -62,7 +62,7 @@ pub fn run_default() -> ExitCode { cwd, args, version, - } = extract_bin_meta(); + } = extract_bin_meta().unwrap(); if let Some(cwd) = cwd { cwd.sys_set_current_dir() From 4a5e830f3be10e7ca513a842ca3a4aaf5c686bb6 Mon Sep 17 00:00:00 2001 From: Paul Soporan Date: Wed, 16 Jul 2025 01:13:03 +0300 Subject: [PATCH 4/6] refactor: use expect --- packages/zpm-switch/src/commands/mod.rs | 2 +- packages/zpm/src/commands/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/zpm-switch/src/commands/mod.rs b/packages/zpm-switch/src/commands/mod.rs index a2374ef1..b2631717 100644 --- a/packages/zpm-switch/src/commands/mod.rs +++ b/packages/zpm-switch/src/commands/mod.rs @@ -29,7 +29,7 @@ pub async fn run_default() -> ExitCode { cwd, args, version, - } = extract_bin_meta().unwrap(); + } = extract_bin_meta().expect("Failed to extract binary metadata"); if let Some(cwd) = cwd { set_fake_cwd(cwd); diff --git a/packages/zpm/src/commands/mod.rs b/packages/zpm/src/commands/mod.rs index 256f4576..ee6d3593 100644 --- a/packages/zpm/src/commands/mod.rs +++ b/packages/zpm/src/commands/mod.rs @@ -62,7 +62,7 @@ pub fn run_default() -> ExitCode { cwd, args, version, - } = extract_bin_meta().unwrap(); + } = extract_bin_meta().expect("Failed to extract binary metadata"); if let Some(cwd) = cwd { cwd.sys_set_current_dir() From e1e2c0105444f2b61e6beec2979565185833263b Mon Sep 17 00:00:00 2001 From: Paul Soporan Date: Wed, 16 Jul 2025 01:34:22 +0300 Subject: [PATCH 5/6] refactor: avoid magic number --- packages/zpm-switch/src/yarn.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/zpm-switch/src/yarn.rs b/packages/zpm-switch/src/yarn.rs index 66ad3176..ab9b6c0d 100644 --- a/packages/zpm-switch/src/yarn.rs +++ b/packages/zpm-switch/src/yarn.rs @@ -105,6 +105,9 @@ pub fn get_bin_version() -> String { .to_string() } +const CWD_FLAG: &str = "--cwd"; +const CWD_FLAG_EQUALS: &str = "--cwd="; + pub fn extract_bin_meta() -> Result { let mut cwd = None; @@ -113,15 +116,15 @@ pub fn extract_bin_meta() -> Result { .collect::>(); // TODO: Use clipanion to error on incorrect placement of `--cwd` argument. - if args.len() >= 2 && args[0] == "--cwd" { + if args.len() >= 2 && args[0] == CWD_FLAG { let raw_path = RawPath::from_str(&args[1])?; cwd = Some(raw_path.path); args.drain(..2); - } else if args.len() >= 1 && args[0].starts_with("--cwd=") { + } else if args.len() >= 1 && args[0].starts_with(CWD_FLAG_EQUALS) { let raw_path - = RawPath::from_str(&args[0][6..])?; + = RawPath::from_str(&args[0][CWD_FLAG_EQUALS.len()..])?; cwd = Some(raw_path.path); args.remove(0); From 92411687cd7f9d3b60e1be23fe390566d7977d15 Mon Sep 17 00:00:00 2001 From: Paul Soporan Date: Wed, 16 Jul 2025 18:09:57 +0300 Subject: [PATCH 6/6] refactor: use clipanion for cwd arguments --- packages/zpm-switch/src/yarn.rs | 1 - packages/zpm/src/commands/entries/cwd.rs | 23 +++++++++++++++ packages/zpm/src/commands/entries/mod.rs | 2 ++ packages/zpm/src/commands/entries/run.rs | 37 ++++++++++++++++++++++++ packages/zpm/src/commands/mod.rs | 21 +++++--------- packages/zpm/src/commands/run.rs | 2 +- 6 files changed, 70 insertions(+), 16 deletions(-) create mode 100644 packages/zpm/src/commands/entries/cwd.rs create mode 100644 packages/zpm/src/commands/entries/mod.rs create mode 100644 packages/zpm/src/commands/entries/run.rs diff --git a/packages/zpm-switch/src/yarn.rs b/packages/zpm-switch/src/yarn.rs index ab9b6c0d..b7cf8886 100644 --- a/packages/zpm-switch/src/yarn.rs +++ b/packages/zpm-switch/src/yarn.rs @@ -115,7 +115,6 @@ pub fn extract_bin_meta() -> Result { .skip(1) .collect::>(); - // TODO: Use clipanion to error on incorrect placement of `--cwd` argument. if args.len() >= 2 && args[0] == CWD_FLAG { let raw_path = RawPath::from_str(&args[1])?; diff --git a/packages/zpm/src/commands/entries/cwd.rs b/packages/zpm/src/commands/entries/cwd.rs new file mode 100644 index 00000000..a470c770 --- /dev/null +++ b/packages/zpm/src/commands/entries/cwd.rs @@ -0,0 +1,23 @@ +use std::{path::PathBuf, process::ExitCode}; + +use clipanion::{cli, prelude::Cli}; + +use crate::{commands::YarnCli, error::Error}; + +// TODO: Use clipanion to error on incorrect placement of `--cwd` argument. +#[cli::command(default, proxy)] +#[derive(Debug)] +pub struct Cwd { + #[cli::option("--cwd")] + cwd: String, + + args: Vec, +} + +impl Cwd { + pub fn execute(&self) -> Result { + std::env::set_current_dir(PathBuf::from(&self.cwd))?; + + Ok(YarnCli::run(self.cli_environment.clone().with_argv(self.args.clone()))) + } +} diff --git a/packages/zpm/src/commands/entries/mod.rs b/packages/zpm/src/commands/entries/mod.rs new file mode 100644 index 00000000..d794bea7 --- /dev/null +++ b/packages/zpm/src/commands/entries/mod.rs @@ -0,0 +1,2 @@ +pub mod cwd; +pub mod run; diff --git a/packages/zpm/src/commands/entries/run.rs b/packages/zpm/src/commands/entries/run.rs new file mode 100644 index 00000000..288e7220 --- /dev/null +++ b/packages/zpm/src/commands/entries/run.rs @@ -0,0 +1,37 @@ +use std::{process::ExitCode, str::FromStr}; + +use clipanion::{cli, prelude::Cli}; +use zpm_utils::{ExplicitPath, PathError}; + +use crate::{commands::YarnCli, error::Error}; + +#[cli::command(default, proxy)] +#[derive(Debug)] +pub struct Run { + leading_argument: String, + + args: Vec, +} + +impl Run { + pub fn execute(&self) -> Result { + match ExplicitPath::from_str(&self.leading_argument) { + Ok(explicit_path) => { + std::env::set_current_dir(explicit_path.raw_path.path.to_path_buf())?; + + Ok(YarnCli::run(self.cli_environment.clone().with_argv(self.args.clone()))) + }, + + Err(PathError::InvalidExplicitPathParameter(_)) => { + Ok(YarnCli::run(self.cli_environment.clone().with_argv( + ["run".to_owned(), self.leading_argument.clone()] + .into_iter() + .chain(self.args.clone()) + .collect() + ))) + }, + + Err(err) => Err(err.into()), + } + } +} diff --git a/packages/zpm/src/commands/mod.rs b/packages/zpm/src/commands/mod.rs index ee6d3593..59fb5835 100644 --- a/packages/zpm/src/commands/mod.rs +++ b/packages/zpm/src/commands/mod.rs @@ -2,9 +2,10 @@ use std::process::ExitCode; use clipanion::{prelude::*, program, Environment}; use zpm_macros::track_time; -use zpm_switch::{extract_bin_meta, BinMeta}; +use zpm_switch::{extract_bin_meta, get_bin_version, BinMeta}; mod debug; +mod entries; mod add; mod bin; @@ -33,6 +34,9 @@ program!(YarnCli, [ debug::check_semver_version::CheckSemverVersion, debug::print_platform::PrintPlatform, + entries::cwd::Cwd, + entries::run::Run, + add::Add, bin::BinList, bin::Bin, @@ -58,23 +62,12 @@ program!(YarnCli, [ #[track_time] pub fn run_default() -> ExitCode { - let BinMeta { - cwd, - args, - version, - } = extract_bin_meta().expect("Failed to extract binary metadata"); - - if let Some(cwd) = cwd { - cwd.sys_set_current_dir() - .expect("Failed to set current directory"); - } - let env = Environment::default() .with_program_name("Yarn Package Manager".to_string()) .with_binary_name("yarn".to_string()) - .with_version(version) - .with_argv(args); + .with_version(get_bin_version()) + .with_argv(std::env::args().skip(1).collect()); YarnCli::run(env) } diff --git a/packages/zpm/src/commands/run.rs b/packages/zpm/src/commands/run.rs index 60138d01..eaf2f9db 100644 --- a/packages/zpm/src/commands/run.rs +++ b/packages/zpm/src/commands/run.rs @@ -5,7 +5,7 @@ use clipanion::cli; use crate::{error::Error, project, script::ScriptEnvironment}; -#[cli::command(default, proxy)] +#[cli::command(proxy)] #[cli::path("run")] #[cli::category("Scripting commands")] #[cli::description("Run a dependency binary or local script")]