aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/util
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-18 07:18:07 +0900
committernsfisis <nsfisis@gmail.com>2026-08-18 07:18:07 +0900
commit6a7e64d04a8b0ad932169df43e2d93efe8ceedf8 (patch)
treef74bca82b226fde11c820ba3988e094a49073940 /crates/shirabe/src/util
parentbed0cd33ca32b95aed9d65891f97649a6f9d0069 (diff)
downloadphp-shirabe-6a7e64d04a8b0ad932169df43e2d93efe8ceedf8.tar.gz
php-shirabe-6a7e64d04a8b0ad932169df43e2d93efe8ceedf8.tar.zst
php-shirabe-6a7e64d04a8b0ad932169df43e2d93efe8ceedf8.zip
fix: propagate ported exceptions instead of flattening them
`ProcessExecutor::execute_args` existed only to turn `execute`'s `anyhow::Result` into an exit code of 1, so every one of its ~87 call sites silently took the "command failed" branch on an error PHP would have thrown. It is gone; callers use `execute` and propagate with `?`. Where the enclosing function had no `Result` to propagate into, its signature grew one, up to and including `Git::get_version`, `Svn::binary_version`, `GitHub`/`GitLab`/`Bitbucket::authorize_oauth`, `InitCommand::get_git_config` and `DiagnoseCommand::check_git`. The VCS drivers had the same problem in the other direction: their `get_contents` returned `Result<Response, Box<TransportException>>`, a type too narrow for the PHP method, which lets any Throwable out of the `catch (TransportException $e)` block. Every non-transport error was therefore rewritten into a `TransportException` with code 0, which the callers switch on. They now return `anyhow::Result<Result<Response, Box<TransportException>>>`: the outer `Result` carries what PHP does not catch, the inner one the exception the drivers handle. That signature also restores `GitLabDriver::getContents`: the 400/401 `TransportException`s it raises to force authentication are thrown inside its own `try` block and handled by its own `catch`, but the port returned them straight to the caller, so the authentication flow behind them never ran. `impl_php_exception!` gains `From<Box<$ty>> for anyhow::Error` so a caught exception can be re-propagated with `?`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/util')
-rw-r--r--crates/shirabe/src/util/auth_helper.rs6
-rw-r--r--crates/shirabe/src/util/bitbucket.rs23
-rw-r--r--crates/shirabe/src/util/filesystem.rs10
-rw-r--r--crates/shirabe/src/util/git.rs98
-rw-r--r--crates/shirabe/src/util/github.rs14
-rw-r--r--crates/shirabe/src/util/gitlab.rs24
-rw-r--r--crates/shirabe/src/util/hg.rs49
-rw-r--r--crates/shirabe/src/util/perforce.rs9
-rw-r--r--crates/shirabe/src/util/platform.rs2
-rw-r--r--crates/shirabe/src/util/process_executor.rs32
-rw-r--r--crates/shirabe/src/util/svn.rs8
11 files changed, 131 insertions, 144 deletions
diff --git a/crates/shirabe/src/util/auth_helper.rs b/crates/shirabe/src/util/auth_helper.rs
index 72f50aa3..5e6ea16e 100644
--- a/crates/shirabe/src/util/auth_helper.rs
+++ b/crates/shirabe/src/util/auth_helper.rs
@@ -213,7 +213,7 @@ impl AuthHelper {
}
}
- if !git_hub_util.authorize_oauth(origin)
+ if !git_hub_util.authorize_oauth(origin)?
&& (!self.io.is_interactive()
|| !git_hub_util.authorize_oauth_interactively(origin, Some(&message))?)
{
@@ -257,7 +257,7 @@ impl AuthHelper {
}
let scheme = parse_url(url).and_then(|parsed| parsed.scheme);
- if !git_lab_util.authorize_oauth(origin)
+ if !git_lab_util.authorize_oauth(origin)?
&& (!self.io.is_interactive()
|| !git_lab_util.authorize_oauth_interactively(
scheme.as_deref().unwrap_or(""),
@@ -337,7 +337,7 @@ impl AuthHelper {
);
let mut bit_bucket_util =
Bitbucket::new(self.io.clone(), self.config.clone(), None, None, None)?;
- if !bit_bucket_util.authorize_oauth(&origin)
+ if !bit_bucket_util.authorize_oauth(&origin)?
&& (!self.io.is_interactive()
|| !bit_bucket_util
.authorize_oauth_interactively(&origin, Some(&message))?)
diff --git a/crates/shirabe/src/util/bitbucket.rs b/crates/shirabe/src/util/bitbucket.rs
index 9a16c9a6..f50dc878 100644
--- a/crates/shirabe/src/util/bitbucket.rs
+++ b/crates/shirabe/src/util/bitbucket.rs
@@ -71,22 +71,17 @@ impl Bitbucket {
}
}
- pub fn authorize_oauth(&mut self, origin_url: &str) -> bool {
+ pub fn authorize_oauth(&mut self, origin_url: &str) -> anyhow::Result<bool> {
if origin_url != "bitbucket.org" {
- return false;
+ return Ok(false);
}
let mut output = PhpMixed::Null;
- if self
- .process
- .borrow_mut()
- .execute(
- &["git", "config", "bitbucket.accesstoken"],
- &mut output,
- None,
- )
- .unwrap_or(1)
- == 0
+ if self.process.borrow_mut().execute(
+ &["git", "config", "bitbucket.accesstoken"],
+ &mut output,
+ None,
+ )? == 0
{
let output_str = output.as_string().unwrap_or("").trim().to_string();
self.io.borrow_mut().set_authentication(
@@ -94,10 +89,10 @@ impl Bitbucket {
"x-token-auth".to_string(),
Some(output_str),
);
- return true;
+ return Ok(true);
}
- false
+ Ok(false)
}
fn request_access_token(&mut self) -> anyhow::Result<bool> {
diff --git a/crates/shirabe/src/util/filesystem.rs b/crates/shirabe/src/util/filesystem.rs
index 8726df63..bca18771 100644
--- a/crates/shirabe/src/util/filesystem.rs
+++ b/crates/shirabe/src/util/filesystem.rs
@@ -475,7 +475,7 @@ impl Filesystem {
if Platform::is_windows() {
// Try to copy & delete - this is a workaround for random "Access denied" errors.
let mut output = String::new();
- let result = self.get_process().execute_args(
+ let result = self.get_process().execute(
&[
"xcopy".to_string(),
source.to_string(),
@@ -487,7 +487,7 @@ impl Filesystem {
],
&mut output,
None,
- );
+ )?;
// clear stat cache because external processes aren't tracked by the php stat cache
clearstatcache2(false, "");
@@ -501,11 +501,11 @@ impl Filesystem {
// We do not use PHP's "rename" function here since it does not support
// the case where $source, and $target are located on different partitions.
let mut output = String::new();
- let result = self.get_process().execute_args(
+ let result = self.get_process().execute(
&["mv".to_string(), source.to_string(), target.to_string()],
&mut output,
None,
- );
+ )?;
// clear stat cache because external processes aren't tracked by the php stat cache
clearstatcache2(false, "");
@@ -932,7 +932,7 @@ impl Filesystem {
Platform::realpath(target),
];
let mut output = String::new();
- if self.get_process().execute_args(&cmd, &mut output, None) != 0 {
+ if self.get_process().execute(&cmd, &mut output, None)? != 0 {
return Err(IOException::new(
format!(
"Failed to create junction to \"{}\" at \"{}\".",
diff --git a/crates/shirabe/src/util/git.rs b/crates/shirabe/src/util/git.rs
index 5577250f..aea8b7b8 100644
--- a/crates/shirabe/src/util/git.rs
+++ b/crates/shirabe/src/util/git.rs
@@ -362,8 +362,8 @@ impl Git {
)?;
let message = "Cloning failed using an ssh key for authentication, enter your GitHub credentials to access private repos";
- if !git_hub_util.authorize_oauth(&m1) && self.io.is_interactive() {
- git_hub_util.authorize_oauth_interactively(&m1, Some(message));
+ if !git_hub_util.authorize_oauth(&m1)? && self.io.is_interactive() {
+ git_hub_util.authorize_oauth_interactively(&m1, Some(message))?;
}
}
@@ -423,8 +423,8 @@ impl Git {
if !self.io.has_authentication(&domain) {
let message = "Enter your Bitbucket credentials to access private repos";
- if !bitbucket_util.authorize_oauth(&domain) && self.io.is_interactive() {
- bitbucket_util.authorize_oauth_interactively(&domain, Some(message));
+ if !bitbucket_util.authorize_oauth(&domain)? && self.io.is_interactive() {
+ bitbucket_util.authorize_oauth_interactively(&domain, Some(message))?;
let access_token = bitbucket_util.get_token();
self.io.borrow_mut().set_authentication(
domain.clone(),
@@ -575,8 +575,8 @@ impl Git {
let message =
"Cloning failed, enter your GitLab credentials to access private repos";
- if !git_lab_util.authorize_oauth(&m2) && self.io.is_interactive() {
- git_lab_util.authorize_oauth_interactively(&m1, &m2, Some(message));
+ if !git_lab_util.authorize_oauth(&m2)? && self.io.is_interactive() {
+ git_lab_util.authorize_oauth_interactively(&m1, &m2, Some(message))?;
}
}
@@ -783,7 +783,7 @@ impl Git {
// update the repo if it is a valid git repository
let mut output = String::new();
if is_dir(dir)
- && self.process.borrow_mut().execute_args(
+ && self.process.borrow_mut().execute(
&[
"git".to_string(),
"rev-parse".to_string(),
@@ -791,7 +791,7 @@ impl Git {
],
&mut output,
Some(dir),
- ) == 0
+ )? == 0
&& trim(&output, None) == "."
{
// PHP try/finally
@@ -915,20 +915,20 @@ impl Git {
let mut branches: Option<String> = None;
let mut tags: Option<String> = None;
let mut output = String::new();
- if self.process.borrow_mut().execute_args(
+ if self.process.borrow_mut().execute(
&["git".to_string(), "branch".to_string()],
&mut output,
Some(dir),
- ) == 0
+ )? == 0
{
branches = Some(output);
}
let mut output = String::new();
- if self.process.borrow_mut().execute_args(
+ if self.process.borrow_mut().execute(
&["git".to_string(), "tag".to_string()],
&mut output,
Some(dir),
- ) == 0
+ )? == 0
{
tags = Some(output);
}
@@ -964,26 +964,26 @@ impl Git {
pub fn get_no_show_signature_flag(
process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
- ) -> String {
- let git_version = Self::get_version(process);
+ ) -> anyhow::Result<String> {
+ let git_version = Self::get_version(process)?;
if let Some(v) = git_version
&& version_compare(&v, "2.10.0-rc0", CmpOp::Ge)
{
- return " --no-show-signature".to_string();
+ return Ok(" --no-show-signature".to_string());
}
- String::new()
+ Ok(String::new())
}
pub fn get_no_show_signature_flags(
process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
- ) -> Vec<String> {
- let flags = Self::get_no_show_signature_flag(process);
+ ) -> anyhow::Result<Vec<String>> {
+ let flags = Self::get_no_show_signature_flag(process)?;
if flags.is_empty() {
- return vec![];
+ return Ok(vec![]);
}
- explode(" ", &substr(&flags, 1, None))
+ Ok(explode(" ", &substr(&flags, 1, None)))
}
/// Checks if git version supports --no-commit-header flag (git 2.33+)
@@ -991,12 +991,12 @@ impl Git {
/// @internal
pub fn supports_no_commit_header_flag(
process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
- ) -> bool {
- let git_version = Self::get_version(process);
+ ) -> anyhow::Result<bool> {
+ let git_version = Self::get_version(process)?;
- git_version
+ Ok(git_version
.map(|v| version_compare(&v, "2.33.0-rc0", CmpOp::Ge))
- .unwrap_or(false)
+ .unwrap_or(false))
}
/// Builds a git rev-list command with --no-commit-header flag when supported (git 2.33+)
@@ -1006,14 +1006,14 @@ impl Git {
pub fn build_rev_list_command(
process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
arguments: Vec<String>,
- ) -> Vec<String> {
+ ) -> anyhow::Result<Vec<String>> {
let mut command = vec!["git".to_string(), "rev-list".to_string()];
- if Self::supports_no_commit_header_flag(process) {
+ if Self::supports_no_commit_header_flag(process)? {
command.push("--no-commit-header".to_string());
}
command.extend(arguments);
- command
+ Ok(command)
}
/// Parses git rev-list output, removing 'commit <hash>' header lines for git < 2.33.
@@ -1025,20 +1025,24 @@ impl Git {
pub fn parse_rev_list_output(
output: &str,
process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
- ) -> String {
+ ) -> anyhow::Result<String> {
// If git supports --no-commit-header, output is already clean
- if Self::supports_no_commit_header_flag(process) {
- return output.to_string();
+ if Self::supports_no_commit_header_flag(process)? {
+ return Ok(output.to_string());
}
// Filter out "commit <hash>" lines for older git versions
- preg_replace(php_regex!(r"{^commit [a-f0-9]{40}\n?}m"), "", output)
+ Ok(preg_replace(
+ php_regex!(r"{^commit [a-f0-9]{40}\n?}m"),
+ "",
+ output,
+ ))
}
fn check_ref_is_in_mirror(&mut self, dir: &str, r#ref: &str) -> anyhow::Result<bool> {
let mut output = String::new();
if is_dir(dir)
- && self.process.borrow_mut().execute_args(
+ && self.process.borrow_mut().execute(
&[
"git".to_string(),
"rev-parse".to_string(),
@@ -1046,11 +1050,11 @@ impl Git {
],
&mut output,
Some(dir),
- ) == 0
+ )? == 0
&& trim(&output, None) == "."
{
let mut ignored_output = String::new();
- let exit_code = self.process.borrow_mut().execute_args(
+ let exit_code = self.process.borrow_mut().execute(
&[
"git".to_string(),
"rev-parse".to_string(),
@@ -1060,7 +1064,7 @@ impl Git {
],
&mut ignored_output,
Some(dir),
- );
+ )?;
if exit_code == 0 {
return Ok(true);
}
@@ -1108,7 +1112,7 @@ impl Git {
let mut output_mixed = PhpMixed::Null;
if is_local_path_repository {
let mut output = String::new();
- self.process.borrow_mut().execute_args(
+ self.process.borrow_mut().execute(
&[
"git".to_string(),
"remote".to_string(),
@@ -1117,7 +1121,7 @@ impl Git {
],
&mut output,
Some(dir),
- );
+ )?;
output_mixed = PhpMixed::String(output);
} else {
let commands = vec![
@@ -1178,9 +1182,11 @@ impl Git {
}
}
- pub fn clean_env(process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>) {
+ pub fn clean_env(
+ process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
+ ) -> anyhow::Result<()> {
// PHP: $process ?? new ProcessExecutor()
- let git_version = Self::get_version(process);
+ let git_version = Self::get_version(process)?;
if let Some(v) = git_version {
if version_compare(&v, "2.3.0", CmpOp::Ge) {
// added in git 2.3.0, prevents prompting the user for username/password
@@ -1210,6 +1216,8 @@ impl Git {
// clean up env for OSX, see https://github.com/composer/composer/issues/2146#issuecomment-35478940
Platform::clear_env("DYLD_LIBRARY_PATH");
+
+ Ok(())
}
pub fn get_github_domains_regex(config: &Config) -> String {
@@ -1241,11 +1249,11 @@ impl Git {
clearstatcache();
let mut ignored_output = String::new();
- if self.process.borrow_mut().execute_args(
+ if self.process.borrow_mut().execute(
&["git".to_string(), "--version".to_string()],
&mut ignored_output,
Option::<&str>::None,
- ) != 0
+ )? != 0
{
return Err(RuntimeException::new(Url::sanitize(format!(
"Failed to clone {}, git was not found, check that it is installed and in your PATH env.\n\n{}",
@@ -1263,16 +1271,16 @@ impl Git {
/// @return string|null The git version number, if present.
pub fn get_version(
process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
- ) -> Option<String> {
+ ) -> anyhow::Result<Option<String>> {
let mut version = VERSION.lock().unwrap();
if version.is_none() {
*version = Some(None);
let mut output = String::new();
- let exit_code: i64 = process.borrow_mut().execute_args(
+ let exit_code: i64 = process.borrow_mut().execute(
&["git".to_string(), "--version".to_string()],
&mut output,
Option::<&str>::None,
- );
+ )?;
if exit_code == 0
&& let Some(matches) =
preg_match(php_regex!(r"/^git version (\d+(?:\.\d+)+)/m"), &output)
@@ -1280,7 +1288,7 @@ impl Git {
*version = Some(matches.get(1).map(str::to_string));
}
}
- version.clone().unwrap_or(None)
+ Ok(version.clone().unwrap_or(None))
}
/// For testing only. Resets the cached git `version` static so the next
diff --git a/crates/shirabe/src/util/github.rs b/crates/shirabe/src/util/github.rs
index ccfedcf8..d4fd3283 100644
--- a/crates/shirabe/src/util/github.rs
+++ b/crates/shirabe/src/util/github.rs
@@ -52,14 +52,14 @@ impl GitHub {
})
}
- pub fn authorize_oauth(&mut self, origin_url: &str) -> bool {
+ pub fn authorize_oauth(&mut self, origin_url: &str) -> anyhow::Result<bool> {
let github_domains = self.config.borrow_mut().get("github-domains");
if !in_array_loose(origin_url.to_string(), github_domains.values()) {
- return false;
+ return Ok(false);
}
let mut output = String::new();
- if self.process.borrow_mut().execute_args(
+ if self.process.borrow_mut().execute(
&[
"git".to_string(),
"config".to_string(),
@@ -67,17 +67,17 @@ impl GitHub {
],
&mut output,
None,
- ) == 0
+ )? == 0
{
self.io.borrow_mut().set_authentication(
origin_url.to_string(),
output.trim().to_string(),
Some("x-oauth-basic".to_string()),
);
- return true;
+ return Ok(true);
}
- false
+ Ok(false)
}
pub fn authorize_oauth_interactively(
@@ -101,7 +101,7 @@ impl GitHub {
if self
.process
.borrow_mut()
- .execute_args(&["hostname".to_string()], &mut output, None)
+ .execute(&["hostname".to_string()], &mut output, None)?
== 0
{
note += &format!(" on {}", output.trim());
diff --git a/crates/shirabe/src/util/gitlab.rs b/crates/shirabe/src/util/gitlab.rs
index c101b1f9..fa9bef69 100644
--- a/crates/shirabe/src/util/gitlab.rs
+++ b/crates/shirabe/src/util/gitlab.rs
@@ -51,7 +51,7 @@ impl GitLab {
})
}
- pub fn authorize_oauth(&mut self, origin_url: &str) -> bool {
+ pub fn authorize_oauth(&mut self, origin_url: &str) -> anyhow::Result<bool> {
// before composer 1.9, origin URLs had no port number in them
let bc_origin_url = preg_replace(php_regex!("{:\\d+}"), "", origin_url);
@@ -59,12 +59,12 @@ impl GitLab {
if !in_array_strict(origin_url.to_string(), gitlab_domains.values())
&& !in_array_strict(bc_origin_url.clone(), gitlab_domains.values())
{
- return false;
+ return Ok(false);
}
// if available use token from git config
let mut output = String::new();
- if self.process.borrow_mut().execute_args(
+ if self.process.borrow_mut().execute(
&[
"git".to_string(),
"config".to_string(),
@@ -72,20 +72,20 @@ impl GitLab {
],
&mut output,
None,
- ) == 0
+ )? == 0
{
self.io.borrow_mut().set_authentication(
origin_url.to_string(),
output.trim().to_string(),
Some("oauth2".to_string()),
);
- return true;
+ return Ok(true);
}
// if available use deploy token from git config
let mut token_user = String::new();
let mut token_password = String::new();
- if self.process.borrow_mut().execute_args(
+ if self.process.borrow_mut().execute(
&[
"git".to_string(),
"config".to_string(),
@@ -93,8 +93,8 @@ impl GitLab {
],
&mut token_user,
None,
- ) == 0
- && self.process.borrow_mut().execute_args(
+ )? == 0
+ && self.process.borrow_mut().execute(
&[
"git".to_string(),
"config".to_string(),
@@ -102,14 +102,14 @@ impl GitLab {
],
&mut token_password,
None,
- ) == 0
+ )? == 0
{
self.io.borrow_mut().set_authentication(
origin_url.to_string(),
token_user.trim().to_string(),
Some(token_password.trim().to_string()),
);
- return true;
+ return Ok(true);
}
// if available use token from composer config
@@ -165,10 +165,10 @@ impl GitLab {
);
}
- return true;
+ return Ok(true);
}
- false
+ Ok(false)
}
pub fn authorize_oauth_interactively(
diff --git a/crates/shirabe/src/util/hg.rs b/crates/shirabe/src/util/hg.rs
index 3180f4f9..575c2c1f 100644
--- a/crates/shirabe/src/util/hg.rs
+++ b/crates/shirabe/src/util/hg.rs
@@ -6,9 +6,9 @@ use crate::io::IOInterfaceImmutable;
use crate::util::ProcessExecutor;
use crate::util::Url;
use shirabe_php_shim::{php_regex, preg_match, rawurlencode};
-use std::sync::OnceLock;
+use std::sync::Mutex;
-static VERSION: OnceLock<Option<String>> = OnceLock::new();
+static VERSION: Mutex<Option<Option<String>>> = Mutex::new(None);
#[derive(Debug)]
pub struct Hg {
@@ -48,7 +48,7 @@ impl Hg {
if self
.process
.borrow_mut()
- .execute_args(&command, &mut ignored_output, cwd.as_deref())
+ .execute(&command, &mut ignored_output, cwd.as_deref())?
== 0
{
return Ok(());
@@ -107,7 +107,7 @@ impl Hg {
if self
.process
.borrow_mut()
- .execute_args(&command, &mut ignored_output, cwd.as_deref())
+ .execute(&command, &mut ignored_output, cwd.as_deref())?
== 0
{
return Ok(());
@@ -125,7 +125,7 @@ impl Hg {
}
fn throw_exception(&self, message: &str, url: &str) -> anyhow::Result<()> {
- if Self::get_version(&self.process).is_none() {
+ if Self::get_version(&self.process)?.is_none() {
anyhow::bail!(
"{}",
Url::sanitize(format!(
@@ -141,24 +141,25 @@ impl Hg {
pub fn get_version(
process: &std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
- ) -> Option<&'static str> {
- VERSION
- .get_or_init(|| {
- let mut output = String::new();
- if process.borrow_mut().execute_args(
- &["hg".to_string(), "--version".to_string()],
- &mut output,
- None,
- ) == 0
- && let Some(matches) = preg_match(
- php_regex!(r"/^.+? (\d+(?:\.\d+)+)(?:\+.*?)?\)?\r?\n/"),
- &output,
- )
- {
- return matches.get(1).map(str::to_string);
- }
- None
- })
- .as_deref()
+ ) -> anyhow::Result<Option<String>> {
+ let mut version = VERSION.lock().unwrap();
+ if version.is_none() {
+ *version = Some(None);
+ let mut output = String::new();
+ if process.borrow_mut().execute(
+ &["hg".to_string(), "--version".to_string()],
+ &mut output,
+ None,
+ )? == 0
+ && let Some(matches) = preg_match(
+ php_regex!(r"/^.+? (\d+(?:\.\d+)+)(?:\+.*?)?\)?\r?\n/"),
+ &output,
+ )
+ {
+ *version = Some(matches.get(1).map(str::to_string));
+ }
+ }
+
+ Ok(version.clone().unwrap_or(None))
}
}
diff --git a/crates/shirabe/src/util/perforce.rs b/crates/shirabe/src/util/perforce.rs
index 95cff0ca..295851c6 100644
--- a/crates/shirabe/src/util/perforce.rs
+++ b/crates/shirabe/src/util/perforce.rs
@@ -78,9 +78,12 @@ impl Perforce {
Self::new(repo_config, port, path, process, Platform::is_windows(), io)
}
- pub fn check_server_exists(url: &str, process_executor: &mut ProcessExecutor) -> bool {
+ pub fn check_server_exists(
+ url: &str,
+ process_executor: &mut ProcessExecutor,
+ ) -> anyhow::Result<bool> {
let mut ignored_output = String::new();
- process_executor.execute_args(
+ Ok(process_executor.execute(
&[
"p4".to_string(),
"-p".to_string(),
@@ -90,7 +93,7 @@ impl Perforce {
],
&mut ignored_output,
Option::<&str>::None,
- ) == 0
+ )? == 0)
}
pub fn initialize(&mut self, repo_config: &IndexMap<String, PhpMixed>) {
diff --git a/crates/shirabe/src/util/platform.rs b/crates/shirabe/src/util/platform.rs
index c40f3ec5..ad530db3 100644
--- a/crates/shirabe/src/util/platform.rs
+++ b/crates/shirabe/src/util/platform.rs
@@ -310,7 +310,7 @@ impl Platform {
let mut process = ProcessExecutor::new(None);
let mut output = String::new();
let result: anyhow::Result<()> = (|| {
- if process.execute_args(&["lsmod".to_string()], &mut output, None) == 0
+ if process.execute(&["lsmod".to_string()], &mut output, None)? == 0
&& output.contains("vboxguest")
{
*cached = Some(true);
diff --git a/crates/shirabe/src/util/process_executor.rs b/crates/shirabe/src/util/process_executor.rs
index c1be0e18..eae6692b 100644
--- a/crates/shirabe/src/util/process_executor.rs
+++ b/crates/shirabe/src/util/process_executor.rs
@@ -160,32 +160,6 @@ impl ProcessExecutor {
self.do_execute(command, cwd, false, output)
}
- /// Convenience wrapper used by phase-A code that calls
- /// `process.execute(&[String], &mut String, Option<&str>) == 0`.
- /// Forwards to `execute`, returning the status code (1 on Err for compatibility) — this
- /// mirrors PHP call sites that check the `int` return of `execute()` without a surrounding
- /// `try`/`catch`, where an uncaught mock-mismatch exception would otherwise propagate.
- // TODO(mock): under a strict `ProcessExecutorMock`, an incomplete expectation list now
- // surfaces here as a swallowed "exit code 1" instead of the old `panic!`, so a future test
- // ported through this call site could silently take a wrong branch instead of failing loudly.
- // `ProcessExecutorMockGuard::__assert_complete` still catches unconsumed expectations at
- // scope exit, but not a mismatch that happened to consume nothing. Distinguishing "expectation
- // mismatch" from "real process failure" here would need a marker type incompatible with
- // `RuntimeException` (see `mock_match`'s doc comment) — deferred until a concrete test needs it.
- pub fn execute_args(
- &mut self,
- command: &[String],
- output: &mut String,
- cwd: Option<&str>,
- ) -> i64 {
- let mut buf = String::new();
- let rc = self
- .execute(CommandLine::Args(command.to_vec()), &mut buf, cwd)
- .unwrap_or(1);
- *output = buf;
- rc
- }
-
/// runs a process on the commandline in TTY mode
pub fn execute_tty<C>(&mut self, command: C, cwd: Option<&str>) -> anyhow::Result<i64>
where
@@ -984,6 +958,12 @@ impl IntoExecCommand for &[String] {
}
}
+impl<const N: usize> IntoExecCommand for &[String; N] {
+ fn into_exec_command(self) -> CommandLine {
+ CommandLine::Args(self.to_vec())
+ }
+}
+
/// Models the `mixed &$output` parameter of `ProcessExecutor::execute` (cf.
/// `composer/src/Composer/Util/ProcessExecutor.php`). In PHP the behaviour is selected by
/// `func_num_args()` and `is_callable($output)`; here each behaviour is a distinct implementing type:
diff --git a/crates/shirabe/src/util/svn.rs b/crates/shirabe/src/util/svn.rs
index b4f53470..aab90133 100644
--- a/crates/shirabe/src/util/svn.rs
+++ b/crates/shirabe/src/util/svn.rs
@@ -396,20 +396,20 @@ impl Svn {
}
/// Returns the version of the svn binary contained in PATH
- pub fn binary_version(&mut self) -> Option<String> {
+ pub fn binary_version(&mut self) -> anyhow::Result<Option<String>> {
let mut cached = VERSION.lock().unwrap();
if cached.is_none() {
let mut output = String::new();
- if 0 == self.process.borrow_mut().execute_args(
+ if 0 == self.process.borrow_mut().execute(
&["svn".to_string(), "--version".to_string()],
&mut output,
None,
- ) && let Some(matches) = preg_match(php_regex!(r"{(\d+(?:\.\d+)+)}"), &output)
+ )? && let Some(matches) = preg_match(php_regex!(r"{(\d+(?:\.\d+)+)}"), &output)
{
*cached = Some(matches.get(1).unwrap_or_default().to_string());
}
}
- cached.clone()
+ Ok(cached.clone())
}
}