diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-20 23:37:42 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-20 23:37:42 +0900 |
| commit | 84655bf3a4a21dbbe7aec8e3aee1e661505bbb6d (patch) | |
| tree | 189f8ce5d7d53246632d5e2419c392d58f06e2b8 /crates/shirabe/src | |
| parent | 10840ec1fd7f1140305449fc94175d60998572c1 (diff) | |
| download | php-shirabe-84655bf3a4a21dbbe7aec8e3aee1e661505bbb6d.tar.gz php-shirabe-84655bf3a4a21dbbe7aec8e3aee1e661505bbb6d.tar.zst php-shirabe-84655bf3a4a21dbbe7aec8e3aee1e661505bbb6d.zip | |
feat(symfony-process): port full Process class from PHP
Faithfully port every method, field and constant of Symfony's
Process.php into process.rs, replacing the reduced stub. Add the
supporting pipes module (PipesInterface/AbstractPipes/UnixPipes/
WindowsPipes), ProcessUtils and the missing process exceptions
(LogicException/InvalidArgumentException) with constructors.
Methods now return anyhow::Result where PHP throws, take the env
argument and a bool-returning callback, and borrow &mut for
status-updating accessors; all callers are updated accordingly.
Extend the php-shim with proc_open/proc_close (PHP-compatible
signatures), proc_get_status, proc_terminate, posix_kill, uniqid,
ftruncate, ftell_stream, fseek3, stream_get_contents3 and env
helpers.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src')
| -rw-r--r-- | crates/shirabe/src/downloader/zip_downloader.rs | 4 | ||||
| -rw-r--r-- | crates/shirabe/src/package/version/version_guesser.rs | 4 | ||||
| -rw-r--r-- | crates/shirabe/src/repository/vcs/perforce_driver.rs | 2 | ||||
| -rw-r--r-- | crates/shirabe/src/util/filesystem.rs | 2 | ||||
| -rw-r--r-- | crates/shirabe/src/util/perforce.rs | 37 | ||||
| -rw-r--r-- | crates/shirabe/src/util/process_executor.rs | 57 |
6 files changed, 68 insertions, 38 deletions
diff --git a/crates/shirabe/src/downloader/zip_downloader.rs b/crates/shirabe/src/downloader/zip_downloader.rs index 3d72377..d7508a6 100644 --- a/crates/shirabe/src/downloader/zip_downloader.rs +++ b/crates/shirabe/src/downloader/zip_downloader.rs @@ -135,7 +135,7 @@ impl ZipDownloader { .execute_async(&command, ()) .await; match process_result { - Ok(process) => { + Ok(mut process) => { if !process.is_successful() { if self.cleanup_executed.contains_key(&package.get_name()) { return Err(RuntimeException { @@ -148,7 +148,7 @@ impl ZipDownloader { .into()); } - let output = process.get_error_output(); + let output = process.get_error_output()?; let output = str_replace(&format!(", {}.zip or {}.ZIP", file, file), "", &output); diff --git a/crates/shirabe/src/package/version/version_guesser.rs b/crates/shirabe/src/package/version/version_guesser.rs index 42646cb..7f0625f 100644 --- a/crates/shirabe/src/package/version/version_guesser.rs +++ b/crates/shirabe/src/package/version/version_guesser.rs @@ -533,14 +533,14 @@ impl VersionGuesser { }, &scm_cmdline, ); - let process = tokio::runtime::Runtime::new() + let mut process = tokio::runtime::Runtime::new() .unwrap() .block_on(self.process.borrow_mut().execute_async(&cmd_line, path))?; if !process.is_successful() { continue; } - let output = process.get_output(); + let output = process.get_output()?; // overwrite existing if we have a shorter diff, or we have an equal diff and an index that comes later in the array (i.e. older version) // as newer versions typically have more commits, if the feature branch is based on a newer branch it should have a longer diff to the old version // but if it doesn't and they have equal diffs, then it probably is based on the old version diff --git a/crates/shirabe/src/repository/vcs/perforce_driver.rs b/crates/shirabe/src/repository/vcs/perforce_driver.rs index 3077cff..50d5eb9 100644 --- a/crates/shirabe/src/repository/vcs/perforce_driver.rs +++ b/crates/shirabe/src/repository/vcs/perforce_driver.rs @@ -60,7 +60,7 @@ impl PerforceDriver { self.perforce.as_mut().unwrap().p4_login()?; self.perforce.as_mut().unwrap().check_stream(); self.perforce.as_mut().unwrap().write_p4_client_spec()?; - self.perforce.as_mut().unwrap().connect_client(); + self.perforce.as_mut().unwrap().connect_client()?; Ok(()) } diff --git a/crates/shirabe/src/util/filesystem.rs b/crates/shirabe/src/util/filesystem.rs index 2d73422..7a4885a 100644 --- a/crates/shirabe/src/util/filesystem.rs +++ b/crates/shirabe/src/util/filesystem.rs @@ -157,7 +157,7 @@ impl Filesystem { vec!["rm".to_string(), "-rf".to_string(), directory.to_string()] }; - let process = self + let mut process = self .get_process() .execute_async( PhpMixed::List(cmd.iter().map(|s| PhpMixed::String(s.clone())).collect()), diff --git a/crates/shirabe/src/util/perforce.rs b/crates/shirabe/src/util/perforce.rs index 0d56afb..d5facd2 100644 --- a/crates/shirabe/src/util/perforce.rs +++ b/crates/shirabe/src/util/perforce.rs @@ -382,7 +382,7 @@ impl Perforce { Ok(true) } - pub fn connect_client(&mut self) { + pub fn connect_client(&mut self) -> Result<()> { let p4_create_client_command = self.generate_p4_command(vec!["client".to_string(), "-i".to_string()], true); @@ -390,10 +390,13 @@ impl Perforce { p4_create_client_command, None, None, - file_get_contents(&self.get_p4_client_spec()), + file_get_contents(&self.get_p4_client_spec()) + .map(PhpMixed::String) + .unwrap_or(PhpMixed::Null), None, - ); - process.run(None); + )?; + process.run(None, indexmap::IndexMap::new())?; + Ok(()) } pub fn sync_code_base(&mut self, source_reference: Option<&str>) -> Result<()> { @@ -531,12 +534,20 @@ impl Perforce { } } - pub fn windows_login(&mut self, password: Option<&str>) -> i64 { + pub fn windows_login(&mut self, password: Option<&str>) -> Result<i64> { let command = self.generate_p4_command(vec!["login".to_string(), "-a".to_string()], true); - let mut process = Process::new(command, None, None, password.map(|s| s.to_string()), None); + let mut process = Process::new( + command, + None, + None, + password + .map(|s| PhpMixed::String(s.to_string())) + .unwrap_or(PhpMixed::Null), + None, + )?; - process.run(None) + process.run(None, indexmap::IndexMap::new()) } pub fn p4_login(&mut self) -> Result<()> { @@ -544,13 +555,19 @@ impl Perforce { if !self.is_logged_in()? { let password = self.query_p4_password(); if self.windows_flag { - self.windows_login(password.as_deref()); + self.windows_login(password.as_deref())?; } else { let command = self.generate_p4_command(vec!["login".to_string(), "-a".to_string()], false); - let mut process = Process::new(command, None, None, password, None); - process.run(None); + let mut process = Process::new( + command, + None, + None, + password.map(PhpMixed::String).unwrap_or(PhpMixed::Null), + None, + )?; + process.run(None, indexmap::IndexMap::new())?; if !process.is_successful() { return Err(Exception { diff --git a/crates/shirabe/src/util/process_executor.rs b/crates/shirabe/src/util/process_executor.rs index 2bdf065..61fc0a1 100644 --- a/crates/shirabe/src/util/process_executor.rs +++ b/crates/shirabe/src/util/process_executor.rs @@ -221,9 +221,9 @@ impl ProcessExecutor { &command_str, cwd, env.clone(), - None, + PhpMixed::Null, Some(Self::get_timeout() as f64), - ); + )?; } else if let PhpMixed::List(ref list) = command { let mut cmd_vec: Vec<String> = list .iter() @@ -238,9 +238,9 @@ impl ProcessExecutor { cmd_vec, cwd.map(String::from), env, - None, + PhpMixed::Null, Some(Self::get_timeout() as f64), - ); + )?; } else { return Err(LogicException { message: "Invalid command type".to_string(), @@ -291,20 +291,23 @@ impl ProcessExecutor { }), ); - let result: Result<()> = { - let _ = process.run(/* callback */ Some(Box::new(|_t: &str, _b: &str| {}))); + let result: Result<()> = (|| -> Result<()> { + process.run( + /* callback */ Some(Box::new(|_t: &str, _b: &str| false)), + IndexMap::new(), + )?; let output_is_callable_inner = output.as_deref().map(is_callable).unwrap_or(false); if self.capture_output && !output_is_callable_inner && let Some(out) = output.as_mut() { - **out = PhpMixed::String(process.get_output()); + **out = PhpMixed::String(process.get_output()?); } - self.error_output = process.get_error_output(); + self.error_output = process.get_error_output()?; Ok(()) - }; + })(); let final_result: Result<()> = match result { Ok(()) => Ok(()), Err(e) => { @@ -467,23 +470,23 @@ impl ProcessExecutor { self.output_command_run(&command, cwd.as_deref(), true); let process_result: Result<Process> = (if is_string(&command) { - Ok(Process::from_shell_commandline( + Process::from_shell_commandline( command.as_string().unwrap_or(""), cwd.as_deref(), None, - None, + PhpMixed::Null, Some(Self::get_timeout() as f64), - )) + ) } else if let PhpMixed::List(ref list) = command { - Ok(Process::new( + Process::new( list.iter() .map(|v| v.as_string().unwrap_or("").to_string()) .collect(), cwd.clone(), None, - None, + PhpMixed::Null, Some(Self::get_timeout() as f64), - )) + ) } else { Err(LogicException { message: "Invalid command type".to_string(), @@ -509,10 +512,20 @@ impl ProcessExecutor { } // PHP: $process->start($callback); — we operate on the stored job.process directly - if let Some(job) = self.jobs.get_mut(&id) + let start_result = if let Some(job) = self.jobs.get_mut(&id) && let Some(p) = job.process.as_mut() { - p.start(None); + p.start(None, IndexMap::new()) + } else { + Ok(()) + }; + if let Err(e) = start_result { + // PHP: $job['reject']($e) — record the rejection and settle the job as failed. + if let Some(job) = self.jobs.get_mut(&id) { + job.status = Self::STATUS_FAILED; + job.exception = Some(e); + } + self.mark_job_done(); } } @@ -571,8 +584,8 @@ impl ProcessExecutor { if status == Self::STATUS_STARTED && has_process { let is_running = self .jobs - .get(id) - .and_then(|j| j.process.as_ref()) + .get_mut(id) + .and_then(|j| j.process.as_mut()) .map(|p| p.is_running()) .unwrap_or(false); if !is_running { @@ -580,8 +593,8 @@ impl ProcessExecutor { // marks the job completed/failed based on the process exit status. let successful = self .jobs - .get(id) - .and_then(|j| j.process.as_ref()) + .get_mut(id) + .and_then(|j| j.process.as_mut()) .map(|p| p.is_successful()) .unwrap_or(false); if let Some(job) = self.jobs.get_mut(id) { @@ -594,7 +607,7 @@ impl ProcessExecutor { self.mark_job_done(); } - if let Some(p) = self.jobs.get(id).and_then(|j| j.process.as_ref()) { + if let Some(p) = self.jobs.get_mut(id).and_then(|j| j.process.as_mut()) { p.check_timeout()?; } } |
