From 1176d51af35e2dd4bfe0a232ab9403a0e99740d4 Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Sun, 20 Oct 2024 16:08:02 +0100 Subject: [PATCH 01/31] wip --- src/ChildProcess/ChildProcess.php | 91 ++++++++++++++++++++++ src/ChildProcess/ChildProcessManager.php | 24 ++++++ src/Events/ChildProcess/ProcessSpawned.php | 22 ++++++ src/Facades/ChildProcess.php | 18 +++++ 4 files changed, 155 insertions(+) create mode 100644 src/ChildProcess/ChildProcess.php create mode 100644 src/ChildProcess/ChildProcessManager.php create mode 100644 src/Events/ChildProcess/ProcessSpawned.php create mode 100644 src/Facades/ChildProcess.php diff --git a/src/ChildProcess/ChildProcess.php b/src/ChildProcess/ChildProcess.php new file mode 100644 index 0000000..9afce01 --- /dev/null +++ b/src/ChildProcess/ChildProcess.php @@ -0,0 +1,91 @@ +alias = $alias; + + $this->process = $this->client->post('child-process/start', [ + 'alias' => $alias, + 'cmd' => $cmd, + 'cwd' => base_path(), + 'env' => $env, + ])->json(); + + return $this; + } + + public function stop(string $alias): void + { + $this->client->post('child-process/stop', [ + 'alias' => $alias, + ])->json(); + } + + public function message(string $alias, mixed $message): void + { + $this->client->post('child-process/message', [ + 'alias' => $alias, + 'message' => json_encode($message), + ])->json(); + } + + public function onMessage(\Closure $callback) { + // Event::listen(function (MessageReceived $event) use ($callback) { + // if ($event->alias !== $this->alias) { + // return; + // } + // + // $callback($event); + // ); + return $this; + } + + public function onError(\Closure $callback) { + // Event::listen(function (ErrorReceived $event) use ($callback) { + // if ($event->alias !== $this->alias) { + // return; + // } + // + // $callback($event); + // ); + return $this; + } + + public function onSpawn(\Closure $callback) { + // Event::listen(function (ProcessSpawned $event) use ($callback) { + // if ($event->alias !== $this->alias) { + // return; + // } + // + // $callback($event); + // ); + return $this; + } + + public function onExit(\Closure $callback) { + // Event::listen(function (ProcessExited $event) use ($callback) { + // if ($event->alias !== $this->alias) { + // return; + // } + // + // $callback($event); + // ); + return $this; + } +} diff --git a/src/ChildProcess/ChildProcessManager.php b/src/ChildProcess/ChildProcessManager.php new file mode 100644 index 0000000..a1c1daf --- /dev/null +++ b/src/ChildProcess/ChildProcessManager.php @@ -0,0 +1,24 @@ +process = $this->client->post('child-process/start')->json([ + 'alias' => $alias, + 'cmd' => $cmd, + 'cwd' => $cwd, + 'env' => $env, + ]); + } + + public function onMessage() { + + } +} diff --git a/src/Events/ChildProcess/ProcessSpawned.php b/src/Events/ChildProcess/ProcessSpawned.php new file mode 100644 index 0000000..4cb089f --- /dev/null +++ b/src/Events/ChildProcess/ProcessSpawned.php @@ -0,0 +1,22 @@ + Date: Sun, 20 Oct 2024 15:08:30 +0000 Subject: [PATCH 02/31] Fix styling --- src/ChildProcess/ChildProcess.php | 17 +++++++++++------ src/ChildProcess/ChildProcessManager.php | 6 ++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/ChildProcess/ChildProcess.php b/src/ChildProcess/ChildProcess.php index 9afce01..2049bc5 100644 --- a/src/ChildProcess/ChildProcess.php +++ b/src/ChildProcess/ChildProcess.php @@ -6,17 +6,18 @@ use Native\Laravel\Client\Client; use Native\Laravel\Events\ChildProcess\ErrorReceived; use Native\Laravel\Events\ChildProcess\MessageReceived; -use Native\Laravel\Events\ChildProcess\ProcessSpawned; use Native\Laravel\Events\ChildProcess\ProcessExited; +use Native\Laravel\Events\ChildProcess\ProcessSpawned; class ChildProcess { private string $alias; + private ?array $process; public function __construct(protected Client $client) {} - public function start(string $alias, array $cmd, string $cwd = null, array $env = null): object + public function start(string $alias, array $cmd, ?string $cwd = null, ?array $env = null): object { $this->alias = $alias; @@ -45,7 +46,8 @@ public function message(string $alias, mixed $message): void ])->json(); } - public function onMessage(\Closure $callback) { + public function onMessage(\Closure $callback) + { // Event::listen(function (MessageReceived $event) use ($callback) { // if ($event->alias !== $this->alias) { // return; @@ -56,7 +58,8 @@ public function onMessage(\Closure $callback) { return $this; } - public function onError(\Closure $callback) { + public function onError(\Closure $callback) + { // Event::listen(function (ErrorReceived $event) use ($callback) { // if ($event->alias !== $this->alias) { // return; @@ -67,7 +70,8 @@ public function onError(\Closure $callback) { return $this; } - public function onSpawn(\Closure $callback) { + public function onSpawn(\Closure $callback) + { // Event::listen(function (ProcessSpawned $event) use ($callback) { // if ($event->alias !== $this->alias) { // return; @@ -78,7 +82,8 @@ public function onSpawn(\Closure $callback) { return $this; } - public function onExit(\Closure $callback) { + public function onExit(\Closure $callback) + { // Event::listen(function (ProcessExited $event) use ($callback) { // if ($event->alias !== $this->alias) { // return; diff --git a/src/ChildProcess/ChildProcessManager.php b/src/ChildProcess/ChildProcessManager.php index a1c1daf..5615d62 100644 --- a/src/ChildProcess/ChildProcessManager.php +++ b/src/ChildProcess/ChildProcessManager.php @@ -8,7 +8,7 @@ class ChildProcessManager { public function __construct(protected Client $client) {} - public function start(string $alias, array $cmd, string $cwd = null, array $env = null): object + public function start(string $alias, array $cmd, ?string $cwd = null, ?array $env = null): object { $this->process = $this->client->post('child-process/start')->json([ 'alias' => $alias, @@ -18,7 +18,5 @@ public function start(string $alias, array $cmd, string $cwd = null, array $env ]); } - public function onMessage() { - - } + public function onMessage() {} } From d55b42b298de503ae3d5a3219b6d049e337cba9e Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Sun, 20 Oct 2024 16:09:54 +0100 Subject: [PATCH 03/31] cleanup --- src/{ChildProcess => }/ChildProcess.php | 2 +- src/ChildProcess/ChildProcessManager.php | 22 ---------------------- 2 files changed, 1 insertion(+), 23 deletions(-) rename src/{ChildProcess => }/ChildProcess.php (98%) delete mode 100644 src/ChildProcess/ChildProcessManager.php diff --git a/src/ChildProcess/ChildProcess.php b/src/ChildProcess.php similarity index 98% rename from src/ChildProcess/ChildProcess.php rename to src/ChildProcess.php index 2049bc5..0841130 100644 --- a/src/ChildProcess/ChildProcess.php +++ b/src/ChildProcess.php @@ -1,6 +1,6 @@ process = $this->client->post('child-process/start')->json([ - 'alias' => $alias, - 'cmd' => $cmd, - 'cwd' => $cwd, - 'env' => $env, - ]); - } - - public function onMessage() {} -} From 624fbc5ea6c2da77ad6c3cf8234e9a9110c6789e Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Mon, 21 Oct 2024 13:05:47 +0100 Subject: [PATCH 04/31] phpstan --- phpstan.neon.dist | 1 - src/Commands/MigrateCommand.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index a91953b..260b5e1 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -10,5 +10,4 @@ parameters: tmpDir: build/phpstan checkOctaneCompatibility: true checkModelProperties: true - checkMissingIterableValueType: false diff --git a/src/Commands/MigrateCommand.php b/src/Commands/MigrateCommand.php index ebf8522..9093d67 100644 --- a/src/Commands/MigrateCommand.php +++ b/src/Commands/MigrateCommand.php @@ -22,6 +22,6 @@ public function handle() { (new NativeServiceProvider($this->laravel))->rewriteDatabase(); - parent::handle(); + return parent::handle(); } } From 83d3706fa606dc53c0a7c87a153e446498987bfc Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Tue, 22 Oct 2024 22:25:33 +0100 Subject: [PATCH 05/31] Fix event --- src/Events/ChildProcess/ProcessSpawned.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Events/ChildProcess/ProcessSpawned.php b/src/Events/ChildProcess/ProcessSpawned.php index 4cb089f..91fc917 100644 --- a/src/Events/ChildProcess/ProcessSpawned.php +++ b/src/Events/ChildProcess/ProcessSpawned.php @@ -11,7 +11,7 @@ class ProcessSpawned implements ShouldBroadcast { use Dispatchable, SerializesModels; - public function __construct(public array $item) {} + public function __construct(public string $alias) {} public function broadcastOn() { From 927f6e50d971a7172d887e67eb1216976cdc37f3 Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Tue, 22 Oct 2024 22:25:57 +0100 Subject: [PATCH 06/31] Fix event watcher --- src/Events/EventWatcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Events/EventWatcher.php b/src/Events/EventWatcher.php index b7f57c5..bab9373 100644 --- a/src/Events/EventWatcher.php +++ b/src/Events/EventWatcher.php @@ -13,7 +13,7 @@ public function register(): void { Event::listen('*', function (string $eventName, array $data) { - $event = $data[0] ?? null; + $event = $data[0] ?? (object) null; if (! method_exists($event, 'broadcastOn')) { return; From 3d776c11d3ee4369d94c08d32bbff2eb89d33467 Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Tue, 22 Oct 2024 22:26:05 +0100 Subject: [PATCH 07/31] Fix facade --- src/Facades/ChildProcess.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Facades/ChildProcess.php b/src/Facades/ChildProcess.php index 66a8f4e..8730f80 100644 --- a/src/Facades/ChildProcess.php +++ b/src/Facades/ChildProcess.php @@ -13,6 +13,6 @@ class ChildProcess extends Facade { protected static function getFacadeAccessor() { - return \Native\Laravel\ChildProcess\ChildProcess::class; + return \Native\Laravel\ChildProcess::class; } } From a99d2724ea24707868313973e8c6f9585e26b885 Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Thu, 24 Oct 2024 20:37:08 +0100 Subject: [PATCH 08/31] add events --- src/Events/ChildProcess/ErrorReceived.php | 22 +++++++++++++++++++++ src/Events/ChildProcess/MessageReceived.php | 22 +++++++++++++++++++++ src/Events/ChildProcess/ProcessExited.php | 22 +++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 src/Events/ChildProcess/ErrorReceived.php create mode 100644 src/Events/ChildProcess/MessageReceived.php create mode 100644 src/Events/ChildProcess/ProcessExited.php diff --git a/src/Events/ChildProcess/ErrorReceived.php b/src/Events/ChildProcess/ErrorReceived.php new file mode 100644 index 0000000..65db9c6 --- /dev/null +++ b/src/Events/ChildProcess/ErrorReceived.php @@ -0,0 +1,22 @@ + Date: Thu, 24 Oct 2024 22:18:26 +0100 Subject: [PATCH 09/31] Remove useless stubs --- src/ChildProcess.php | 48 -------------------------------------------- 1 file changed, 48 deletions(-) diff --git a/src/ChildProcess.php b/src/ChildProcess.php index 0841130..6bfba81 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -45,52 +45,4 @@ public function message(string $alias, mixed $message): void 'message' => json_encode($message), ])->json(); } - - public function onMessage(\Closure $callback) - { - // Event::listen(function (MessageReceived $event) use ($callback) { - // if ($event->alias !== $this->alias) { - // return; - // } - // - // $callback($event); - // ); - return $this; - } - - public function onError(\Closure $callback) - { - // Event::listen(function (ErrorReceived $event) use ($callback) { - // if ($event->alias !== $this->alias) { - // return; - // } - // - // $callback($event); - // ); - return $this; - } - - public function onSpawn(\Closure $callback) - { - // Event::listen(function (ProcessSpawned $event) use ($callback) { - // if ($event->alias !== $this->alias) { - // return; - // } - // - // $callback($event); - // ); - return $this; - } - - public function onExit(\Closure $callback) - { - // Event::listen(function (ProcessExited $event) use ($callback) { - // if ($event->alias !== $this->alias) { - // return; - // } - // - // $callback($event); - // ); - return $this; - } } From 233675a6f724f3a5fa79bae046bc4cb762ede237 Mon Sep 17 00:00:00 2001 From: simonhamp Date: Thu, 24 Oct 2024 21:18:56 +0000 Subject: [PATCH 10/31] Fix styling --- src/ChildProcess.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/ChildProcess.php b/src/ChildProcess.php index 6bfba81..4d4794c 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -2,12 +2,7 @@ namespace Native\Laravel; -use Illuminate\Support\Facades\Event; use Native\Laravel\Client\Client; -use Native\Laravel\Events\ChildProcess\ErrorReceived; -use Native\Laravel\Events\ChildProcess\MessageReceived; -use Native\Laravel\Events\ChildProcess\ProcessExited; -use Native\Laravel\Events\ChildProcess\ProcessSpawned; class ChildProcess { From 6cf848889d4d4077ce97605552e0215a85efea0f Mon Sep 17 00:00:00 2001 From: gwleuverink Date: Fri, 25 Oct 2024 10:55:13 +0200 Subject: [PATCH 11/31] add some sanity tests --- src/ChildProcess.php | 4 +- tests/ChildProcess/ChildProcessTest.php | 52 +++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 tests/ChildProcess/ChildProcessTest.php diff --git a/src/ChildProcess.php b/src/ChildProcess.php index 4d4794c..f438fca 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -16,10 +16,12 @@ public function start(string $alias, array $cmd, ?string $cwd = null, ?array $en { $this->alias = $alias; + $cwd = $cwd ?? base_path(); + $this->process = $this->client->post('child-process/start', [ 'alias' => $alias, 'cmd' => $cmd, - 'cwd' => base_path(), + 'cwd' => $cwd, 'env' => $env, ])->json(); diff --git a/tests/ChildProcess/ChildProcessTest.php b/tests/ChildProcess/ChildProcessTest.php new file mode 100644 index 0000000..1a6b6ce --- /dev/null +++ b/tests/ChildProcess/ChildProcessTest.php @@ -0,0 +1,52 @@ + 'zah']); + + Http::assertSent(function (Request $request) { + return $request->url() === 'http://localhost:4000/api/child-process/start' && + $request['alias'] === 'some-alias' && + $request['cmd'] === ['foo', 'bar'] && + $request['cwd'] === 'path/to/dir' && + $request['env'] === ['baz' => 'zah']; + }); +}); + +it('can start a artisan command', function () {})->todo(); + +it('accepts either a string or a array as command input')->todo(); + +it('sets the cwd to the base path if none was given', function () { + ChildProcess::start('some-alias', ['foo', 'bar'], cwd: 'path/to/dir'); + Http::assertSent(fn (Request $request) => $request['cwd'] === 'path/to/dir'); + + ChildProcess::start('some-alias', ['foo', 'bar']); + Http::assertSent(fn (Request $request) => $request['cwd'] === base_path()); +}); + +it('can stop a child process', function () { + ChildProcess::stop('some-alias'); + + Http::assertSent(function (Request $request) { + return $request->url() === 'http://localhost:4000/api/child-process/stop' && + $request['alias'] === 'some-alias'; + }); +}); + +it('can send messages to a child process', function () { + ChildProcess::message('some-alias', 'some-message'); + + Http::assertSent(function (Request $request) { + return $request->url() === 'http://localhost:4000/api/child-process/message' && + $request['alias'] === 'some-alias' && + $request['message'] === '"some-message"'; + }); +}); From 1e673bb30ff5fa4693bbb0f2df9593ef7abc39c6 Mon Sep 17 00:00:00 2001 From: gwleuverink Date: Fri, 25 Oct 2024 11:31:43 +0200 Subject: [PATCH 12/31] wip --- tests/ChildProcess/ChildProcessTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/ChildProcess/ChildProcessTest.php b/tests/ChildProcess/ChildProcessTest.php index 1a6b6ce..9f8c309 100644 --- a/tests/ChildProcess/ChildProcessTest.php +++ b/tests/ChildProcess/ChildProcessTest.php @@ -20,7 +20,9 @@ }); }); -it('can start a artisan command', function () {})->todo(); +it('can start a artisan command')->todo(); + +it('can mark the process as persistent')->todo(); it('accepts either a string or a array as command input')->todo(); From 7a4f4077636336d8b6582ca5111933b950a6ac77 Mon Sep 17 00:00:00 2001 From: gwleuverink Date: Fri, 25 Oct 2024 14:38:20 +0200 Subject: [PATCH 13/31] add artisan shorthand --- src/ChildProcess.php | 25 +++++++++++++++++++++++++ tests/ChildProcess/ChildProcessTest.php | 16 ++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/ChildProcess.php b/src/ChildProcess.php index f438fca..28298b8 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -28,6 +28,31 @@ public function start(string $alias, array $cmd, ?string $cwd = null, ?array $en return $this; } + public function artisan(string $alias, array $cmd, ?array $env = null): object + { + $cmd = array_merge([PHP_BINARY, 'artisan'], $cmd); + + return $this->start( + $alias, + $cmd, + base_path(), + $env + ); + + $this->alias = $alias; + + $cwd = $cwd ?? base_path(); + + $this->process = $this->client->post('child-process/start', [ + 'alias' => $alias, + 'cmd' => $cmd, + 'cwd' => $cwd, + 'env' => $env, + ])->json(); + + return $this; + } + public function stop(string $alias): void { $this->client->post('child-process/stop', [ diff --git a/tests/ChildProcess/ChildProcessTest.php b/tests/ChildProcess/ChildProcessTest.php index 9f8c309..882b1d3 100644 --- a/tests/ChildProcess/ChildProcessTest.php +++ b/tests/ChildProcess/ChildProcessTest.php @@ -20,11 +20,23 @@ }); }); -it('can start a artisan command')->todo(); +it('can start a artisan command', function () { + ChildProcess::artisan('some-alias', ['foo:bar'], ['baz' => 'zah']); + + Http::assertSent(function (Request $request) { + return $request->url() === 'http://localhost:4000/api/child-process/start' && + $request['alias'] === 'some-alias' && + $request['cmd'] === [PHP_BINARY, 'artisan', 'foo:bar'] && + $request['cwd'] === base_path() && + $request['env'] === ['baz' => 'zah']; + }); +}); it('can mark the process as persistent')->todo(); -it('accepts either a string or a array as command input')->todo(); +it('start method accepts either a string or a array as command input')->todo(); + +it('artisan method accepts either a string or a array as command input')->todo(); it('sets the cwd to the base path if none was given', function () { ChildProcess::start('some-alias', ['foo', 'bar'], cwd: 'path/to/dir'); From 6e4f7db9c01382b589f889211c9b7af6fb4915a5 Mon Sep 17 00:00:00 2001 From: gwleuverink Date: Fri, 25 Oct 2024 14:58:11 +0200 Subject: [PATCH 14/31] allow passing either a string or array --- src/ChildProcess.php | 32 ++++++++----------------- tests/ChildProcess/ChildProcessTest.php | 32 +++++++++++++++++++++---- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/ChildProcess.php b/src/ChildProcess.php index 28298b8..fc33eb6 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -12,12 +12,18 @@ class ChildProcess public function __construct(protected Client $client) {} - public function start(string $alias, array $cmd, ?string $cwd = null, ?array $env = null): object + public function start(string $alias, string|array $cmd, ?string $cwd = null, ?array $env = null): object { $this->alias = $alias; $cwd = $cwd ?? base_path(); + $cmd = is_iterable($cmd) + // when an array is passed, escape spaces for each item + ? array_map(fn ($a) => str_replace(' ', '\ ', $a), $cmd) + // when a string is passed, explode it on the space + : array_values(array_filter(explode(' ', $cmd))); + $this->process = $this->client->post('child-process/start', [ 'alias' => $alias, 'cmd' => $cmd, @@ -28,29 +34,11 @@ public function start(string $alias, array $cmd, ?string $cwd = null, ?array $en return $this; } - public function artisan(string $alias, array $cmd, ?array $env = null): object + public function artisan(string $alias, string|array $cmd, ?array $env = null): object { - $cmd = array_merge([PHP_BINARY, 'artisan'], $cmd); - - return $this->start( - $alias, - $cmd, - base_path(), - $env - ); - - $this->alias = $alias; - - $cwd = $cwd ?? base_path(); - - $this->process = $this->client->post('child-process/start', [ - 'alias' => $alias, - 'cmd' => $cmd, - 'cwd' => $cwd, - 'env' => $env, - ])->json(); + $cmd = [PHP_BINARY, 'artisan', ...(array) $cmd]; - return $this; + return $this->start($alias, $cmd, env: $env); } public function stop(string $alias): void diff --git a/tests/ChildProcess/ChildProcessTest.php b/tests/ChildProcess/ChildProcessTest.php index 882b1d3..336dbb4 100644 --- a/tests/ChildProcess/ChildProcessTest.php +++ b/tests/ChildProcess/ChildProcessTest.php @@ -9,7 +9,7 @@ }); it('can start a child process', function () { - ChildProcess::start('some-alias', ['foo', 'bar'], 'path/to/dir', ['baz' => 'zah']); + ChildProcess::start('some-alias', 'foo bar', 'path/to/dir', ['baz' => 'zah']); Http::assertSent(function (Request $request) { return $request->url() === 'http://localhost:4000/api/child-process/start' && @@ -21,12 +21,12 @@ }); it('can start a artisan command', function () { - ChildProcess::artisan('some-alias', ['foo:bar'], ['baz' => 'zah']); + ChildProcess::artisan('some-alias', 'foo:bar', ['baz' => 'zah']); Http::assertSent(function (Request $request) { return $request->url() === 'http://localhost:4000/api/child-process/start' && $request['alias'] === 'some-alias' && - $request['cmd'] === [PHP_BINARY, 'artisan', 'foo:bar'] && + $request['cmd'] === [str_replace(' ', '\ ', PHP_BINARY), 'artisan', 'foo:bar'] && $request['cwd'] === base_path() && $request['env'] === ['baz' => 'zah']; }); @@ -34,9 +34,21 @@ it('can mark the process as persistent')->todo(); -it('start method accepts either a string or a array as command input')->todo(); +it('accepts either a string or a array as start command argument', function () { + ChildProcess::start('some-alias', 'foo bar'); + Http::assertSent(fn (Request $request) => $request['cmd'] === ['foo', 'bar']); -it('artisan method accepts either a string or a array as command input')->todo(); + ChildProcess::start('some-alias', ['foo', 'baz']); + Http::assertSent(fn (Request $request) => $request['cmd'] === ['foo', 'baz']); +}); + +it('accepts either a string or a array as artisan command argument', function () { + ChildProcess::artisan('some-alias', 'foo:bar'); + Http::assertSent(fn (Request $request) => $request['cmd'] === [str_replace(' ', '\ ', PHP_BINARY), 'artisan', 'foo:bar']); + + ChildProcess::artisan('some-alias', ['foo:baz']); + Http::assertSent(fn (Request $request) => $request['cmd'] === [str_replace(' ', '\ ', PHP_BINARY), 'artisan', 'foo:baz']); +}); it('sets the cwd to the base path if none was given', function () { ChildProcess::start('some-alias', ['foo', 'bar'], cwd: 'path/to/dir'); @@ -46,6 +58,16 @@ Http::assertSent(fn (Request $request) => $request['cwd'] === base_path()); }); +it('filters double spaces when exploding a command string', function () { + ChildProcess::start('some-alias', 'foo bar baz bak'); + Http::assertSent(fn (Request $request) => $request['cmd'] === ['foo', 'bar', 'baz', 'bak']); +}); + +it('escapes spaces when passing a command array', function () { + ChildProcess::start('some-alias', ['path/to/some executable with spaces.sh', '--foo', '--bar']); + Http::assertSent(fn (Request $request) => $request['cmd'] === ['path/to/some\ executable\ with\ spaces.sh', '--foo', '--bar']); +}); + it('can stop a child process', function () { ChildProcess::stop('some-alias'); From ee53a67356f633518100798ad23ddd8eca2044e1 Mon Sep 17 00:00:00 2001 From: gwleuverink Date: Fri, 25 Oct 2024 15:00:06 +0200 Subject: [PATCH 15/31] correct return type --- src/ChildProcess.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ChildProcess.php b/src/ChildProcess.php index fc33eb6..ceb231e 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -12,7 +12,7 @@ class ChildProcess public function __construct(protected Client $client) {} - public function start(string $alias, string|array $cmd, ?string $cwd = null, ?array $env = null): object + public function start(string $alias, string|array $cmd, ?string $cwd = null, ?array $env = null): self { $this->alias = $alias; @@ -34,7 +34,7 @@ public function start(string $alias, string|array $cmd, ?string $cwd = null, ?ar return $this; } - public function artisan(string $alias, string|array $cmd, ?array $env = null): object + public function artisan(string $alias, string|array $cmd, ?array $env = null): self { $cmd = [PHP_BINARY, 'artisan', ...(array) $cmd]; From 8b342fdb283078d954bfe7c94d73fcd8841e9900 Mon Sep 17 00:00:00 2001 From: gwleuverink Date: Tue, 29 Oct 2024 08:12:16 +0100 Subject: [PATCH 16/31] flip arguments for consistency --- src/ChildProcess.php | 10 +++++----- tests/ChildProcess/ChildProcessTest.php | 22 +++++++++++----------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/ChildProcess.php b/src/ChildProcess.php index ceb231e..e023002 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -12,7 +12,7 @@ class ChildProcess public function __construct(protected Client $client) {} - public function start(string $alias, string|array $cmd, ?string $cwd = null, ?array $env = null): self + public function start(string|array $cmd, string $alias, ?string $cwd = null, ?array $env = null): self { $this->alias = $alias; @@ -34,11 +34,11 @@ public function start(string $alias, string|array $cmd, ?string $cwd = null, ?ar return $this; } - public function artisan(string $alias, string|array $cmd, ?array $env = null): self + public function artisan(string|array $cmd, string $alias, ?array $env = null): self { $cmd = [PHP_BINARY, 'artisan', ...(array) $cmd]; - return $this->start($alias, $cmd, env: $env); + return $this->start($cmd, $alias, env: $env); } public function stop(string $alias): void @@ -48,11 +48,11 @@ public function stop(string $alias): void ])->json(); } - public function message(string $alias, mixed $message): void + public function message(mixed $message, string $alias): void { $this->client->post('child-process/message', [ - 'alias' => $alias, 'message' => json_encode($message), + 'alias' => $alias, ])->json(); } } diff --git a/tests/ChildProcess/ChildProcessTest.php b/tests/ChildProcess/ChildProcessTest.php index 336dbb4..66a3a17 100644 --- a/tests/ChildProcess/ChildProcessTest.php +++ b/tests/ChildProcess/ChildProcessTest.php @@ -9,7 +9,7 @@ }); it('can start a child process', function () { - ChildProcess::start('some-alias', 'foo bar', 'path/to/dir', ['baz' => 'zah']); + ChildProcess::start('foo bar', 'some-alias', 'path/to/dir', ['baz' => 'zah']); Http::assertSent(function (Request $request) { return $request->url() === 'http://localhost:4000/api/child-process/start' && @@ -21,7 +21,7 @@ }); it('can start a artisan command', function () { - ChildProcess::artisan('some-alias', 'foo:bar', ['baz' => 'zah']); + ChildProcess::artisan('foo:bar', 'some-alias', ['baz' => 'zah']); Http::assertSent(function (Request $request) { return $request->url() === 'http://localhost:4000/api/child-process/start' && @@ -35,36 +35,36 @@ it('can mark the process as persistent')->todo(); it('accepts either a string or a array as start command argument', function () { - ChildProcess::start('some-alias', 'foo bar'); + ChildProcess::start('foo bar', 'some-alias'); Http::assertSent(fn (Request $request) => $request['cmd'] === ['foo', 'bar']); - ChildProcess::start('some-alias', ['foo', 'baz']); + ChildProcess::start(['foo', 'baz'], 'some-alias'); Http::assertSent(fn (Request $request) => $request['cmd'] === ['foo', 'baz']); }); it('accepts either a string or a array as artisan command argument', function () { - ChildProcess::artisan('some-alias', 'foo:bar'); + ChildProcess::artisan('foo:bar', 'some-alias'); Http::assertSent(fn (Request $request) => $request['cmd'] === [str_replace(' ', '\ ', PHP_BINARY), 'artisan', 'foo:bar']); - ChildProcess::artisan('some-alias', ['foo:baz']); + ChildProcess::artisan(['foo:baz'], 'some-alias'); Http::assertSent(fn (Request $request) => $request['cmd'] === [str_replace(' ', '\ ', PHP_BINARY), 'artisan', 'foo:baz']); }); it('sets the cwd to the base path if none was given', function () { - ChildProcess::start('some-alias', ['foo', 'bar'], cwd: 'path/to/dir'); + ChildProcess::start(['foo', 'bar'], 'some-alias', cwd: 'path/to/dir'); Http::assertSent(fn (Request $request) => $request['cwd'] === 'path/to/dir'); - ChildProcess::start('some-alias', ['foo', 'bar']); + ChildProcess::start(['foo', 'bar'], 'some-alias'); Http::assertSent(fn (Request $request) => $request['cwd'] === base_path()); }); it('filters double spaces when exploding a command string', function () { - ChildProcess::start('some-alias', 'foo bar baz bak'); + ChildProcess::start('foo bar baz bak', 'some-alias'); Http::assertSent(fn (Request $request) => $request['cmd'] === ['foo', 'bar', 'baz', 'bak']); }); it('escapes spaces when passing a command array', function () { - ChildProcess::start('some-alias', ['path/to/some executable with spaces.sh', '--foo', '--bar']); + ChildProcess::start(['path/to/some executable with spaces.sh', '--foo', '--bar'], 'some-alias'); Http::assertSent(fn (Request $request) => $request['cmd'] === ['path/to/some\ executable\ with\ spaces.sh', '--foo', '--bar']); }); @@ -78,7 +78,7 @@ }); it('can send messages to a child process', function () { - ChildProcess::message('some-alias', 'some-message'); + ChildProcess::message('some-message', 'some-alias'); Http::assertSent(function (Request $request) { return $request->url() === 'http://localhost:4000/api/child-process/message' && From bc46e8c2d95fd20c76d1213a4ac318779ee9e9d9 Mon Sep 17 00:00:00 2001 From: gwleuverink Date: Tue, 29 Oct 2024 08:13:24 +0100 Subject: [PATCH 17/31] tidy - remove unused class properties --- src/ChildProcess.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/ChildProcess.php b/src/ChildProcess.php index e023002..11018ec 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -6,16 +6,10 @@ class ChildProcess { - private string $alias; - - private ?array $process; - public function __construct(protected Client $client) {} public function start(string|array $cmd, string $alias, ?string $cwd = null, ?array $env = null): self { - $this->alias = $alias; - $cwd = $cwd ?? base_path(); $cmd = is_iterable($cmd) @@ -24,7 +18,7 @@ public function start(string|array $cmd, string $alias, ?string $cwd = null, ?ar // when a string is passed, explode it on the space : array_values(array_filter(explode(' ', $cmd))); - $this->process = $this->client->post('child-process/start', [ + $this->client->post('child-process/start', [ 'alias' => $alias, 'cmd' => $cmd, 'cwd' => $cwd, From 8af94f61f5a4e9489094c6e0462c9183f70591e1 Mon Sep 17 00:00:00 2001 From: gwleuverink Date: Tue, 29 Oct 2024 08:20:06 +0100 Subject: [PATCH 18/31] remove unnecessary space escape --- src/ChildProcess.php | 9 ++++----- tests/ChildProcess/ChildProcessTest.php | 11 +++-------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/ChildProcess.php b/src/ChildProcess.php index 11018ec..e5ce2d9 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -12,11 +12,10 @@ public function start(string|array $cmd, string $alias, ?string $cwd = null, ?ar { $cwd = $cwd ?? base_path(); - $cmd = is_iterable($cmd) - // when an array is passed, escape spaces for each item - ? array_map(fn ($a) => str_replace(' ', '\ ', $a), $cmd) - // when a string is passed, explode it on the space - : array_values(array_filter(explode(' ', $cmd))); + if (is_string($cmd)) { + // When a string is passed, explode it on the space + $cmd = array_values(array_filter(explode(' ', $cmd))); + } $this->client->post('child-process/start', [ 'alias' => $alias, diff --git a/tests/ChildProcess/ChildProcessTest.php b/tests/ChildProcess/ChildProcessTest.php index 66a3a17..fe87938 100644 --- a/tests/ChildProcess/ChildProcessTest.php +++ b/tests/ChildProcess/ChildProcessTest.php @@ -26,7 +26,7 @@ Http::assertSent(function (Request $request) { return $request->url() === 'http://localhost:4000/api/child-process/start' && $request['alias'] === 'some-alias' && - $request['cmd'] === [str_replace(' ', '\ ', PHP_BINARY), 'artisan', 'foo:bar'] && + $request['cmd'] === [PHP_BINARY, 'artisan', 'foo:bar'] && $request['cwd'] === base_path() && $request['env'] === ['baz' => 'zah']; }); @@ -44,10 +44,10 @@ it('accepts either a string or a array as artisan command argument', function () { ChildProcess::artisan('foo:bar', 'some-alias'); - Http::assertSent(fn (Request $request) => $request['cmd'] === [str_replace(' ', '\ ', PHP_BINARY), 'artisan', 'foo:bar']); + Http::assertSent(fn (Request $request) => $request['cmd'] === [PHP_BINARY, 'artisan', 'foo:bar']); ChildProcess::artisan(['foo:baz'], 'some-alias'); - Http::assertSent(fn (Request $request) => $request['cmd'] === [str_replace(' ', '\ ', PHP_BINARY), 'artisan', 'foo:baz']); + Http::assertSent(fn (Request $request) => $request['cmd'] === [PHP_BINARY, 'artisan', 'foo:baz']); }); it('sets the cwd to the base path if none was given', function () { @@ -63,11 +63,6 @@ Http::assertSent(fn (Request $request) => $request['cmd'] === ['foo', 'bar', 'baz', 'bak']); }); -it('escapes spaces when passing a command array', function () { - ChildProcess::start(['path/to/some executable with spaces.sh', '--foo', '--bar'], 'some-alias'); - Http::assertSent(fn (Request $request) => $request['cmd'] === ['path/to/some\ executable\ with\ spaces.sh', '--foo', '--bar']); -}); - it('can stop a child process', function () { ChildProcess::stop('some-alias'); From 58a5539b9dca57ed20e55152580d8bb9ae494c90 Mon Sep 17 00:00:00 2001 From: gwleuverink Date: Tue, 29 Oct 2024 11:12:59 +0100 Subject: [PATCH 19/31] add optional arg to make the process persistent --- src/ChildProcess.php | 7 ++++--- tests/ChildProcess/ChildProcessTest.php | 17 +++++++++++++++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/ChildProcess.php b/src/ChildProcess.php index e5ce2d9..a308038 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -8,7 +8,7 @@ class ChildProcess { public function __construct(protected Client $client) {} - public function start(string|array $cmd, string $alias, ?string $cwd = null, ?array $env = null): self + public function start(string|array $cmd, string $alias, ?string $cwd = null, ?array $env = null, ?bool $persistent = false): self { $cwd = $cwd ?? base_path(); @@ -22,16 +22,17 @@ public function start(string|array $cmd, string $alias, ?string $cwd = null, ?ar 'cmd' => $cmd, 'cwd' => $cwd, 'env' => $env, + 'persistent' => $persistent, ])->json(); return $this; } - public function artisan(string|array $cmd, string $alias, ?array $env = null): self + public function artisan(string|array $cmd, string $alias, ?array $env = null, ?bool $persistent = false): self { $cmd = [PHP_BINARY, 'artisan', ...(array) $cmd]; - return $this->start($cmd, $alias, env: $env); + return $this->start($cmd, $alias, env: $env, persistent: $persistent); } public function stop(string $alias): void diff --git a/tests/ChildProcess/ChildProcessTest.php b/tests/ChildProcess/ChildProcessTest.php index fe87938..504e275 100644 --- a/tests/ChildProcess/ChildProcessTest.php +++ b/tests/ChildProcess/ChildProcessTest.php @@ -32,8 +32,6 @@ }); }); -it('can mark the process as persistent')->todo(); - it('accepts either a string or a array as start command argument', function () { ChildProcess::start('foo bar', 'some-alias'); Http::assertSent(fn (Request $request) => $request['cmd'] === ['foo', 'bar']); @@ -81,3 +79,18 @@ $request['message'] === '"some-message"'; }); }); + +it('can mark a process as persistent', function () { + ChildProcess::start('foo bar', 'some-alias', persistent: true); + Http::assertSent(fn (Request $request) => $request['persistent'] === true); +}); + +it('can mark a artisan command as persistent', function () { + ChildProcess::artisan('foo:bar', 'some-alias', persistent: true); + Http::assertSent(fn (Request $request) => $request['persistent'] === true); +}); + +it('marks the process as non-persistent by default', function () { + ChildProcess::start('foo bar', 'some-alias'); + Http::assertSent(fn (Request $request) => $request['persistent'] === false); +}); From ad8ef09f47fe61fdf0363a26e24a8a1c34128d9b Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Tue, 29 Oct 2024 19:10:38 +0000 Subject: [PATCH 20/31] improvements - ChildProcess instances can be used to interact with a process - get, all and restart are piped up --- src/ChildProcess.php | 94 +++++++++++++++++++++++++++++++----- src/Facades/ChildProcess.php | 9 ++-- 2 files changed, 89 insertions(+), 14 deletions(-) diff --git a/src/ChildProcess.php b/src/ChildProcess.php index 4d4794c..f0acd46 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -6,38 +6,110 @@ class ChildProcess { - private string $alias; + public readonly int $pid; - private ?array $process; + public readonly string $alias; + + public readonly array $cmd; + + public readonly ?string $cwd; + + public readonly ?array $env; + + public readonly bool $persistent; public function __construct(protected Client $client) {} - public function start(string $alias, array $cmd, ?string $cwd = null, ?array $env = null): object + public function get(string $alias = null): ?static { - $this->alias = $alias; + $alias = $alias ?? $this->alias; + + $process = $this->client->get("child-process/get/{$alias}")->json(); - $this->process = $this->client->post('child-process/start', [ + if (! $process) { + return null; + } + + return $this->fromRuntimeProcess($process); + } + + public function all(): array + { + $processes = $this->client->get("child-process/")->json(); + + if (empty($processes)) { + return []; + } + + $hydrated = []; + + foreach ($processes as $alias => $process) { + $hydrated[$alias] = (new static($this->client)) + ->fromRuntimeProcess($process); + } + + return $hydrated; + } + + public function start( + string $alias, + array $cmd, + ?string $cwd = null, + ?array $env = null, + bool $persistent = false + ): static + { + $process = $this->client->post('child-process/start', [ 'alias' => $alias, 'cmd' => $cmd, 'cwd' => base_path(), 'env' => $env, + 'persistent' => $persistent, ])->json(); - return $this; + return $this->fromRuntimeProcess($process); } - public function stop(string $alias): void + public function stop(string $alias = null): void { $this->client->post('child-process/stop', [ - 'alias' => $alias, + 'alias' => $alias ?? $this->alias, + ])->json(); + } + + public function restart(string $alias = null): ?static + { + $process = $this->client->post('child-process/restart', [ + 'alias' => $alias ?? $this->alias, ])->json(); + + if (! $process) { + return null; + } + + return $this->fromRuntimeProcess($process); } - public function message(string $alias, mixed $message): void + public function message(string $message, string $alias = null): static { $this->client->post('child-process/message', [ - 'alias' => $alias, - 'message' => json_encode($message), + 'alias' => $alias ?? $this->alias, + 'message' => $message, ])->json(); + + return $this; + } + + private function fromRuntimeProcess($process): static + { + if (isset($process['pid'])) { + $this->pid = $process['pid']; + } + + foreach ($process['settings'] as $key => $value) { + $this->{$key} = $value; + } + + return $this; } } diff --git a/src/Facades/ChildProcess.php b/src/Facades/ChildProcess.php index 8730f80..c0731de 100644 --- a/src/Facades/ChildProcess.php +++ b/src/Facades/ChildProcess.php @@ -5,9 +5,12 @@ use Illuminate\Support\Facades\Facade; /** - * @method static string start(string $alias, array $cmd, string $cwd = null, array $env = null) - * @method static string stop(string $alias) - * @method static string message(string $alias, mixed $message) + * @method static \Native\Laravel\ChildProcess[] all() + * @method static \Native\Laravel\ChildProcess get(string $alias) + * @method static \Native\Laravel\ChildProcess message(string $message, string $alias = null) + * @method static \Native\Laravel\ChildProcess restart(string $alias) + * @method static \Native\Laravel\ChildProcess start(string $alias, array $cmd, string $cwd = null, array $env = null, bool $persistent = false) + * @method static void stop(string $alias) */ class ChildProcess extends Facade { From 62ce8955079e52795f3ba55f1e2a0627865b396c Mon Sep 17 00:00:00 2001 From: simonhamp Date: Tue, 29 Oct 2024 19:11:08 +0000 Subject: [PATCH 21/31] Fix styling --- src/ChildProcess.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ChildProcess.php b/src/ChildProcess.php index f0acd46..fd77621 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -20,7 +20,7 @@ class ChildProcess public function __construct(protected Client $client) {} - public function get(string $alias = null): ?static + public function get(?string $alias = null): ?static { $alias = $alias ?? $this->alias; @@ -35,7 +35,7 @@ public function get(string $alias = null): ?static public function all(): array { - $processes = $this->client->get("child-process/")->json(); + $processes = $this->client->get('child-process/')->json(); if (empty($processes)) { return []; @@ -57,8 +57,7 @@ public function start( ?string $cwd = null, ?array $env = null, bool $persistent = false - ): static - { + ): static { $process = $this->client->post('child-process/start', [ 'alias' => $alias, 'cmd' => $cmd, @@ -70,14 +69,14 @@ public function start( return $this->fromRuntimeProcess($process); } - public function stop(string $alias = null): void + public function stop(?string $alias = null): void { $this->client->post('child-process/stop', [ 'alias' => $alias ?? $this->alias, ])->json(); } - public function restart(string $alias = null): ?static + public function restart(?string $alias = null): ?static { $process = $this->client->post('child-process/restart', [ 'alias' => $alias ?? $this->alias, @@ -90,7 +89,7 @@ public function restart(string $alias = null): ?static return $this->fromRuntimeProcess($process); } - public function message(string $message, string $alias = null): static + public function message(string $message, ?string $alias = null): static { $this->client->post('child-process/message', [ 'alias' => $alias ?? $this->alias, From 160b8673251938ad014ca979a16b66fbab6f5207 Mon Sep 17 00:00:00 2001 From: Willem Leuverink Date: Tue, 29 Oct 2024 22:55:28 +0100 Subject: [PATCH 22/31] Update src/ChildProcess.php Co-authored-by: Simon Hamp --- src/ChildProcess.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ChildProcess.php b/src/ChildProcess.php index 512b1e6..f618a10 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -68,7 +68,7 @@ public function start( $process = $this->client->post('child-process/start', [ 'alias' => $alias, 'cmd' => $cmd, - 'cwd' => $cwd, + 'cwd' => $cwd ?? base_path(), 'env' => $env, 'persistent' => $persistent, ])->json(); From 62b8faf0ab5c58f90ffa1dd7e03d12b155fca522 Mon Sep 17 00:00:00 2001 From: gwleuverink Date: Tue, 29 Oct 2024 22:58:30 +0100 Subject: [PATCH 23/31] feedback - tidy cwd default path --- src/ChildProcess.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ChildProcess.php b/src/ChildProcess.php index f618a10..fbc6e67 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -50,15 +50,14 @@ public function all(): array return $hydrated; } - + public function start( - string|array $cmd + string|array $cmd, string $alias, ?string $cwd = null, ?array $env = null, bool $persistent = false ): static { - $cwd = $cwd ?? base_path(); if (is_string($cmd)) { // When a string is passed, explode it on the space From 246a2362d71913e44b7491ebc9557e231974fef2 Mon Sep 17 00:00:00 2001 From: gwleuverink Date: Tue, 29 Oct 2024 23:12:42 +0100 Subject: [PATCH 24/31] stub out php command tests --- tests/ChildProcess/ChildProcessTest.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/ChildProcess/ChildProcessTest.php b/tests/ChildProcess/ChildProcessTest.php index 504e275..12e5cc3 100644 --- a/tests/ChildProcess/ChildProcessTest.php +++ b/tests/ChildProcess/ChildProcessTest.php @@ -20,6 +20,18 @@ }); }); +it('can start a php command', function () { + ChildProcess::artisan("-r 'sleep(5);'", 'some-alias', ['baz' => 'zah']); + + Http::assertSent(function (Request $request) { + return $request->url() === 'http://localhost:4000/api/child-process/start' && + $request['alias'] === 'some-alias' && + $request['cmd'] === [PHP_BINARY, '-r', "'sleep(5);'"] && + $request['cwd'] === base_path() && + $request['env'] === ['baz' => 'zah']; + }); +}); + it('can start a artisan command', function () { ChildProcess::artisan('foo:bar', 'some-alias', ['baz' => 'zah']); @@ -40,6 +52,14 @@ Http::assertSent(fn (Request $request) => $request['cmd'] === ['foo', 'baz']); }); +it('accepts either a string or a array as php command argument', function () { + ChildProcess::artisan(" 'sleep(5);'", 'some-alias'); + Http::assertSent(fn (Request $request) => $request['cmd'] === [PHP_BINARY, '-r', "'sleep(5);'"]); + + ChildProcess::artisan(['-r', "'sleep(5);'"], 'some-alias'); + Http::assertSent(fn (Request $request) => $request['cmd'] === [PHP_BINARY, '-r', "'sleep(5);'"]); +}); + it('accepts either a string or a array as artisan command argument', function () { ChildProcess::artisan('foo:bar', 'some-alias'); Http::assertSent(fn (Request $request) => $request['cmd'] === [PHP_BINARY, 'artisan', 'foo:bar']); @@ -85,6 +105,11 @@ Http::assertSent(fn (Request $request) => $request['persistent'] === true); }); +it('can mark a php command as persistent', function () { + ChildProcess::php("-r 'sleep(5);'", 'some-alias', persistent: true); + Http::assertSent(fn (Request $request) => $request['persistent'] === true); +}); + it('can mark a artisan command as persistent', function () { ChildProcess::artisan('foo:bar', 'some-alias', persistent: true); Http::assertSent(fn (Request $request) => $request['persistent'] === true); From 31d78aee9a2ff12d557216ffe1827519de71164d Mon Sep 17 00:00:00 2001 From: gwleuverink Date: Tue, 29 Oct 2024 23:49:46 +0100 Subject: [PATCH 25/31] fix - tests after upstream merge --- src/ChildProcess.php | 2 +- tests/ChildProcess/ChildProcessTest.php | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/ChildProcess.php b/src/ChildProcess.php index fbc6e67..4361b98 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -112,7 +112,7 @@ public function message(string $message, ?string $alias = null): static return $this; } - private function fromRuntimeProcess($process): static + protected function fromRuntimeProcess($process): static { if (isset($process['pid'])) { $this->pid = $process['pid']; diff --git a/tests/ChildProcess/ChildProcessTest.php b/tests/ChildProcess/ChildProcessTest.php index 12e5cc3..8f3a490 100644 --- a/tests/ChildProcess/ChildProcessTest.php +++ b/tests/ChildProcess/ChildProcessTest.php @@ -2,10 +2,21 @@ use Illuminate\Http\Client\Request; use Illuminate\Support\Facades\Http; +use Mockery; +use Native\Laravel\ChildProcess as ChildProcessImplement; +use Native\Laravel\Client\Client; use Native\Laravel\Facades\ChildProcess; beforeEach(function () { Http::fake(); + + $mock = Mockery::mock(ChildProcessImplement::class, [resolve(Client::class)]) + ->makePartial() + ->shouldAllowMockingProtectedMethods(); + + $this->instance(ChildProcessImplement::class, $mock->allows([ + 'fromRuntimeProcess' => $mock, + ])); }); it('can start a child process', function () { @@ -30,7 +41,7 @@ $request['cwd'] === base_path() && $request['env'] === ['baz' => 'zah']; }); -}); +})->todo(); it('can start a artisan command', function () { ChildProcess::artisan('foo:bar', 'some-alias', ['baz' => 'zah']); @@ -58,7 +69,7 @@ ChildProcess::artisan(['-r', "'sleep(5);'"], 'some-alias'); Http::assertSent(fn (Request $request) => $request['cmd'] === [PHP_BINARY, '-r', "'sleep(5);'"]); -}); +})->todo(); it('accepts either a string or a array as artisan command argument', function () { ChildProcess::artisan('foo:bar', 'some-alias'); @@ -96,7 +107,7 @@ Http::assertSent(function (Request $request) { return $request->url() === 'http://localhost:4000/api/child-process/message' && $request['alias'] === 'some-alias' && - $request['message'] === '"some-message"'; + $request['message'] === 'some-message'; }); }); @@ -108,7 +119,7 @@ it('can mark a php command as persistent', function () { ChildProcess::php("-r 'sleep(5);'", 'some-alias', persistent: true); Http::assertSent(fn (Request $request) => $request['persistent'] === true); -}); +})->todo(); it('can mark a artisan command as persistent', function () { ChildProcess::artisan('foo:bar', 'some-alias', persistent: true); From 8dbee1d1d08afa0809c41b6672063a8b3c2d170c Mon Sep 17 00:00:00 2001 From: gwleuverink Date: Wed, 30 Oct 2024 00:06:26 +0100 Subject: [PATCH 26/31] add php convenience method --- src/ChildProcess.php | 25 ++++++++++++++++++------- tests/ChildProcess/ChildProcessTest.php | 14 +++++++------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/ChildProcess.php b/src/ChildProcess.php index 4361b98..1216710 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -59,14 +59,9 @@ public function start( bool $persistent = false ): static { - if (is_string($cmd)) { - // When a string is passed, explode it on the space - $cmd = array_values(array_filter(explode(' ', $cmd))); - } - $process = $this->client->post('child-process/start', [ 'alias' => $alias, - 'cmd' => $cmd, + 'cmd' => $this->explodeCommand($cmd), 'cwd' => $cwd ?? base_path(), 'env' => $env, 'persistent' => $persistent, @@ -75,9 +70,16 @@ public function start( return $this->fromRuntimeProcess($process); } + public function php(string|array $cmd, string $alias, ?array $env = null, ?bool $persistent = false): self + { + $cmd = [PHP_BINARY, ...$this->explodeCommand($cmd)]; + + return $this->start($cmd, $alias, env: $env, persistent: $persistent); + } + public function artisan(string|array $cmd, string $alias, ?array $env = null, ?bool $persistent = false): self { - $cmd = [PHP_BINARY, 'artisan', ...(array) $cmd]; + $cmd = [PHP_BINARY, 'artisan', ...$this->explodeCommand($cmd)]; return $this->start($cmd, $alias, env: $env, persistent: $persistent); } @@ -124,4 +126,13 @@ protected function fromRuntimeProcess($process): static return $this; } + + private function explodeCommand(string|array $cmd): array + { + if (is_iterable($cmd)) { + return $cmd; + } + + return array_values(array_filter(explode(' ', $cmd))); + } } diff --git a/tests/ChildProcess/ChildProcessTest.php b/tests/ChildProcess/ChildProcessTest.php index 8f3a490..7108b37 100644 --- a/tests/ChildProcess/ChildProcessTest.php +++ b/tests/ChildProcess/ChildProcessTest.php @@ -32,7 +32,7 @@ }); it('can start a php command', function () { - ChildProcess::artisan("-r 'sleep(5);'", 'some-alias', ['baz' => 'zah']); + ChildProcess::php("-r 'sleep(5);'", 'some-alias', ['baz' => 'zah']); Http::assertSent(function (Request $request) { return $request->url() === 'http://localhost:4000/api/child-process/start' && @@ -41,15 +41,15 @@ $request['cwd'] === base_path() && $request['env'] === ['baz' => 'zah']; }); -})->todo(); +}); it('can start a artisan command', function () { - ChildProcess::artisan('foo:bar', 'some-alias', ['baz' => 'zah']); + ChildProcess::artisan('foo:bar --verbose', 'some-alias', ['baz' => 'zah']); Http::assertSent(function (Request $request) { return $request->url() === 'http://localhost:4000/api/child-process/start' && $request['alias'] === 'some-alias' && - $request['cmd'] === [PHP_BINARY, 'artisan', 'foo:bar'] && + $request['cmd'] === [PHP_BINARY, 'artisan', 'foo:bar', '--verbose'] && $request['cwd'] === base_path() && $request['env'] === ['baz' => 'zah']; }); @@ -64,12 +64,12 @@ }); it('accepts either a string or a array as php command argument', function () { - ChildProcess::artisan(" 'sleep(5);'", 'some-alias'); + ChildProcess::php("-r 'sleep(5);'", 'some-alias'); Http::assertSent(fn (Request $request) => $request['cmd'] === [PHP_BINARY, '-r', "'sleep(5);'"]); ChildProcess::artisan(['-r', "'sleep(5);'"], 'some-alias'); Http::assertSent(fn (Request $request) => $request['cmd'] === [PHP_BINARY, '-r', "'sleep(5);'"]); -})->todo(); +}); it('accepts either a string or a array as artisan command argument', function () { ChildProcess::artisan('foo:bar', 'some-alias'); @@ -119,7 +119,7 @@ it('can mark a php command as persistent', function () { ChildProcess::php("-r 'sleep(5);'", 'some-alias', persistent: true); Http::assertSent(fn (Request $request) => $request['persistent'] === true); -})->todo(); +}); it('can mark a artisan command as persistent', function () { ChildProcess::artisan('foo:bar', 'some-alias', persistent: true); From 4e9ce39124682722160c8aa8619827ef08a7b473 Mon Sep 17 00:00:00 2001 From: gwleuverink Date: Wed, 30 Oct 2024 00:12:20 +0100 Subject: [PATCH 27/31] wip - refactor --- src/ChildProcess.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ChildProcess.php b/src/ChildProcess.php index 1216710..de4418b 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -79,9 +79,9 @@ public function php(string|array $cmd, string $alias, ?array $env = null, ?bool public function artisan(string|array $cmd, string $alias, ?array $env = null, ?bool $persistent = false): self { - $cmd = [PHP_BINARY, 'artisan', ...$this->explodeCommand($cmd)]; + $cmd = ['artisan', ...$this->explodeCommand($cmd)]; - return $this->start($cmd, $alias, env: $env, persistent: $persistent); + return $this->php($cmd, $alias, env: $env, persistent: $persistent); } public function stop(?string $alias = null): void From 2f76685d03a50634b24f89eee0fa4a12cbe72668 Mon Sep 17 00:00:00 2001 From: gwleuverink Date: Wed, 30 Oct 2024 00:15:13 +0100 Subject: [PATCH 28/31] add phpdoc for facade methods --- src/Facades/ChildProcess.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Facades/ChildProcess.php b/src/Facades/ChildProcess.php index c0731de..ad04240 100644 --- a/src/Facades/ChildProcess.php +++ b/src/Facades/ChildProcess.php @@ -6,11 +6,13 @@ /** * @method static \Native\Laravel\ChildProcess[] all() - * @method static \Native\Laravel\ChildProcess get(string $alias) + * @method static \Native\Laravel\ChildProcess get(string $alias = null) * @method static \Native\Laravel\ChildProcess message(string $message, string $alias = null) - * @method static \Native\Laravel\ChildProcess restart(string $alias) - * @method static \Native\Laravel\ChildProcess start(string $alias, array $cmd, string $cwd = null, array $env = null, bool $persistent = false) - * @method static void stop(string $alias) + * @method static \Native\Laravel\ChildProcess restart(string $alias = null) + * @method static \Native\Laravel\ChildProcess start(string|array $cmd, string $alias, string $cwd = null, array $env = null, bool $persistent = false) + * @method static \Native\Laravel\ChildProcess php(string|array $cmd, string $alias, array $env = null, bool $persistent = false) + * @method static \Native\Laravel\ChildProcess artisan(string|array $cmd, string $alias, array $env = null, bool $persistent = false) + * @method static \Native\Laravel\ChildProcess void stop(string $alias = null) */ class ChildProcess extends Facade { From 2153f1c79f772fbd327ef957158a39ebe53e4d5c Mon Sep 17 00:00:00 2001 From: Willem Leuverink Date: Wed, 30 Oct 2024 00:25:42 +0100 Subject: [PATCH 29/31] Update src/Facades/ChildProcess.php Co-authored-by: Simon Hamp --- src/Facades/ChildProcess.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Facades/ChildProcess.php b/src/Facades/ChildProcess.php index ad04240..563a3ba 100644 --- a/src/Facades/ChildProcess.php +++ b/src/Facades/ChildProcess.php @@ -12,7 +12,7 @@ * @method static \Native\Laravel\ChildProcess start(string|array $cmd, string $alias, string $cwd = null, array $env = null, bool $persistent = false) * @method static \Native\Laravel\ChildProcess php(string|array $cmd, string $alias, array $env = null, bool $persistent = false) * @method static \Native\Laravel\ChildProcess artisan(string|array $cmd, string $alias, array $env = null, bool $persistent = false) - * @method static \Native\Laravel\ChildProcess void stop(string $alias = null) + * @method static void stop(string $alias = null) */ class ChildProcess extends Facade { From c201b1b9cd32f7c718bfd377df54b939265c2649 Mon Sep 17 00:00:00 2001 From: gwleuverink Date: Wed, 30 Oct 2024 23:44:19 +0100 Subject: [PATCH 30/31] remove exploding string commands --- src/ChildProcess.php | 15 +++------------ tests/ChildProcess/ChildProcessTest.php | 17 ++++++----------- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/src/ChildProcess.php b/src/ChildProcess.php index de4418b..ff80a07 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -61,7 +61,7 @@ public function start( $process = $this->client->post('child-process/start', [ 'alias' => $alias, - 'cmd' => $this->explodeCommand($cmd), + 'cmd' => (array) $cmd, 'cwd' => $cwd ?? base_path(), 'env' => $env, 'persistent' => $persistent, @@ -72,14 +72,14 @@ public function start( public function php(string|array $cmd, string $alias, ?array $env = null, ?bool $persistent = false): self { - $cmd = [PHP_BINARY, ...$this->explodeCommand($cmd)]; + $cmd = [PHP_BINARY, ...(array) $cmd]; return $this->start($cmd, $alias, env: $env, persistent: $persistent); } public function artisan(string|array $cmd, string $alias, ?array $env = null, ?bool $persistent = false): self { - $cmd = ['artisan', ...$this->explodeCommand($cmd)]; + $cmd = ['artisan', ...(array) $cmd]; return $this->php($cmd, $alias, env: $env, persistent: $persistent); } @@ -126,13 +126,4 @@ protected function fromRuntimeProcess($process): static return $this; } - - private function explodeCommand(string|array $cmd): array - { - if (is_iterable($cmd)) { - return $cmd; - } - - return array_values(array_filter(explode(' ', $cmd))); - } } diff --git a/tests/ChildProcess/ChildProcessTest.php b/tests/ChildProcess/ChildProcessTest.php index 7108b37..a2d9259 100644 --- a/tests/ChildProcess/ChildProcessTest.php +++ b/tests/ChildProcess/ChildProcessTest.php @@ -25,7 +25,7 @@ Http::assertSent(function (Request $request) { return $request->url() === 'http://localhost:4000/api/child-process/start' && $request['alias'] === 'some-alias' && - $request['cmd'] === ['foo', 'bar'] && + $request['cmd'] === ['foo bar'] && $request['cwd'] === 'path/to/dir' && $request['env'] === ['baz' => 'zah']; }); @@ -37,7 +37,7 @@ Http::assertSent(function (Request $request) { return $request->url() === 'http://localhost:4000/api/child-process/start' && $request['alias'] === 'some-alias' && - $request['cmd'] === [PHP_BINARY, '-r', "'sleep(5);'"] && + $request['cmd'] === [PHP_BINARY, "-r 'sleep(5);'"] && $request['cwd'] === base_path() && $request['env'] === ['baz' => 'zah']; }); @@ -49,7 +49,7 @@ Http::assertSent(function (Request $request) { return $request->url() === 'http://localhost:4000/api/child-process/start' && $request['alias'] === 'some-alias' && - $request['cmd'] === [PHP_BINARY, 'artisan', 'foo:bar', '--verbose'] && + $request['cmd'] === [PHP_BINARY, 'artisan', 'foo:bar --verbose'] && $request['cwd'] === base_path() && $request['env'] === ['baz' => 'zah']; }); @@ -57,7 +57,7 @@ it('accepts either a string or a array as start command argument', function () { ChildProcess::start('foo bar', 'some-alias'); - Http::assertSent(fn (Request $request) => $request['cmd'] === ['foo', 'bar']); + Http::assertSent(fn (Request $request) => $request['cmd'] === ['foo bar']); ChildProcess::start(['foo', 'baz'], 'some-alias'); Http::assertSent(fn (Request $request) => $request['cmd'] === ['foo', 'baz']); @@ -65,9 +65,9 @@ it('accepts either a string or a array as php command argument', function () { ChildProcess::php("-r 'sleep(5);'", 'some-alias'); - Http::assertSent(fn (Request $request) => $request['cmd'] === [PHP_BINARY, '-r', "'sleep(5);'"]); + Http::assertSent(fn (Request $request) => $request['cmd'] === [PHP_BINARY, "-r 'sleep(5);'"]); - ChildProcess::artisan(['-r', "'sleep(5);'"], 'some-alias'); + ChildProcess::php(['-r', "'sleep(5);'"], 'some-alias'); Http::assertSent(fn (Request $request) => $request['cmd'] === [PHP_BINARY, '-r', "'sleep(5);'"]); }); @@ -87,11 +87,6 @@ Http::assertSent(fn (Request $request) => $request['cwd'] === base_path()); }); -it('filters double spaces when exploding a command string', function () { - ChildProcess::start('foo bar baz bak', 'some-alias'); - Http::assertSent(fn (Request $request) => $request['cmd'] === ['foo', 'bar', 'baz', 'bak']); -}); - it('can stop a child process', function () { ChildProcess::stop('some-alias'); From a8bf775d549dafad42a0c5ac9f8c32ecb286a5b7 Mon Sep 17 00:00:00 2001 From: gwleuverink Date: Thu, 31 Oct 2024 10:06:49 +0100 Subject: [PATCH 31/31] fix - return a fresh instance from the facade each time --- src/Facades/ChildProcess.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Facades/ChildProcess.php b/src/Facades/ChildProcess.php index 563a3ba..89b7236 100644 --- a/src/Facades/ChildProcess.php +++ b/src/Facades/ChildProcess.php @@ -3,6 +3,7 @@ namespace Native\Laravel\Facades; use Illuminate\Support\Facades\Facade; +use Native\Laravel\ChildProcess as Implement; /** * @method static \Native\Laravel\ChildProcess[] all() @@ -18,6 +19,8 @@ class ChildProcess extends Facade { protected static function getFacadeAccessor() { - return \Native\Laravel\ChildProcess::class; + self::clearResolvedInstance(Implement::class); + + return Implement::class; } }