From dcb691252594d48dae243070a6a8ac59b3ce01e2 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 15:48:23 +0300 Subject: [PATCH 01/55] fix: update Laravel version and badge links in README --- README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9bbdbe8..be1077e 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,11 @@

- Laravel Version : 10.x, 11.x - License - GitHub Actions Workflow Status -
- Source - Packagist Version - Packagist Downloads +Build Status +Laravel Version : 12.x + Total Downloads +Latest Stable Version +License

Allows you to execute commands, actions, jobs, and automated tasks on your production server.

From 23e36b9b45ffb96f912b6070e20a73db2fd40d2a Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 19:23:08 +0300 Subject: [PATCH 02/55] feat: add GitHub Actions workflow for running tests --- .github/workflows/{build.yml => tests.yml} | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) rename .github/workflows/{build.yml => tests.yml} (74%) diff --git a/.github/workflows/build.yml b/.github/workflows/tests.yml similarity index 74% rename from .github/workflows/build.yml rename to .github/workflows/tests.yml index 5a014ea..789cb38 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/tests.yml @@ -1,6 +1,9 @@ -name: build +name: Tests -on: [push, pull_request] +on: + push: + branches: [ main, develop ] + pull_request: jobs: run: @@ -8,7 +11,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-versions: ["8.1", "8.2", "8.3"] + php-versions: ['8.2', '8.3', '8.4'] name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} steps: @@ -19,7 +22,7 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-versions }} - extensions: mbstring, pdo, pdo_sqlite, sqlite3, intl, zip + extensions: mbstring, pdo, intl, zip coverage: none - name: Check PHP Version @@ -35,7 +38,7 @@ jobs: run: composer validate - name: Install dependencies - run: composer install --prefer-dist --no-progress --no-suggest + run: composer install --prefer-dist --no-progress - name: Run test suite - run: vendor/bin/phpunit + run: vendor/bin/phpunit --testdox \ No newline at end of file From b077e885fe9d711199c7a24b3c5ee3c7f780a3ee Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 19:23:21 +0300 Subject: [PATCH 03/55] feat: add PHP Coding Standards Fixer workflow and configuration --- .github/workflows/php-cs-fixer.yml | 23 ++++++++++++++++++ .php-cs-fixer.dist.php | 38 ++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 .github/workflows/php-cs-fixer.yml create mode 100644 .php-cs-fixer.dist.php diff --git a/.github/workflows/php-cs-fixer.yml b/.github/workflows/php-cs-fixer.yml new file mode 100644 index 0000000..5a596f1 --- /dev/null +++ b/.github/workflows/php-cs-fixer.yml @@ -0,0 +1,23 @@ +name: PHP Coding Standards Fixer + +on: [push] + +jobs: + php-cs-fixer: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + + - name: Run PHP CS Fixer + uses: docker://oskarstark/php-cs-fixer-ga + with: + args: --config=.php-cs-fixer.dist.php --allow-risky=yes + + - name: Commit changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: Fixing php-cs \ No newline at end of file diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..d5c8593 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,38 @@ +notPath('bootstrap/*') + ->notPath('storage/*') + ->notPath('storage/*') + ->notPath('resources/view/mail/*') + ->in([ + __DIR__ . '/src', + __DIR__ . '/tests', + ]) + ->name('*.php') + ->notName('*.blade.php') + ->ignoreDotFiles(true) + ->ignoreVCS(true); + +return (new PhpCsFixer\Config()) + ->setRules([ + '@PSR12' => true, + 'array_syntax' => ['syntax' => 'short'], + 'ordered_imports' => ['sort_algorithm' => 'alpha'], + 'no_unused_imports' => true, + 'not_operator_with_successor_space' => true, + 'trailing_comma_in_multiline' => true, + 'phpdoc_scalar' => true, + 'unary_operator_spaces' => true, + 'binary_operator_spaces' => true, + 'blank_line_before_statement' => [ + 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], + ], + 'phpdoc_single_line_var_spacing' => true, + 'phpdoc_var_without_name' => true, + 'method_argument_space' => [ + 'on_multiline' => 'ensure_fully_multiline', + 'keep_multiple_spaces_after_comma' => true, + ], + ]) + ->setFinder($finder); From 5ab8a60f9e3be23e941de2542642957f922eb12c Mon Sep 17 00:00:00 2001 From: MoamenEltouny Date: Thu, 4 Sep 2025 16:26:53 +0000 Subject: [PATCH 04/55] Fixing php-cs --- .php-cs-fixer.cache | 1 + src/Classes/ExecutorPoolClass.php | 6 +++--- src/Console/ExecuteCommand.php | 2 +- src/Console/ExecuteFreshCommand.php | 2 +- src/Console/ExecuteMakeCommand.php | 7 ++++--- src/Console/ExecuteRollbackCommand.php | 3 ++- src/Enums/ExecutorType.php | 4 ++-- src/Executor.php | 8 ++++---- src/ExecutorServiceProvider.php | 2 +- src/Models/Executor.php | 4 ++-- src/Services/ExecutorService.php | 5 +++-- tests/ExecutorTest.php | 4 ++-- tests/TestCase.php | 4 ++-- 13 files changed, 28 insertions(+), 24 deletions(-) create mode 100644 .php-cs-fixer.cache diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache new file mode 100644 index 0000000..60a0724 --- /dev/null +++ b/.php-cs-fixer.cache @@ -0,0 +1 @@ +{"php":"8.4.12","version":"3.87.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_extra_blank_lines":{"tokens":["use"]},"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","const_import","do","else","elseif","final","finally","for","foreach","function","function_import","if","insteadof","interface","namespace","new","private","protected","public","static","switch","trait","try","use","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"not_operator_with_successor_space":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true},"hashes":{"src\/Classes\/ExecutorPoolClass.php":"49d8adcc58eeeef618d5a1570ea7238c","src\/Traits\/InteractsWithIO.php":"2c14bbe144793d3173ecf119463ac1c0","src\/Facades\/ExecutorPool.php":"37d21a5c610b443ff7e373542cfeae70","src\/Executor.php":"6af562b05b62f0c0f9862ae322df67e7","src\/Enums\/ExecutorType.php":"7add25f4f0054055062fc63458a554be","src\/Models\/Executor.php":"2c6a2c1e00c09ee9129d0aece8017a57","src\/ExecutorServiceProvider.php":"6603e7955041b2aac7818f7ee384b511","src\/Console\/ExecuteMakeCommand.php":"e383e7764a04e63ee2e76abe18321338","src\/Console\/ExecuteCommand.php":"328e4ea27ac1781f3f6db79ee5d99429","src\/Console\/ExecuteFreshCommand.php":"a0fb7c2f8889ed26dc248d865bc58513","src\/Console\/ExecuteStatusCommand.php":"7f7e63dbf0b46edfe1fec1c551fa7a86","src\/Console\/ExecuteRollbackCommand.php":"6cc2f82e106f5bdb0721104ee8602668","src\/Services\/ExecutorService.php":"64deee9fe9e90aff30faf8de8b926845","tests\/TestCase.php":"d3d4ad73937ddf34e46cdb8e4d248135","tests\/ExecutorTest.php":"0d04893fe42b79626ad77a636a4f1ac8"}} \ No newline at end of file diff --git a/src/Classes/ExecutorPoolClass.php b/src/Classes/ExecutorPoolClass.php index d390051..66cf543 100644 --- a/src/Classes/ExecutorPoolClass.php +++ b/src/Classes/ExecutorPoolClass.php @@ -6,7 +6,7 @@ class ExecutorPoolClass { /** * These paths for all pools of executors. - * + * * @var array */ private array $paths = []; @@ -18,7 +18,7 @@ public function __construct() /** * Return all pools of executors. - * + * * @return array */ public function getPaths(): array @@ -28,7 +28,7 @@ public function getPaths(): array /** * Add a new path to executors pools. - * + * * @param string $path * @return void */ diff --git a/src/Console/ExecuteCommand.php b/src/Console/ExecuteCommand.php index b8db1e5..22201d6 100644 --- a/src/Console/ExecuteCommand.php +++ b/src/Console/ExecuteCommand.php @@ -27,7 +27,7 @@ class ExecuteCommand extends Command */ public function handle(ExecutorService $service) { - if (!$service->isExists()) { + if (! $service->isExists()) { $this->warn('There are no executors need to be executed.'); } diff --git a/src/Console/ExecuteFreshCommand.php b/src/Console/ExecuteFreshCommand.php index a5f3f88..6cf9909 100644 --- a/src/Console/ExecuteFreshCommand.php +++ b/src/Console/ExecuteFreshCommand.php @@ -23,7 +23,7 @@ class ExecuteFreshCommand extends Command /** * Execute the console command. - * + * * @return int */ public function handle() diff --git a/src/Console/ExecuteMakeCommand.php b/src/Console/ExecuteMakeCommand.php index c0194ad..4e7abe4 100644 --- a/src/Console/ExecuteMakeCommand.php +++ b/src/Console/ExecuteMakeCommand.php @@ -67,11 +67,11 @@ protected function replaceClass($stub, $name) */ protected function setTypeProperty(string &$stub) { - if (!$this->option('once')) { + if (! $this->option('once')) { $stub = str_replace( [ "{{ type }}\n", - "{{ type-use }}\n" + "{{ type-use }}\n", ], '', $stub @@ -101,8 +101,9 @@ protected function setTypeProperty(string &$stub) */ protected function setTagProperty(string &$stub) { - if (!$this->option('tag')) { + if (! $this->option('tag')) { $stub = str_replace("{{ tag }}\n", '', $stub); + return; } diff --git a/src/Console/ExecuteRollbackCommand.php b/src/Console/ExecuteRollbackCommand.php index 6c0a55d..2542624 100644 --- a/src/Console/ExecuteRollbackCommand.php +++ b/src/Console/ExecuteRollbackCommand.php @@ -24,7 +24,7 @@ class ExecuteRollbackCommand extends Command /** * Execute the console command. - * + * * @return int */ public function handle() @@ -33,6 +33,7 @@ public function handle() if (empty($batches)) { $this->error('There are no executors has been found.'); + return 1; } diff --git a/src/Enums/ExecutorType.php b/src/Enums/ExecutorType.php index 2ee201f..bdd0db1 100644 --- a/src/Enums/ExecutorType.php +++ b/src/Enums/ExecutorType.php @@ -14,7 +14,7 @@ enum ExecutorType: int /** * Check if the executor will be executed always. * - * @return boolean + * @return bool */ public function isAlways(): bool { @@ -24,7 +24,7 @@ public function isAlways(): bool /** * Check if the executor will be executed once. * - * @return boolean + * @return bool */ public function isOnce(): bool { diff --git a/src/Executor.php b/src/Executor.php index b504264..8b4ef43 100644 --- a/src/Executor.php +++ b/src/Executor.php @@ -30,23 +30,23 @@ abstract class Executor /** * Execute it. - * + * * @return void */ abstract public function handle(): void; /** * Create a new Executor instance. - * + * * @return void */ public function __construct() { - if (!($this->type instanceof ExecutorType)) { + if (! ($this->type instanceof ExecutorType)) { throw new \Exception('The type of the executor must be an instance of ExecutorType.'); } - if (!is_null($this->tag) && !is_string($this->tag) && !is_array($this->tag)) { + if (! is_null($this->tag) && ! is_string($this->tag) && ! is_array($this->tag)) { throw new \Exception('The tag of the executor must be a string or an array or null.'); } diff --git a/src/ExecutorServiceProvider.php b/src/ExecutorServiceProvider.php index efba8c2..2caaaac 100644 --- a/src/ExecutorServiceProvider.php +++ b/src/ExecutorServiceProvider.php @@ -39,7 +39,7 @@ public function boot() // Load Migrations $this->loadMigrationsFrom(__DIR__ . '/../database/migrations'); - // Load Commands + // Load Commands $this->commands([ ExecuteCommand::class, ExecuteMakeCommand::class, diff --git a/src/Models/Executor.php b/src/Models/Executor.php index 47e7c48..1754279 100644 --- a/src/Models/Executor.php +++ b/src/Models/Executor.php @@ -21,7 +21,7 @@ class Executor extends Model /** * Indicates if the model should be timestamped. * - * @var boolean + * @var bool */ public $timestamps = false; @@ -36,7 +36,7 @@ class Executor extends Model 'tag', 'batch', 'executed', - 'last_executed_at' + 'last_executed_at', ]; /** diff --git a/src/Services/ExecutorService.php b/src/Services/ExecutorService.php index 8b23727..8206876 100644 --- a/src/Services/ExecutorService.php +++ b/src/Services/ExecutorService.php @@ -48,7 +48,7 @@ protected function prepareExecutors() 'type' => $class->getProperty('type')->getDefaultValue(), 'tag' => $class->getProperty('tag')->getDefaultValue(), 'path' => $class->getFileName(), - 'model' => $db[$name] ?? null + 'model' => $db[$name] ?? null, ]; if ($executor['model']) { @@ -61,6 +61,7 @@ protected function prepareExecutors() $executor['model']->save(); } } + return $executor; }, $this->getPaths()))->keyBy('name'); } @@ -103,7 +104,7 @@ protected function getNextBatch() { return (Executor::orderBy('batch', 'desc')->first()?->batch ?? 0) + 1; } - + /** * Get the paths of the executors. * diff --git a/tests/ExecutorTest.php b/tests/ExecutorTest.php index d9d5394..de07603 100644 --- a/tests/ExecutorTest.php +++ b/tests/ExecutorTest.php @@ -74,8 +74,8 @@ public function testFreshExecutors() public function testStatusOfExecutors() { $this->testMakeExecutor(); - - $executors = (new ExecutorService)->sync(); + + $executors = (new ExecutorService())->sync(); $this->artisan('execute:status') ->assertOk() diff --git a/tests/TestCase.php b/tests/TestCase.php index 8998d59..86b1ad9 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -36,9 +36,9 @@ protected function getEnvironmentSetUp($app) { $app['config']->set('database.default', 'testbench'); $app['config']->set('database.connections.testbench', [ - 'driver' => 'sqlite', + 'driver' => 'sqlite', 'database' => ':memory:', - 'prefix' => '', + 'prefix' => '', ]); } } From 6e90da218d81484caa3a687c1701b7c5ccebdd75 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 19:27:00 +0300 Subject: [PATCH 05/55] feat: add funding configuration for GitHub Sponsors --- .github/FUNDING.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..3ba6281 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: MoamenEltouny +buy_me_a_coffee: moameneltouny \ No newline at end of file From 94b637df4fdab619e73e3ae816bd0c6004626046 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 19:31:30 +0300 Subject: [PATCH 06/55] fix: update .gitignore to include phpunit and php-cs-fixer cache files --- .gitignore | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 35024a0..929319b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ -vendor +/vendor composer.lock -.phpunit.cache \ No newline at end of file +.phpunit.result.cache +.php-cs-fixer.cache \ No newline at end of file From ed12eb1c210c8434900588d2db3ba7447964f237 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 20:12:34 +0300 Subject: [PATCH 07/55] refactor: update Executor model and migration for improved attributes and connection handling --- ...24_07_07_000001_create_executors_table.php | 22 ++++---- src/Models/Executor.php | 52 +++++++++++++++---- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/database/migrations/2024_07_07_000001_create_executors_table.php b/database/migrations/2024_07_07_000001_create_executors_table.php index 23ad719..5020cb5 100644 --- a/database/migrations/2024_07_07_000001_create_executors_table.php +++ b/database/migrations/2024_07_07_000001_create_executors_table.php @@ -14,15 +14,16 @@ class CreateExecutorsTable extends Migration */ public function up() { - Schema::create('executors', function (Blueprint $table) { - $table->id(); - $table->unsignedTinyInteger('type')->default(ExecutorType::Always); - $table->string('executor'); - $table->string('tag')->nullable(); - $table->integer('batch')->default(1); - $table->integer('executed')->default(0); - $table->timestamp('last_executed_at')->nullable(); - }); + Schema::connection(config('pharaonic.executor.connection', config('database.default'))) + ->create(config('pharaonic.executor.table', 'executors'), function (Blueprint $table) { + $table->id(); + $table->unsignedTinyInteger('type')->default(ExecutorType::Always); + $table->string('name'); + $table->json('tags')->nullable(); + $table->integer('batch')->default(1); + $table->integer('executed')->default(0); + $table->timestamp('last_executed_at')->nullable(); + }); } /** @@ -32,6 +33,7 @@ public function up() */ public function down() { - Schema::dropIfExists('executors'); + Schema::connection(config('pharaonic.executor.connection', config('database.default'))) + ->dropIfExists(config('pharaonic.executor.table', 'executors')); } } diff --git a/src/Models/Executor.php b/src/Models/Executor.php index 1754279..21bba94 100644 --- a/src/Models/Executor.php +++ b/src/Models/Executor.php @@ -8,12 +8,13 @@ /** * @property int $id * @property ExecutorType $type - * @property string $executor - * @property string $tag + * @property string $name + * @property array|null $tags * @property int $batch * @property int $executed - * @property \Carbon\Carbon $last_executed_at - * @property-read string $executable + * @property \Illuminate\Support\Carbon $last_executed_at + * @method bool isNew() + * @method bool isExecutable() * @method void execute() */ class Executor extends Model @@ -32,8 +33,8 @@ class Executor extends Model */ protected $fillable = [ 'type', - 'executor', - 'tag', + 'name', + 'tags', 'batch', 'executed', 'last_executed_at', @@ -46,19 +47,50 @@ class Executor extends Model */ protected $casts = [ 'type' => ExecutorType::class, + 'tags' => 'array', 'batch' => 'integer', 'executed' => 'integer', 'last_executed_at' => 'datetime', ]; /** - * Get the executable attribute. + * Get the current connection name for the model. * - * @return void + * @return string|null + */ + public function getConnectionName() + { + return config('pharaonic.executor.connection', parent::getConnectionName()); + } + + /** + * Get the table associated with the model. + * + * @return string + */ + public function getTable() + { + return config('pharaonic.executor.table', parent::getTable()); + } + + /** + * Check if the executor is new (never executed). + * + * @return boolean + */ + public function isNew() + { + return $this->executed == 0; + } + + /** + * Check if the executor is executable. + * + * @return boolean */ - public function getExecutableAttribute() + public function isExecutable() { - return $this->type->isAlways() || ($this->type->isOnce() && $this->executed == 0); + return $this->type->isAlways() || $this->isNew(); } /** From 1008d71f4a5144c6670e456383f142c962ff3fbe Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 20:13:34 +0300 Subject: [PATCH 08/55] feat: update configuration files for Laravel compatibility and add executor config --- .php-cs-fixer.dist.php | 1 + README.md | 2 +- composer.json | 6 +++--- config/executor.php | 6 ++++++ 4 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 config/executor.php diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index d5c8593..70e2ea4 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -17,6 +17,7 @@ return (new PhpCsFixer\Config()) ->setRules([ '@PSR12' => true, + 'indentation_type' => true, 'array_syntax' => ['syntax' => 'short'], 'ordered_imports' => ['sort_algorithm' => 'alpha'], 'no_unused_imports' => true, diff --git a/README.md b/README.md index be1077e..db0108c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

Build Status -Laravel Version : 12.x +Laravel Version : 11.x Total Downloads Latest Stable Version License diff --git a/composer.json b/composer.json index 79f4465..02f19b2 100644 --- a/composer.json +++ b/composer.json @@ -24,11 +24,11 @@ } ], "require": { - "php": ">=8.1", - "laravel/framework": ">=10.0" + "php": "~8.2|~8.3|~8.4", + "laravel/framework": "^11.0" }, "require-dev": { - "orchestra/testbench": "^8.0" + "orchestra/testbench": "^9.15" }, "config": { "sort-packages": true diff --git a/config/executor.php b/config/executor.php new file mode 100644 index 0000000..401aa90 --- /dev/null +++ b/config/executor.php @@ -0,0 +1,6 @@ + env('DB_CONNECTION', 'mysql'), + 'table' => 'executors', +]; \ No newline at end of file From 635128e00d4d010fb5d5060e550e3ea2793463fb Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 20:40:44 +0300 Subject: [PATCH 09/55] fix: add .phpunit.cache to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 929319b..ee34d49 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /vendor composer.lock +.phpunit.cache .phpunit.result.cache .php-cs-fixer.cache \ No newline at end of file From e8f41c99f1fdb8bbdd7c9252a4425e1fe13102d4 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 20:40:51 +0300 Subject: [PATCH 10/55] fix: update database connection settings in TestCase for consistency in testing environment --- tests/TestCase.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index 86b1ad9..820e273 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -34,8 +34,8 @@ protected function getPackageProviders($app) */ protected function getEnvironmentSetUp($app) { - $app['config']->set('database.default', 'testbench'); - $app['config']->set('database.connections.testbench', [ + $app['config']->set('database.default', 'testing'); + $app['config']->set('database.connections.testing', [ 'driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '', From fc1db27374c1083247d3120c4b9dfddcb76ce8e5 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 21:43:07 +0300 Subject: [PATCH 11/55] fix: update database connection setting in executor config for consistency --- config/executor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/executor.php b/config/executor.php index 401aa90..fc2ad77 100644 --- a/config/executor.php +++ b/config/executor.php @@ -1,6 +1,6 @@ env('DB_CONNECTION', 'mysql'), + 'connection' => env('DB_CONNECTION', config('database.default')), 'table' => 'executors', ]; \ No newline at end of file From a9f4e343a3db1aa2a32362c11ba4202bb4a4619e Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 21:43:32 +0300 Subject: [PATCH 12/55] feat: update executor stubs and tests for improved structure and functionality --- phpunit.xml.dist | 22 +++--- stubs/executor.php.stub | 27 +++++-- stubs/property/once.stub | 6 -- stubs/property/servers.stub | 6 ++ stubs/property/tag.stub | 6 -- stubs/property/tags.stub | 6 ++ tests/ExecutorTest.php | 136 ++++++++++++++++++------------------ tests/TestCase.php | 14 ++-- 8 files changed, 119 insertions(+), 104 deletions(-) delete mode 100644 stubs/property/once.stub create mode 100644 stubs/property/servers.stub delete mode 100644 stubs/property/tag.stub create mode 100644 stubs/property/tags.stub diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 5d2e220..84e9176 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,13 +1,13 @@ - - - - ./tests/ - - - - - ./src/ - - + + + + + tests + + + diff --git a/stubs/executor.php.stub b/stubs/executor.php.stub index 9c51a4a..1c5df5c 100644 --- a/stubs/executor.php.stub +++ b/stubs/executor.php.stub @@ -1,18 +1,35 @@ artisan('execute:make', ['name' => 'testMakeExecutor']) ->assertExitCode(0); } - public function testMakeOnceExecutor() - { - $this->artisan('execute:make', [ - 'name' => 'testMakeOnceExecutor', - '--once' => true, - ]) - ->assertExitCode(0); - } + // public function testMakeOnceExecutor() + // { + // $this->artisan('execute:make', [ + // 'name' => 'testMakeOnceExecutor', + // '--once' => true, + // ]) + // ->assertExitCode(0); + // } - public function testMakeExecutorWithTag() - { - $this->artisan('execute:make', [ - 'name' => 'testMakeExecutorWithTag', - '--tag' => 'test', - ]) - ->assertExitCode(0); - } + // public function testMakeExecutorWithTag() + // { + // $this->artisan('execute:make', [ + // 'name' => 'testMakeExecutorWithTag', + // '--tags' => 'test', + // ]) + // ->assertExitCode(0); + // } - public function testMakeOnceExecutorWithTag() - { - $this->artisan('execute:make', [ - 'name' => 'testMakeOnceExecutorWithTag', - '--once' => true, - '--tag' => 'test', - ]) - ->assertExitCode(0); - } + // public function testMakeOnceExecutorWithTag() + // { + // $this->artisan('execute:make', [ + // 'name' => 'testMakeOnceExecutorWithTag', + // '--once' => true, + // '--tags' => 'test,example', + // ]) + // ->assertExitCode(0); + // } - public function testExecute() - { - $this->testMakeExecutor(); - $this->artisan('execute')->assertOk(); - $this->assertEquals(1, Executor::count()); - } + // public function testExecute() + // { + // $this->testMakeExecutor(); + // $this->artisan('execute')->assertOk(); + // $this->assertEquals(1, Executor::count()); + // } - public function testRollbackSuccess() - { - $this->testMakeExecutor(); - $this->artisan('execute')->assertOk(); - $this->artisan('execute:rollback')->assertOk(); - $this->assertEquals(0, Executor::count()); - } + // public function testRollbackSuccess() + // { + // $this->testMakeExecutor(); + // $this->artisan('execute')->assertOk(); + // $this->artisan('execute:rollback')->assertOk(); + // $this->assertEquals(0, Executor::count()); + // } - public function testRollbackFailed() - { - $this->artisan('execute:rollback')->assertFailed(); - } + // public function testRollbackFailed() + // { + // $this->artisan('execute:rollback')->assertFailed(); + // } - public function testFreshExecutors() - { - $this->testMakeExecutor(); - $this->artisan('execute:fresh')->assertOk(); - $this->assertEquals(1, Executor::count()); - } + // public function testFreshExecutors() + // { + // $this->testMakeExecutor(); + // $this->artisan('execute:fresh')->assertOk(); + // $this->assertEquals(1, Executor::count()); + // } - public function testStatusOfExecutors() - { - $this->testMakeExecutor(); + // public function testStatusOfExecutors() + // { + // $this->testMakeExecutor(); - $executors = (new ExecutorService())->sync(); + // $executors = (new ExecutorService())->sync(); - $this->artisan('execute:status') - ->assertOk() - ->expectsTable( - ['Name', 'Type', 'Tag', 'Batch', 'Executed'], - $executors->map(function ($executor) { - return [ - $executor['name'], - ucfirst($executor['type']->name), - $executor['tag'], - $executor['model']->batch, - $executor['model']->executed > 0 ? 'Yes' : 'No', - ]; - }) - ); - } + // $this->artisan('execute:status') + // ->assertOk() + // ->expectsTable( + // ['Name', 'Type', 'Tag', 'Batch', 'Executed'], + // $executors->map(function ($executor) { + // return [ + // $executor['name'], + // ucfirst($executor['type']->name), + // $executor['tag'], + // $executor['model']->batch, + // $executor['model']->executed > 0 ? 'Yes' : 'No', + // ]; + // }) + // ); + // } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 820e273..c1a9913 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -8,12 +8,12 @@ class TestCase extends OrchestraTestCase { - public function setUp(): void - { - parent::setUp(); + // public function setUp(): void + // { + // parent::setUp(); - File::deleteDirectory(base_path('executors')); - } + // File::deleteDirectory(base_path('executors')); + // } /** * add the package provider @@ -36,9 +36,9 @@ protected function getEnvironmentSetUp($app) { $app['config']->set('database.default', 'testing'); $app['config']->set('database.connections.testing', [ - 'driver' => 'sqlite', + 'driver' => 'sqlite', 'database' => ':memory:', - 'prefix' => '', + 'prefix' => '', ]); } } From e453bcee43bbf61263b5abebcf07d05d0f959b04 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 21:43:52 +0300 Subject: [PATCH 13/55] feat: add ExecutorManager and ExecutorPool classes for managing executor instances --- src/Classes/ExecutorManager.php | 18 ++++++++++ ...ExecutorPoolClass.php => ExecutorPool.php} | 36 +++++++++++++++---- 2 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 src/Classes/ExecutorManager.php rename src/Classes/{ExecutorPoolClass.php => ExecutorPool.php} (50%) diff --git a/src/Classes/ExecutorManager.php b/src/Classes/ExecutorManager.php new file mode 100644 index 0000000..64c49d0 --- /dev/null +++ b/src/Classes/ExecutorManager.php @@ -0,0 +1,18 @@ +pool = new ExecutorPool(); + } +} diff --git a/src/Classes/ExecutorPoolClass.php b/src/Classes/ExecutorPool.php similarity index 50% rename from src/Classes/ExecutorPoolClass.php rename to src/Classes/ExecutorPool.php index 66cf543..ed7ccab 100644 --- a/src/Classes/ExecutorPoolClass.php +++ b/src/Classes/ExecutorPool.php @@ -2,7 +2,9 @@ namespace Pharaonic\Laravel\Executor\Classes; -class ExecutorPoolClass +use Illuminate\Support\Facades\File; + +class ExecutorPool { /** * These paths for all pools of executors. @@ -16,6 +18,17 @@ public function __construct() $this->paths = [base_path('executors')]; } + /** + * Add a new path to executors pools. + * + * @param string $path + * @return void + */ + public function addPath(string $path): void + { + array_push($this->paths, $path); + } + /** * Return all pools of executors. * @@ -27,13 +40,24 @@ public function getPaths(): array } /** - * Add a new path to executors pools. + * Return all executors paths from all pools. * - * @param string $path - * @return void + * @return array */ - public function addPath(string $path): void + public function getExecutorsPaths() { - array_push($this->paths, $path); + $list = []; + + foreach ($this->paths as $path) { + if (File::isDirectory($path) && ! File::isEmptyDirectory($path, true)) { + foreach (File::files($path) as $executor) { + if ($executor->getExtension() === 'php') { + array_push($list, $executor->getRealPath()); + } + } + } + } + + return $list; } } From d11c5a1e145fc8a7fd0a0480e0ed07b8df2c37be Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 21:45:28 +0300 Subject: [PATCH 14/55] feat: implement abstract Executor class for managing execution logic --- src/Classes/Executor.php | 97 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/Classes/Executor.php diff --git a/src/Classes/Executor.php b/src/Classes/Executor.php new file mode 100644 index 0000000..76bcaf5 --- /dev/null +++ b/src/Classes/Executor.php @@ -0,0 +1,97 @@ +type instanceof ExecutorType)) { + throw new \Exception('The type of the executor must be an instance of ExecutorType.'); + } + + if (! is_null($this->tags) && ! is_array($this->tags)) { + throw new \Exception('The tags of the executor must be an array or null.'); + } + + $this->output = new OutputStyle(new ArgvInput(), new ConsoleOutput()); + } + + /** + * Dispatch a job to its appropriate handler. + * + * @param string|object $job + * @param mixed ...$arguments + * @return PendingDispatch + */ + final protected function job(string|object $job, ...$arguments): PendingDispatch + { + if (is_callable($job)) { + throw new \Exception('The job must be a string or an object.'); + } + + return dispatch(is_string($job) ? new $job(...$arguments) : $job); + } + + /** + * Execute the command. + * + * @param string $command + * @param array $parameters + * @return int + */ + final protected function command(string $command, array $parameters = []): int + { + return Artisan::call($command, $parameters, $this->output); + } +} From 2b73b71479efc875b6f4f599caee4b92853f478a Mon Sep 17 00:00:00 2001 From: MoamenEltouny Date: Thu, 4 Sep 2025 18:45:48 +0000 Subject: [PATCH 15/55] Fixing php-cs --- .php-cs-fixer.cache | 2 +- src/Models/Executor.php | 4 ++-- tests/ExecutorTest.php | 1 - tests/TestCase.php | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 60a0724..c240489 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.4.12","version":"3.87.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_extra_blank_lines":{"tokens":["use"]},"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","const_import","do","else","elseif","final","finally","for","foreach","function","function_import","if","insteadof","interface","namespace","new","private","protected","public","static","switch","trait","try","use","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"not_operator_with_successor_space":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true},"hashes":{"src\/Classes\/ExecutorPoolClass.php":"49d8adcc58eeeef618d5a1570ea7238c","src\/Traits\/InteractsWithIO.php":"2c14bbe144793d3173ecf119463ac1c0","src\/Facades\/ExecutorPool.php":"37d21a5c610b443ff7e373542cfeae70","src\/Executor.php":"6af562b05b62f0c0f9862ae322df67e7","src\/Enums\/ExecutorType.php":"7add25f4f0054055062fc63458a554be","src\/Models\/Executor.php":"2c6a2c1e00c09ee9129d0aece8017a57","src\/ExecutorServiceProvider.php":"6603e7955041b2aac7818f7ee384b511","src\/Console\/ExecuteMakeCommand.php":"e383e7764a04e63ee2e76abe18321338","src\/Console\/ExecuteCommand.php":"328e4ea27ac1781f3f6db79ee5d99429","src\/Console\/ExecuteFreshCommand.php":"a0fb7c2f8889ed26dc248d865bc58513","src\/Console\/ExecuteStatusCommand.php":"7f7e63dbf0b46edfe1fec1c551fa7a86","src\/Console\/ExecuteRollbackCommand.php":"6cc2f82e106f5bdb0721104ee8602668","src\/Services\/ExecutorService.php":"64deee9fe9e90aff30faf8de8b926845","tests\/TestCase.php":"d3d4ad73937ddf34e46cdb8e4d248135","tests\/ExecutorTest.php":"0d04893fe42b79626ad77a636a4f1ac8"}} \ No newline at end of file +{"php":"8.4.12","version":"3.87.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_extra_blank_lines":{"tokens":["use"]},"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","const_import","do","else","elseif","final","finally","for","foreach","function","function_import","if","insteadof","interface","namespace","new","private","protected","public","static","switch","trait","try","use","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"not_operator_with_successor_space":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true},"hashes":{"src\/Classes\/ExecutorPoolClass.php":"49d8adcc58eeeef618d5a1570ea7238c","src\/Traits\/InteractsWithIO.php":"2c14bbe144793d3173ecf119463ac1c0","src\/Facades\/ExecutorPool.php":"37d21a5c610b443ff7e373542cfeae70","src\/Executor.php":"6af562b05b62f0c0f9862ae322df67e7","src\/Enums\/ExecutorType.php":"7add25f4f0054055062fc63458a554be","src\/Models\/Executor.php":"ab34545b09cc956081f8cda175f85d18","src\/ExecutorServiceProvider.php":"6603e7955041b2aac7818f7ee384b511","src\/Console\/ExecuteMakeCommand.php":"e383e7764a04e63ee2e76abe18321338","src\/Console\/ExecuteCommand.php":"328e4ea27ac1781f3f6db79ee5d99429","src\/Console\/ExecuteFreshCommand.php":"a0fb7c2f8889ed26dc248d865bc58513","src\/Console\/ExecuteStatusCommand.php":"7f7e63dbf0b46edfe1fec1c551fa7a86","src\/Console\/ExecuteRollbackCommand.php":"6cc2f82e106f5bdb0721104ee8602668","src\/Services\/ExecutorService.php":"64deee9fe9e90aff30faf8de8b926845","tests\/TestCase.php":"c90f9bebd2cf5d4533f02e528c6d0c4e","tests\/ExecutorTest.php":"a626fa42f67de07c421e587b710b7e31","src\/Classes\/Executor.php":"d657acb9528a83abd2d6f9b88043d1df","src\/Classes\/ExecutorManager.php":"eeee446b3846e82641bfc19e0f5532b3","src\/Classes\/ExecutorPool.php":"030d3d41ffd50421bf79489610b80eb6"}} \ No newline at end of file diff --git a/src/Models/Executor.php b/src/Models/Executor.php index 21bba94..9a7288d 100644 --- a/src/Models/Executor.php +++ b/src/Models/Executor.php @@ -76,7 +76,7 @@ public function getTable() /** * Check if the executor is new (never executed). * - * @return boolean + * @return bool */ public function isNew() { @@ -86,7 +86,7 @@ public function isNew() /** * Check if the executor is executable. * - * @return boolean + * @return bool */ public function isExecutable() { diff --git a/tests/ExecutorTest.php b/tests/ExecutorTest.php index 2771cb0..b848e69 100644 --- a/tests/ExecutorTest.php +++ b/tests/ExecutorTest.php @@ -2,7 +2,6 @@ namespace Pharaonic\Laravel\Executor\Tests; -use Illuminate\Foundation\Testing\RefreshDatabase; use Pharaonic\Laravel\Executor\Models\Executor; use Pharaonic\Laravel\Executor\Services\ExecutorService; diff --git a/tests/TestCase.php b/tests/TestCase.php index c1a9913..5e07b17 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -36,9 +36,9 @@ protected function getEnvironmentSetUp($app) { $app['config']->set('database.default', 'testing'); $app['config']->set('database.connections.testing', [ - 'driver' => 'sqlite', + 'driver' => 'sqlite', 'database' => ':memory:', - 'prefix' => '', + 'prefix' => '', ]); } } From 44816c0a5e1fe26fa3ec74f81dbdbf5383df9b66 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 22:26:00 +0300 Subject: [PATCH 16/55] fix: add missing documentation comments for database connection and table configuration in executor --- config/executor.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/config/executor.php b/config/executor.php index fc2ad77..ba2fe33 100644 --- a/config/executor.php +++ b/config/executor.php @@ -1,6 +1,13 @@ env('DB_CONNECTION', config('database.default')), + + /** + * The table that should be used to store the executors. + */ 'table' => 'executors', ]; \ No newline at end of file From 75deaa1e3610cb1b3489248dcaefaa1c99ba8e08 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 22:34:52 +0300 Subject: [PATCH 17/55] fix: update executor configuration to use environment variables for connection and table settings; refactor Executor class for improved structure and error handling --- config/executor.php | 6 +-- src/Classes/Executor.php | 97 ---------------------------------------- src/Executor.php | 47 +++++++++++++++---- 3 files changed, 41 insertions(+), 109 deletions(-) delete mode 100644 src/Classes/Executor.php diff --git a/config/executor.php b/config/executor.php index ba2fe33..f8defa3 100644 --- a/config/executor.php +++ b/config/executor.php @@ -4,10 +4,10 @@ /** * The database connection that should be used by the executor. */ - 'connection' => env('DB_CONNECTION', config('database.default')), + 'connection' => env('EXECUTOR_CONNECTION', config('database.default')), /** * The table that should be used to store the executors. */ - 'table' => 'executors', -]; \ No newline at end of file + 'table' => env('EXECUTOR_TABLE', 'executors'), +]; diff --git a/src/Classes/Executor.php b/src/Classes/Executor.php deleted file mode 100644 index 76bcaf5..0000000 --- a/src/Classes/Executor.php +++ /dev/null @@ -1,97 +0,0 @@ -type instanceof ExecutorType)) { - throw new \Exception('The type of the executor must be an instance of ExecutorType.'); - } - - if (! is_null($this->tags) && ! is_array($this->tags)) { - throw new \Exception('The tags of the executor must be an array or null.'); - } - - $this->output = new OutputStyle(new ArgvInput(), new ConsoleOutput()); - } - - /** - * Dispatch a job to its appropriate handler. - * - * @param string|object $job - * @param mixed ...$arguments - * @return PendingDispatch - */ - final protected function job(string|object $job, ...$arguments): PendingDispatch - { - if (is_callable($job)) { - throw new \Exception('The job must be a string or an object.'); - } - - return dispatch(is_string($job) ? new $job(...$arguments) : $job); - } - - /** - * Execute the command. - * - * @param string $command - * @param array $parameters - * @return int - */ - final protected function command(string $command, array $parameters = []): int - { - return Artisan::call($command, $parameters, $this->output); - } -} diff --git a/src/Executor.php b/src/Executor.php index 8b4ef43..6e1d700 100644 --- a/src/Executor.php +++ b/src/Executor.php @@ -2,11 +2,11 @@ namespace Pharaonic\Laravel\Executor; +use Illuminate\Console\Concerns\InteractsWithIO; use Illuminate\Console\OutputStyle; use Illuminate\Foundation\Bus\PendingDispatch; use Illuminate\Support\Facades\Artisan; use Pharaonic\Laravel\Executor\Enums\ExecutorType; -use Pharaonic\Laravel\Executor\Traits\InteractsWithIO; use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Output\ConsoleOutput; @@ -22,18 +22,32 @@ abstract class Executor public $type = ExecutorType::Always; /** - * The tag of the executor. + * The tags of the executor. * - * @var string|null + * @var array|null */ - public $tag = null; + public $tags = null; /** - * Execute it. + * The servers of the executor. + * + * @var array|null + */ + public $servers = null; + + /** + * Run the executor. + * + * @return void + */ + abstract public function up(): void; + + /** + * Reverse the executor. * * @return void */ - abstract public function handle(): void; + abstract public function down(): void; /** * Create a new Executor instance. @@ -46,8 +60,12 @@ public function __construct() throw new \Exception('The type of the executor must be an instance of ExecutorType.'); } - if (! is_null($this->tag) && ! is_string($this->tag) && ! is_array($this->tag)) { - throw new \Exception('The tag of the executor must be a string or an array or null.'); + if (! is_null($this->tags) && ! is_array($this->tags)) { + throw new \Exception('The tags of the executor must be an array or null.'); + } + + if (! is_null($this->servers) && ! is_array($this->servers)) { + throw new \Exception('The servers of the executor must be an array of ips or null.'); } $this->output = new OutputStyle(new ArgvInput(), new ConsoleOutput()); @@ -80,4 +98,15 @@ final protected function command(string $command, array $parameters = []): int { return Artisan::call($command, $parameters, $this->output); } -} + + /** + * Seed the given class. + * + * @param string $class + * @return int + */ + final protected function seed(string $class): int + { + return $this->command('db:seed', ['--class' => $class]); + } +}; From facd9aa18873c7486eb09062997555331c0cddf4 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 22:35:28 +0300 Subject: [PATCH 18/55] refactor: consolidate executor properties into a single stub; remove separate tags and servers stubs --- stubs/executor.php.stub | 29 ++++++++++++++++++++--------- stubs/property/servers.stub | 6 ------ stubs/property/tags.stub | 6 ------ 3 files changed, 20 insertions(+), 21 deletions(-) delete mode 100644 stubs/property/servers.stub delete mode 100644 stubs/property/tags.stub diff --git a/stubs/executor.php.stub b/stubs/executor.php.stub index 1c5df5c..82522cd 100644 --- a/stubs/executor.php.stub +++ b/stubs/executor.php.stub @@ -3,34 +3,45 @@ use Pharaonic\Laravel\Executor\Enums\ExecutorType; use Pharaonic\Laravel\Executor\Executor; -return new class extends Executor -{ +return new class () extends Executor { /** * The type of the executor. * * @var ExecutorType */ public $type = ExecutorType::Always; - -{{ tags }} -{{ servers }} + + /** + * The tags of the executor. + * + * @var array|null + */ + public $tags = null; + + /** + * The servers that the executor will only run on. + * + * @var array|null + */ + public $servers = null; + /** * Run the executor. - * + * * @return void */ public function up(): void { - // + // } /** * Reverse the executor. - * + * * @return void */ public function down(): void { - // + // } }; diff --git a/stubs/property/servers.stub b/stubs/property/servers.stub deleted file mode 100644 index e1a652e..0000000 --- a/stubs/property/servers.stub +++ /dev/null @@ -1,6 +0,0 @@ - /** - * The servers of the executor. - * - * @var array|null - */ - public $servers = {{ servers }}; \ No newline at end of file diff --git a/stubs/property/tags.stub b/stubs/property/tags.stub deleted file mode 100644 index a7ae342..0000000 --- a/stubs/property/tags.stub +++ /dev/null @@ -1,6 +0,0 @@ - /** - * The tags of the executor. - * - * @var array|null - */ - public $tags = {{ tags }}; \ No newline at end of file From 410969433072a1c3814939e45a42454ff67c7648 Mon Sep 17 00:00:00 2001 From: MoamenEltouny Date: Thu, 4 Sep 2025 19:35:45 +0000 Subject: [PATCH 19/55] Fixing php-cs --- .php-cs-fixer.cache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index c240489..74667d0 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.4.12","version":"3.87.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_extra_blank_lines":{"tokens":["use"]},"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","const_import","do","else","elseif","final","finally","for","foreach","function","function_import","if","insteadof","interface","namespace","new","private","protected","public","static","switch","trait","try","use","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"not_operator_with_successor_space":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true},"hashes":{"src\/Classes\/ExecutorPoolClass.php":"49d8adcc58eeeef618d5a1570ea7238c","src\/Traits\/InteractsWithIO.php":"2c14bbe144793d3173ecf119463ac1c0","src\/Facades\/ExecutorPool.php":"37d21a5c610b443ff7e373542cfeae70","src\/Executor.php":"6af562b05b62f0c0f9862ae322df67e7","src\/Enums\/ExecutorType.php":"7add25f4f0054055062fc63458a554be","src\/Models\/Executor.php":"ab34545b09cc956081f8cda175f85d18","src\/ExecutorServiceProvider.php":"6603e7955041b2aac7818f7ee384b511","src\/Console\/ExecuteMakeCommand.php":"e383e7764a04e63ee2e76abe18321338","src\/Console\/ExecuteCommand.php":"328e4ea27ac1781f3f6db79ee5d99429","src\/Console\/ExecuteFreshCommand.php":"a0fb7c2f8889ed26dc248d865bc58513","src\/Console\/ExecuteStatusCommand.php":"7f7e63dbf0b46edfe1fec1c551fa7a86","src\/Console\/ExecuteRollbackCommand.php":"6cc2f82e106f5bdb0721104ee8602668","src\/Services\/ExecutorService.php":"64deee9fe9e90aff30faf8de8b926845","tests\/TestCase.php":"c90f9bebd2cf5d4533f02e528c6d0c4e","tests\/ExecutorTest.php":"a626fa42f67de07c421e587b710b7e31","src\/Classes\/Executor.php":"d657acb9528a83abd2d6f9b88043d1df","src\/Classes\/ExecutorManager.php":"eeee446b3846e82641bfc19e0f5532b3","src\/Classes\/ExecutorPool.php":"030d3d41ffd50421bf79489610b80eb6"}} \ No newline at end of file +{"php":"8.4.12","version":"3.87.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_extra_blank_lines":{"tokens":["use"]},"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","const_import","do","else","elseif","final","finally","for","foreach","function","function_import","if","insteadof","interface","namespace","new","private","protected","public","static","switch","trait","try","use","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"not_operator_with_successor_space":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true},"hashes":{"src\/Classes\/ExecutorPoolClass.php":"49d8adcc58eeeef618d5a1570ea7238c","src\/Traits\/InteractsWithIO.php":"2c14bbe144793d3173ecf119463ac1c0","src\/Facades\/ExecutorPool.php":"37d21a5c610b443ff7e373542cfeae70","src\/Executor.php":"038a2e660a7405b3da7cc04911fa862b","src\/Enums\/ExecutorType.php":"7add25f4f0054055062fc63458a554be","src\/Models\/Executor.php":"ab34545b09cc956081f8cda175f85d18","src\/ExecutorServiceProvider.php":"6603e7955041b2aac7818f7ee384b511","src\/Console\/ExecuteMakeCommand.php":"e383e7764a04e63ee2e76abe18321338","src\/Console\/ExecuteCommand.php":"328e4ea27ac1781f3f6db79ee5d99429","src\/Console\/ExecuteFreshCommand.php":"a0fb7c2f8889ed26dc248d865bc58513","src\/Console\/ExecuteStatusCommand.php":"7f7e63dbf0b46edfe1fec1c551fa7a86","src\/Console\/ExecuteRollbackCommand.php":"6cc2f82e106f5bdb0721104ee8602668","src\/Services\/ExecutorService.php":"64deee9fe9e90aff30faf8de8b926845","tests\/TestCase.php":"c90f9bebd2cf5d4533f02e528c6d0c4e","tests\/ExecutorTest.php":"a626fa42f67de07c421e587b710b7e31","src\/Classes\/Executor.php":"d657acb9528a83abd2d6f9b88043d1df","src\/Classes\/ExecutorManager.php":"eeee446b3846e82641bfc19e0f5532b3","src\/Classes\/ExecutorPool.php":"030d3d41ffd50421bf79489610b80eb6"}} \ No newline at end of file From 3d5d0f929ef0b8132b87fbb93db1afdf3595a45a Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 22:43:59 +0300 Subject: [PATCH 20/55] feat: add Executor class with job dispatching and command execution methods --- src/{ => Classes}/Executor.php | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) rename src/{ => Classes}/Executor.php (81%) diff --git a/src/Executor.php b/src/Classes/Executor.php similarity index 81% rename from src/Executor.php rename to src/Classes/Executor.php index 6e1d700..c9fb519 100644 --- a/src/Executor.php +++ b/src/Classes/Executor.php @@ -1,6 +1,6 @@ tags) && ! is_array($this->tags)) { - throw new \Exception('The tags of the executor must be an array or null.'); + if (! is_array($this->tags)) { + throw new \Exception('The tags of the executor must be an array.'); } - if (! is_null($this->servers) && ! is_array($this->servers)) { - throw new \Exception('The servers of the executor must be an array of ips or null.'); + if (! is_array($this->servers)) { + throw new \Exception('The servers of the executor must be an array of ips.'); } $this->output = new OutputStyle(new ArgvInput(), new ConsoleOutput()); From 60490486b428656e5c38786f025ffab57a44183b Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 22:44:06 +0300 Subject: [PATCH 21/55] fix: update executor class import and initialize tags and servers properties as empty arrays --- stubs/executor.php.stub | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stubs/executor.php.stub b/stubs/executor.php.stub index 82522cd..4b09d23 100644 --- a/stubs/executor.php.stub +++ b/stubs/executor.php.stub @@ -1,7 +1,7 @@ Date: Thu, 4 Sep 2025 19:44:24 +0000 Subject: [PATCH 22/55] Fixing php-cs --- .php-cs-fixer.cache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 74667d0..82c382d 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.4.12","version":"3.87.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_extra_blank_lines":{"tokens":["use"]},"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","const_import","do","else","elseif","final","finally","for","foreach","function","function_import","if","insteadof","interface","namespace","new","private","protected","public","static","switch","trait","try","use","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"not_operator_with_successor_space":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true},"hashes":{"src\/Classes\/ExecutorPoolClass.php":"49d8adcc58eeeef618d5a1570ea7238c","src\/Traits\/InteractsWithIO.php":"2c14bbe144793d3173ecf119463ac1c0","src\/Facades\/ExecutorPool.php":"37d21a5c610b443ff7e373542cfeae70","src\/Executor.php":"038a2e660a7405b3da7cc04911fa862b","src\/Enums\/ExecutorType.php":"7add25f4f0054055062fc63458a554be","src\/Models\/Executor.php":"ab34545b09cc956081f8cda175f85d18","src\/ExecutorServiceProvider.php":"6603e7955041b2aac7818f7ee384b511","src\/Console\/ExecuteMakeCommand.php":"e383e7764a04e63ee2e76abe18321338","src\/Console\/ExecuteCommand.php":"328e4ea27ac1781f3f6db79ee5d99429","src\/Console\/ExecuteFreshCommand.php":"a0fb7c2f8889ed26dc248d865bc58513","src\/Console\/ExecuteStatusCommand.php":"7f7e63dbf0b46edfe1fec1c551fa7a86","src\/Console\/ExecuteRollbackCommand.php":"6cc2f82e106f5bdb0721104ee8602668","src\/Services\/ExecutorService.php":"64deee9fe9e90aff30faf8de8b926845","tests\/TestCase.php":"c90f9bebd2cf5d4533f02e528c6d0c4e","tests\/ExecutorTest.php":"a626fa42f67de07c421e587b710b7e31","src\/Classes\/Executor.php":"d657acb9528a83abd2d6f9b88043d1df","src\/Classes\/ExecutorManager.php":"eeee446b3846e82641bfc19e0f5532b3","src\/Classes\/ExecutorPool.php":"030d3d41ffd50421bf79489610b80eb6"}} \ No newline at end of file +{"php":"8.4.12","version":"3.87.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_extra_blank_lines":{"tokens":["use"]},"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","const_import","do","else","elseif","final","finally","for","foreach","function","function_import","if","insteadof","interface","namespace","new","private","protected","public","static","switch","trait","try","use","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"not_operator_with_successor_space":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true},"hashes":{"src\/Classes\/ExecutorPoolClass.php":"49d8adcc58eeeef618d5a1570ea7238c","src\/Traits\/InteractsWithIO.php":"2c14bbe144793d3173ecf119463ac1c0","src\/Facades\/ExecutorPool.php":"37d21a5c610b443ff7e373542cfeae70","src\/Executor.php":"038a2e660a7405b3da7cc04911fa862b","src\/Enums\/ExecutorType.php":"7add25f4f0054055062fc63458a554be","src\/Models\/Executor.php":"ab34545b09cc956081f8cda175f85d18","src\/ExecutorServiceProvider.php":"6603e7955041b2aac7818f7ee384b511","src\/Console\/ExecuteMakeCommand.php":"e383e7764a04e63ee2e76abe18321338","src\/Console\/ExecuteCommand.php":"328e4ea27ac1781f3f6db79ee5d99429","src\/Console\/ExecuteFreshCommand.php":"a0fb7c2f8889ed26dc248d865bc58513","src\/Console\/ExecuteStatusCommand.php":"7f7e63dbf0b46edfe1fec1c551fa7a86","src\/Console\/ExecuteRollbackCommand.php":"6cc2f82e106f5bdb0721104ee8602668","src\/Services\/ExecutorService.php":"64deee9fe9e90aff30faf8de8b926845","tests\/TestCase.php":"c90f9bebd2cf5d4533f02e528c6d0c4e","tests\/ExecutorTest.php":"a626fa42f67de07c421e587b710b7e31","src\/Classes\/Executor.php":"1ee60c6f2d3a19395d7be649f9d6d3c6","src\/Classes\/ExecutorManager.php":"eeee446b3846e82641bfc19e0f5532b3","src\/Classes\/ExecutorPool.php":"030d3d41ffd50421bf79489610b80eb6"}} \ No newline at end of file From 42a14b687716c6756bf5c73b4a710ee85064c8cc Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 23:00:39 +0300 Subject: [PATCH 23/55] chore: remove .php-cs-fixer.cache and clean up .gitignore --- .gitignore | 1 - .php-cs-fixer.cache | 1 - 2 files changed, 2 deletions(-) delete mode 100644 .php-cs-fixer.cache diff --git a/.gitignore b/.gitignore index ee34d49..929319b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ /vendor composer.lock -.phpunit.cache .phpunit.result.cache .php-cs-fixer.cache \ No newline at end of file diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache deleted file mode 100644 index 82c382d..0000000 --- a/.php-cs-fixer.cache +++ /dev/null @@ -1 +0,0 @@ -{"php":"8.4.12","version":"3.87.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_extra_blank_lines":{"tokens":["use"]},"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","const_import","do","else","elseif","final","finally","for","foreach","function","function_import","if","insteadof","interface","namespace","new","private","protected","public","static","switch","trait","try","use","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"not_operator_with_successor_space":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true},"hashes":{"src\/Classes\/ExecutorPoolClass.php":"49d8adcc58eeeef618d5a1570ea7238c","src\/Traits\/InteractsWithIO.php":"2c14bbe144793d3173ecf119463ac1c0","src\/Facades\/ExecutorPool.php":"37d21a5c610b443ff7e373542cfeae70","src\/Executor.php":"038a2e660a7405b3da7cc04911fa862b","src\/Enums\/ExecutorType.php":"7add25f4f0054055062fc63458a554be","src\/Models\/Executor.php":"ab34545b09cc956081f8cda175f85d18","src\/ExecutorServiceProvider.php":"6603e7955041b2aac7818f7ee384b511","src\/Console\/ExecuteMakeCommand.php":"e383e7764a04e63ee2e76abe18321338","src\/Console\/ExecuteCommand.php":"328e4ea27ac1781f3f6db79ee5d99429","src\/Console\/ExecuteFreshCommand.php":"a0fb7c2f8889ed26dc248d865bc58513","src\/Console\/ExecuteStatusCommand.php":"7f7e63dbf0b46edfe1fec1c551fa7a86","src\/Console\/ExecuteRollbackCommand.php":"6cc2f82e106f5bdb0721104ee8602668","src\/Services\/ExecutorService.php":"64deee9fe9e90aff30faf8de8b926845","tests\/TestCase.php":"c90f9bebd2cf5d4533f02e528c6d0c4e","tests\/ExecutorTest.php":"a626fa42f67de07c421e587b710b7e31","src\/Classes\/Executor.php":"1ee60c6f2d3a19395d7be649f9d6d3c6","src\/Classes\/ExecutorManager.php":"eeee446b3846e82641bfc19e0f5532b3","src\/Classes\/ExecutorPool.php":"030d3d41ffd50421bf79489610b80eb6"}} \ No newline at end of file From 4539d1428044871f6c5a9ded2239687b2b6ef194 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 23:01:43 +0300 Subject: [PATCH 24/55] feat: create Executor facade for pharaonic.executor.manager --- src/Facades/{ExecutorPool.php => Executor.php} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename src/Facades/{ExecutorPool.php => Executor.php} (50%) diff --git a/src/Facades/ExecutorPool.php b/src/Facades/Executor.php similarity index 50% rename from src/Facades/ExecutorPool.php rename to src/Facades/Executor.php index e001671..604234c 100644 --- a/src/Facades/ExecutorPool.php +++ b/src/Facades/Executor.php @@ -5,13 +5,13 @@ use Illuminate\Support\Facades\Facade; /** - * @method static array getPaths() - * @method static void addPath(string $path) + * @method static array info() + * @method static void run() */ -class ExecutorPool extends Facade +class Executor extends Facade { protected static function getFacadeAccessor() { - return 'pharaonic.executor.executorPool'; + return 'pharaonic.executor.manager'; } } From 34c94d96abbe0c1add75f48c8bec6efc384e5a50 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 23:16:28 +0300 Subject: [PATCH 25/55] feat: implement info and run methods in ExecutorManager class --- src/Classes/ExecutorManager.php | 20 ++++++++++++++++++++ src/ExecutorServiceProvider.php | 31 ++++++++++++++++++------------- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/Classes/ExecutorManager.php b/src/Classes/ExecutorManager.php index 64c49d0..06a5e03 100644 --- a/src/Classes/ExecutorManager.php +++ b/src/Classes/ExecutorManager.php @@ -15,4 +15,24 @@ public function __construct() { $this->pool = new ExecutorPool(); } + + /** + * Get info about all executors. + * + * @return array + */ + public function info() + { + // + } + + /** + * Run all the executors. + * + * @return void + */ + public function run() + { + // + } } diff --git a/src/ExecutorServiceProvider.php b/src/ExecutorServiceProvider.php index 2caaaac..e06d0dc 100644 --- a/src/ExecutorServiceProvider.php +++ b/src/ExecutorServiceProvider.php @@ -3,6 +3,7 @@ namespace Pharaonic\Laravel\Executor; use Illuminate\Support\ServiceProvider; +use Pharaonic\Laravel\Executor\Classes\ExecutorManager; use Pharaonic\Laravel\Executor\Classes\ExecutorPoolClass; use Pharaonic\Laravel\Executor\Console\ExecuteCommand; use Pharaonic\Laravel\Executor\Console\ExecuteFreshCommand; @@ -19,7 +20,10 @@ class ExecutorServiceProvider extends ServiceProvider */ public function register() { - $this->app->singleton('pharaonic.executor.executorPool', ExecutorPoolClass::class); + $this->loadMigrationsFrom(__DIR__ . '/../database/migrations'); + $this->mergeConfigFrom(__DIR__ . '/../config/executor.php', 'pharaonic.executor'); + + $this->app->singleton('pharaonic.executor.manager', ExecutorManager::class); } /** @@ -30,23 +34,24 @@ public function register() public function boot() { if ($this->app->runningInConsole()) { - // Publish Migrations $this->publishes( [__DIR__ . '/../database/migrations' => database_path('migrations')], - ['pharaonic', 'migrations', 'executor-migrations'] + ['pharaonic', 'migrations', 'laravel-executor'] ); - // Load Migrations - $this->loadMigrationsFrom(__DIR__ . '/../database/migrations'); + $this->publishes( + [__DIR__ . '/../config/executor.php' => config_path('pharaonic/executor.php')], + ['pharaonic', 'config', 'laravel-executor'] + ); - // Load Commands - $this->commands([ - ExecuteCommand::class, - ExecuteMakeCommand::class, - ExecuteRollbackCommand::class, - ExecuteFreshCommand::class, - ExecuteStatusCommand::class, - ]); + // // Load Commands + // $this->commands([ + // ExecuteCommand::class, + // ExecuteMakeCommand::class, + // ExecuteRollbackCommand::class, + // ExecuteFreshCommand::class, + // ExecuteStatusCommand::class, + // ]); } } } From e0266df9e2d95df26e0782354d5e0cfd6594893f Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 23:16:38 +0300 Subject: [PATCH 26/55] feat: add ExecutorItem class and refactor ExecutorPool to manage executor items --- src/Classes/ExecutorItem.php | 30 ++++++++++++++++++++++++++++++ src/Classes/ExecutorPool.php | 13 ++++++++++--- 2 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 src/Classes/ExecutorItem.php diff --git a/src/Classes/ExecutorItem.php b/src/Classes/ExecutorItem.php new file mode 100644 index 0000000..a3fc203 --- /dev/null +++ b/src/Classes/ExecutorItem.php @@ -0,0 +1,30 @@ +executor = $executor; + $this->path = $path; + } + + /** + * Get info about the executor. + * + * @return array + */ + public function info() + { + return [ + 'type' => $this->executor->type, + 'tags' => $this->executor->tags, + 'servers' => $this->executor->servers, + 'path' => $this->path, + ]; + } +} \ No newline at end of file diff --git a/src/Classes/ExecutorPool.php b/src/Classes/ExecutorPool.php index ed7ccab..afd9d24 100644 --- a/src/Classes/ExecutorPool.php +++ b/src/Classes/ExecutorPool.php @@ -11,7 +11,14 @@ class ExecutorPool * * @var array */ - private array $paths = []; + protected array $paths = []; + + /** + * All executors items. + * + * @var array + */ + protected array $items = []; public function __construct() { @@ -44,10 +51,10 @@ public function getPaths(): array * * @return array */ - public function getExecutorsPaths() + public function collect() { $list = []; - + foreach ($this->paths as $path) { if (File::isDirectory($path) && ! File::isEmptyDirectory($path, true)) { foreach (File::files($path) as $executor) { From bb43870477e87756e4086b94a73189bc73d60ab8 Mon Sep 17 00:00:00 2001 From: MoamenEltouny Date: Thu, 4 Sep 2025 20:16:51 +0000 Subject: [PATCH 27/55] Fixing php-cs --- src/Classes/ExecutorItem.php | 8 ++++---- src/Classes/ExecutorManager.php | 2 +- src/Classes/ExecutorPool.php | 2 +- src/ExecutorServiceProvider.php | 1 - 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Classes/ExecutorItem.php b/src/Classes/ExecutorItem.php index a3fc203..8c76232 100644 --- a/src/Classes/ExecutorItem.php +++ b/src/Classes/ExecutorItem.php @@ -21,10 +21,10 @@ public function __construct(Executor $executor, string $path) public function info() { return [ - 'type' => $this->executor->type, - 'tags' => $this->executor->tags, + 'type' => $this->executor->type, + 'tags' => $this->executor->tags, 'servers' => $this->executor->servers, - 'path' => $this->path, + 'path' => $this->path, ]; } -} \ No newline at end of file +} diff --git a/src/Classes/ExecutorManager.php b/src/Classes/ExecutorManager.php index 06a5e03..95889d2 100644 --- a/src/Classes/ExecutorManager.php +++ b/src/Classes/ExecutorManager.php @@ -23,7 +23,7 @@ public function __construct() */ public function info() { - // + // } /** diff --git a/src/Classes/ExecutorPool.php b/src/Classes/ExecutorPool.php index afd9d24..3e65eca 100644 --- a/src/Classes/ExecutorPool.php +++ b/src/Classes/ExecutorPool.php @@ -54,7 +54,7 @@ public function getPaths(): array public function collect() { $list = []; - + foreach ($this->paths as $path) { if (File::isDirectory($path) && ! File::isEmptyDirectory($path, true)) { foreach (File::files($path) as $executor) { diff --git a/src/ExecutorServiceProvider.php b/src/ExecutorServiceProvider.php index e06d0dc..ff63905 100644 --- a/src/ExecutorServiceProvider.php +++ b/src/ExecutorServiceProvider.php @@ -4,7 +4,6 @@ use Illuminate\Support\ServiceProvider; use Pharaonic\Laravel\Executor\Classes\ExecutorManager; -use Pharaonic\Laravel\Executor\Classes\ExecutorPoolClass; use Pharaonic\Laravel\Executor\Console\ExecuteCommand; use Pharaonic\Laravel\Executor\Console\ExecuteFreshCommand; use Pharaonic\Laravel\Executor\Console\ExecuteMakeCommand; From 01ffa24ea1f764faccf7b39dcc8049bbbc3b667c Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 23:36:49 +0300 Subject: [PATCH 28/55] feat: add servers column to executors table and update ExecutorItem to use SplFileInfo --- ...24_07_07_000001_create_executors_table.php | 1 + src/Classes/ExecutorItem.php | 49 ++++++++++++++++--- src/Classes/ExecutorPool.php | 22 ++++++--- 3 files changed, 57 insertions(+), 15 deletions(-) diff --git a/database/migrations/2024_07_07_000001_create_executors_table.php b/database/migrations/2024_07_07_000001_create_executors_table.php index 5020cb5..b9e2cd4 100644 --- a/database/migrations/2024_07_07_000001_create_executors_table.php +++ b/database/migrations/2024_07_07_000001_create_executors_table.php @@ -20,6 +20,7 @@ public function up() $table->unsignedTinyInteger('type')->default(ExecutorType::Always); $table->string('name'); $table->json('tags')->nullable(); + $table->json('servers')->nullable(); $table->integer('batch')->default(1); $table->integer('executed')->default(0); $table->timestamp('last_executed_at')->nullable(); diff --git a/src/Classes/ExecutorItem.php b/src/Classes/ExecutorItem.php index 8c76232..b563916 100644 --- a/src/Classes/ExecutorItem.php +++ b/src/Classes/ExecutorItem.php @@ -2,15 +2,28 @@ namespace Pharaonic\Laravel\Executor\Classes; +use Symfony\Component\Finder\SplFileInfo; + class ExecutorItem { + /** + * The executor instance. + * + * @var Executor + */ public Executor $executor; - public string $path; - public function __construct(Executor $executor, string $path) + /** + * The file associated with the executor. + * + * @var SplFileInfo + */ + public SplFileInfo $file; + + public function __construct(Executor $executor, SplFileInfo $file) { $this->executor = $executor; - $this->path = $path; + $this->file = $file; } /** @@ -21,10 +34,32 @@ public function __construct(Executor $executor, string $path) public function info() { return [ - 'type' => $this->executor->type, - 'tags' => $this->executor->tags, - 'servers' => $this->executor->servers, - 'path' => $this->path, + 'name' => basename($this->file->getFileName(), '.php'), + 'path' => $this->file->getRealPath(), + + 'type' => $this->executor->type->name, + 'tags' => $this->executor->tags ?: null, + 'servers' => $this->executor->servers ?: null, ]; } + + /** + * Run the executor. + * + * @return void + */ + public function run() + { + $this->executor->up(); + } + + /** + * Reverse the executor. + * + * @return void + */ + public function reverse() + { + $this->executor->down(); + } } diff --git a/src/Classes/ExecutorPool.php b/src/Classes/ExecutorPool.php index 3e65eca..4e450dd 100644 --- a/src/Classes/ExecutorPool.php +++ b/src/Classes/ExecutorPool.php @@ -47,24 +47,30 @@ public function getPaths(): array } /** - * Return all executors paths from all pools. + * Collect all executors from the defined paths. * - * @return array + * @return void */ public function collect() { - $list = []; + if (! empty($this->items)) { + return; + } foreach ($this->paths as $path) { if (File::isDirectory($path) && ! File::isEmptyDirectory($path, true)) { - foreach (File::files($path) as $executor) { - if ($executor->getExtension() === 'php') { - array_push($list, $executor->getRealPath()); + foreach (File::files($path) as $file) { + if ($file->getExtension() === 'php') { + $obj = include $file->getRealPath(); + + if (! $obj instanceof Executor) { + continue; + } + + array_push($this->items, new ExecutorItem($obj, $file)); } } } } - - return $list; } } From 836f2514c6ebc6721ae8009cbe28640a0c49e79b Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 23:41:29 +0300 Subject: [PATCH 29/55] refactor: remove unused options from ExecuteMakeCommand and clean up ExecutorServiceProvider command registration --- src/Console/ExecuteMakeCommand.php | 80 +----------------------------- src/ExecutorServiceProvider.php | 7 ++- 2 files changed, 4 insertions(+), 83 deletions(-) diff --git a/src/Console/ExecuteMakeCommand.php b/src/Console/ExecuteMakeCommand.php index 4e7abe4..469158e 100644 --- a/src/Console/ExecuteMakeCommand.php +++ b/src/Console/ExecuteMakeCommand.php @@ -14,8 +14,6 @@ class ExecuteMakeCommand extends GeneratorCommand * @var string */ protected $signature = 'execute:make {name} - {--o|once : Create a new executor class that will be executed once} - {--tag= : The tag of the executor} {--path= : The path of the executor}'; /** @@ -41,83 +39,7 @@ protected function getStub() { return __DIR__ . '/../../stubs/executor.php.stub'; } - - /** - * Replace the class name for the given stub. - * - * @param string $stub - * @param string $name - * @return string - */ - protected function replaceClass($stub, $name) - { - $stub = parent::replaceClass($stub, $name); - - $this->setTypeProperty($stub); - $this->setTagProperty($stub); - - return $stub; - } - - /** - * Set the type property for the executor. - * - * @param string $stub - * @return void - */ - protected function setTypeProperty(string &$stub) - { - if (! $this->option('once')) { - $stub = str_replace( - [ - "{{ type }}\n", - "{{ type-use }}\n", - ], - '', - $stub - ); - - return; - } - - $stub = str_replace( - [ - '{{ type-use }}', - '{{ type }}', - ], - [ - 'use Pharaonic\Laravel\Executor\Enums\ExecutorType;', - file_get_contents(__DIR__ . '/../../stubs/property/once.stub') . PHP_EOL, - ], - $stub - ); - } - - /** - * Replace the once option for the given stub. - * - * @param string $stub - * @return void - */ - protected function setTagProperty(string &$stub) - { - if (! $this->option('tag')) { - $stub = str_replace("{{ tag }}\n", '', $stub); - - return; - } - - $stub = str_replace( - '{{ tag }}', - str_replace( - '{{ tag }}', - '"' . $this->option('tag') . '"', - file_get_contents(__DIR__ . '/../../stubs/property/tag.stub') - ) . PHP_EOL, - $stub - ); - } - + /** * Get the destination class path. * diff --git a/src/ExecutorServiceProvider.php b/src/ExecutorServiceProvider.php index ff63905..227ec87 100644 --- a/src/ExecutorServiceProvider.php +++ b/src/ExecutorServiceProvider.php @@ -43,14 +43,13 @@ public function boot() ['pharaonic', 'config', 'laravel-executor'] ); - // // Load Commands - // $this->commands([ + $this->commands([ // ExecuteCommand::class, - // ExecuteMakeCommand::class, + ExecuteMakeCommand::class, // ExecuteRollbackCommand::class, // ExecuteFreshCommand::class, // ExecuteStatusCommand::class, - // ]); + ]); } } } From de14e5e77d58ebc00dca23bfd9774e8675883599 Mon Sep 17 00:00:00 2001 From: MoamenEltouny Date: Thu, 4 Sep 2025 20:41:44 +0000 Subject: [PATCH 30/55] Fixing php-cs --- src/Console/ExecuteMakeCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/ExecuteMakeCommand.php b/src/Console/ExecuteMakeCommand.php index 469158e..b6c462d 100644 --- a/src/Console/ExecuteMakeCommand.php +++ b/src/Console/ExecuteMakeCommand.php @@ -39,7 +39,7 @@ protected function getStub() { return __DIR__ . '/../../stubs/executor.php.stub'; } - + /** * Get the destination class path. * From 34e9cbf44878a687f9eccb85fba07b7e393b5b66 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 23:42:54 +0300 Subject: [PATCH 31/55] feat: add AboutCommand to display version information in console --- src/ExecutorServiceProvider.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ExecutorServiceProvider.php b/src/ExecutorServiceProvider.php index 227ec87..396fe1e 100644 --- a/src/ExecutorServiceProvider.php +++ b/src/ExecutorServiceProvider.php @@ -2,6 +2,7 @@ namespace Pharaonic\Laravel\Executor; +use Illuminate\Foundation\Console\AboutCommand; use Illuminate\Support\ServiceProvider; use Pharaonic\Laravel\Executor\Classes\ExecutorManager; use Pharaonic\Laravel\Executor\Console\ExecuteCommand; @@ -33,6 +34,8 @@ public function register() public function boot() { if ($this->app->runningInConsole()) { + AboutCommand::add('Pharaonic', ['Executor' => '11.0.0']); + $this->publishes( [__DIR__ . '/../database/migrations' => database_path('migrations')], ['pharaonic', 'migrations', 'laravel-executor'] From fae3afbb08f4365d01abe3983646c5b47c7423ee Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Thu, 4 Sep 2025 23:52:28 +0300 Subject: [PATCH 32/55] fix: implement info method in ExecutorManager and update addPath and collect methods in ExecutorPool --- src/Classes/ExecutorManager.php | 2 +- src/Classes/ExecutorPool.php | 25 +++++++++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/Classes/ExecutorManager.php b/src/Classes/ExecutorManager.php index 95889d2..d5fb1b0 100644 --- a/src/Classes/ExecutorManager.php +++ b/src/Classes/ExecutorManager.php @@ -23,7 +23,7 @@ public function __construct() */ public function info() { - // + return $this->pool->collect()->info(); } /** diff --git a/src/Classes/ExecutorPool.php b/src/Classes/ExecutorPool.php index 4e450dd..3d9fc92 100644 --- a/src/Classes/ExecutorPool.php +++ b/src/Classes/ExecutorPool.php @@ -29,11 +29,13 @@ public function __construct() * Add a new path to executors pools. * * @param string $path - * @return void + * @return static */ - public function addPath(string $path): void + public function addPath(string $path) { array_push($this->paths, $path); + + return $this; } /** @@ -46,15 +48,28 @@ public function getPaths(): array return $this->paths; } + /** + * Get info about all executors. + * + * @return array + */ + public function info() + { + return collect($this->items)->map(function ($item) { + return $item->info(); + })->toArray(); + } + /** * Collect all executors from the defined paths. * - * @return void + * @return static */ public function collect() { if (! empty($this->items)) { - return; + $this->items = []; + // return; } foreach ($this->paths as $path) { @@ -72,5 +87,7 @@ public function collect() } } } + + return $this; } } From 353a357b571bf0b7abd1f1afc336fa575fd7b6e2 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 00:15:02 +0300 Subject: [PATCH 33/55] feat: enhance ExecutorItem and ExecutorPool to include model data and improve info retrieval --- src/Classes/ExecutorItem.php | 26 +++++++++++++++++++++++--- src/Classes/ExecutorManager.php | 16 +++++++++++++++- src/Classes/ExecutorPool.php | 11 +++++++++-- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/Classes/ExecutorItem.php b/src/Classes/ExecutorItem.php index b563916..c53be71 100644 --- a/src/Classes/ExecutorItem.php +++ b/src/Classes/ExecutorItem.php @@ -2,6 +2,7 @@ namespace Pharaonic\Laravel\Executor\Classes; +use Pharaonic\Laravel\Executor\Models\Executor as Model; use Symfony\Component\Finder\SplFileInfo; class ExecutorItem @@ -13,6 +14,20 @@ class ExecutorItem */ public Executor $executor; + /** + * The associated executor model. + * + * @var Model|null + */ + protected ?Model $model = null; + + /** + * The name of the executor. + * + * @var string + */ + public string $name; + /** * The file associated with the executor. * @@ -20,10 +35,12 @@ class ExecutorItem */ public SplFileInfo $file; - public function __construct(Executor $executor, SplFileInfo $file) + public function __construct(Executor $executor, SplFileInfo $file, string $name, ?Model $model = null) { $this->executor = $executor; $this->file = $file; + $this->name = $name; + $this->model = $model; } /** @@ -34,12 +51,15 @@ public function __construct(Executor $executor, SplFileInfo $file) public function info() { return [ - 'name' => basename($this->file->getFileName(), '.php'), + 'name' => $this->name, 'path' => $this->file->getRealPath(), - 'type' => $this->executor->type->name, + 'type' => $this->executor->type, 'tags' => $this->executor->tags ?: null, 'servers' => $this->executor->servers ?: null, + + 'executed' => $this->model?->executed > 0, + 'batch' => $this->model?->batch, ]; } diff --git a/src/Classes/ExecutorManager.php b/src/Classes/ExecutorManager.php index d5fb1b0..4c26624 100644 --- a/src/Classes/ExecutorManager.php +++ b/src/Classes/ExecutorManager.php @@ -2,6 +2,8 @@ namespace Pharaonic\Laravel\Executor\Classes; +use Pharaonic\Laravel\Executor\Models\Executor; + class ExecutorManager { /** @@ -16,6 +18,16 @@ public function __construct() $this->pool = new ExecutorPool(); } + /** + * Get all executor records. + * + * @return \Illuminate\Support\Collection + */ + protected function getRecords() + { + return Executor::all()->keyBy('executor'); + } + /** * Get info about all executors. * @@ -23,7 +35,9 @@ public function __construct() */ public function info() { - return $this->pool->collect()->info(); + return $this->pool + ->collect($this->getRecords()) + ->info(); } /** diff --git a/src/Classes/ExecutorPool.php b/src/Classes/ExecutorPool.php index 3d9fc92..583dbff 100644 --- a/src/Classes/ExecutorPool.php +++ b/src/Classes/ExecutorPool.php @@ -2,6 +2,7 @@ namespace Pharaonic\Laravel\Executor\Classes; +use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\File; class ExecutorPool @@ -63,9 +64,10 @@ public function info() /** * Collect all executors from the defined paths. * + * @param Collection $records * @return static */ - public function collect() + public function collect(Collection $records) { if (! empty($this->items)) { $this->items = []; @@ -82,7 +84,12 @@ public function collect() continue; } - array_push($this->items, new ExecutorItem($obj, $file)); + $name = basename($file->getFileName(), '.php'); + $record = $records->get($name); + array_push( + $this->items, + new ExecutorItem($obj, $file, $name, $record) + ); } } } From 7fd264aa1c587eae7294e6a6070665d80d5e26a5 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 00:15:22 +0300 Subject: [PATCH 34/55] fix: update docblock for file property in ExecutorItem to include parameter descriptions --- src/Classes/ExecutorItem.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Classes/ExecutorItem.php b/src/Classes/ExecutorItem.php index c53be71..22991b4 100644 --- a/src/Classes/ExecutorItem.php +++ b/src/Classes/ExecutorItem.php @@ -31,7 +31,10 @@ class ExecutorItem /** * The file associated with the executor. * - * @var SplFileInfo + * @param Executor $executor + * @param SplFileInfo $file + * @param string $name + * @param Model|null $model */ public SplFileInfo $file; From 54486362ce1316cfc371d07bb0d4b458bdb484ba Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 00:17:28 +0300 Subject: [PATCH 35/55] fix: reorder keys in info method return array for consistency --- src/Classes/ExecutorItem.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Classes/ExecutorItem.php b/src/Classes/ExecutorItem.php index 22991b4..010458f 100644 --- a/src/Classes/ExecutorItem.php +++ b/src/Classes/ExecutorItem.php @@ -56,13 +56,11 @@ public function info() return [ 'name' => $this->name, 'path' => $this->file->getRealPath(), - 'type' => $this->executor->type, 'tags' => $this->executor->tags ?: null, 'servers' => $this->executor->servers ?: null, - - 'executed' => $this->model?->executed > 0, 'batch' => $this->model?->batch, + 'executed' => $this->model?->executed > 0, ]; } From c705dddd0151376f7b71193f9a2ecef75e4b9f4e Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 00:20:38 +0300 Subject: [PATCH 36/55] refactor: simplify ExecuteStatusCommand by removing ExecutorService dependency and using Executor facade --- src/Console/ExecuteStatusCommand.php | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Console/ExecuteStatusCommand.php b/src/Console/ExecuteStatusCommand.php index cb34e70..b16106d 100644 --- a/src/Console/ExecuteStatusCommand.php +++ b/src/Console/ExecuteStatusCommand.php @@ -3,7 +3,7 @@ namespace Pharaonic\Laravel\Executor\Console; use Illuminate\Console\Command; -use Pharaonic\Laravel\Executor\Services\ExecutorService; +use Pharaonic\Laravel\Executor\Facades\Executor; class ExecuteStatusCommand extends Command { @@ -24,19 +24,18 @@ class ExecuteStatusCommand extends Command /** * Execute the console command. */ - public function handle(ExecutorService $service) + public function handle() { - $executors = $service->sync(); - $this->table( - ['Name', 'Type', 'Tag', 'Batch', 'Executed'], - $executors->map(function ($executor) { + ['Name', 'Type', 'Tags', 'Servers', 'Batch', 'Executed'], + collect(Executor::info())->map(function ($executor) { return [ $executor['name'], - ucfirst($executor['type']->name), - $executor['tag'], - $executor['model']->batch, - $executor['model']->executed > 0 ? 'Yes' : 'No', + $executor['type']->name, + empty($executor['tags']) ? 'None' : implode(', ', $executor['tags']), + empty($executor['servers']) ? 'All' : implode(', ', $executor['servers']), + $executor['batch'] ?? 'N/A', + $executor['executed'] ? 'Yes' : 'No', ]; }) ); From d34e89b2258500549d24827e18fb6de960ed404e Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 00:21:12 +0300 Subject: [PATCH 37/55] fix: enable ExecuteStatusCommand in ExecutorServiceProvider --- src/ExecutorServiceProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ExecutorServiceProvider.php b/src/ExecutorServiceProvider.php index 396fe1e..7c3bafd 100644 --- a/src/ExecutorServiceProvider.php +++ b/src/ExecutorServiceProvider.php @@ -51,7 +51,7 @@ public function boot() ExecuteMakeCommand::class, // ExecuteRollbackCommand::class, // ExecuteFreshCommand::class, - // ExecuteStatusCommand::class, + ExecuteStatusCommand::class, ]); } } From 74b33407259f1b94ac587fc4fb786555df6d8152 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 00:23:20 +0300 Subject: [PATCH 38/55] fix: prevent duplicate Executor names during collection in ExecutorPool --- src/Classes/ExecutorPool.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Classes/ExecutorPool.php b/src/Classes/ExecutorPool.php index 583dbff..0faaa9b 100644 --- a/src/Classes/ExecutorPool.php +++ b/src/Classes/ExecutorPool.php @@ -71,7 +71,6 @@ public function collect(Collection $records) { if (! empty($this->items)) { $this->items = []; - // return; } foreach ($this->paths as $path) { @@ -86,10 +85,12 @@ public function collect(Collection $records) $name = basename($file->getFileName(), '.php'); $record = $records->get($name); - array_push( - $this->items, - new ExecutorItem($obj, $file, $name, $record) - ); + + if (isset($this->items[$name])) { + throw new \Exception("Duplicate Executor name [$name] in file: " . $file->getRealPath()); + } + + $this->items[$name] = new ExecutorItem($obj, $file, $name, $record); } } } From 72fcf26076765f2ebfa5d63a4811aa28efdb705f Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 01:12:01 +0300 Subject: [PATCH 39/55] feat: add pharaonic/php-dot-array dependency and enhance Executor functionality --- composer.json | 3 +- ...24_07_07_000001_create_executors_table.php | 2 +- src/Classes/ExecutorItem.php | 50 ++++++++++++++++++- src/Classes/ExecutorManager.php | 34 +++++++++++-- src/Classes/ExecutorPool.php | 10 ++++ src/Console/ExecuteCommand.php | 50 ++++++++++++------- src/Models/Executor.php | 2 +- 7 files changed, 123 insertions(+), 28 deletions(-) diff --git a/composer.json b/composer.json index 02f19b2..1a26986 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,8 @@ ], "require": { "php": "~8.2|~8.3|~8.4", - "laravel/framework": "^11.0" + "laravel/framework": "^11.0", + "pharaonic/php-dot-array": "^2.0" }, "require-dev": { "orchestra/testbench": "^9.15" diff --git a/database/migrations/2024_07_07_000001_create_executors_table.php b/database/migrations/2024_07_07_000001_create_executors_table.php index b9e2cd4..ee4b7a9 100644 --- a/database/migrations/2024_07_07_000001_create_executors_table.php +++ b/database/migrations/2024_07_07_000001_create_executors_table.php @@ -21,7 +21,7 @@ public function up() $table->string('name'); $table->json('tags')->nullable(); $table->json('servers')->nullable(); - $table->integer('batch')->default(1); + $table->integer('batch')->nullable(); $table->integer('executed')->default(0); $table->timestamp('last_executed_at')->nullable(); }); diff --git a/src/Classes/ExecutorItem.php b/src/Classes/ExecutorItem.php index 010458f..646803c 100644 --- a/src/Classes/ExecutorItem.php +++ b/src/Classes/ExecutorItem.php @@ -2,6 +2,7 @@ namespace Pharaonic\Laravel\Executor\Classes; +use Pharaonic\Laravel\Executor\Facades\Executor as ExecutorFacade; use Pharaonic\Laravel\Executor\Models\Executor as Model; use Symfony\Component\Finder\SplFileInfo; @@ -64,14 +65,60 @@ public function info() ]; } + /** + * Determine if the executor is executable. + * + * @return bool + */ + public function isExecutable() + { + if (! $this->model) { + return true; + } + + if ($servers = $this->executor->servers ?: null) { + if (! array_intersect($servers, ExecutorFacade::getIPs())) { + return false; + } + } + + return $this->model?->executable ?? true; + } + + /** + * Set the associated executor model. + * + * @param Model $model + * @return static + */ + public function setModel(Model $model) + { + $this->model = $model; + + return $this; + } + /** * Run the executor. * * @return void */ - public function run() + public function run(int $nextBatch) { $this->executor->up(); + + if (! $this->model) { + Model::create([ + 'type' => $this->executor->type, + 'name' => $this->name, + 'tags' => $this->executor->tags, + 'batch' => $nextBatch, + 'executed' => 1, + 'last_executed_at' => now(), + ]); + } else { + $this->model->execute(); + } } /** @@ -82,5 +129,6 @@ public function run() public function reverse() { $this->executor->down(); + $this->model?->delete(); } } diff --git a/src/Classes/ExecutorManager.php b/src/Classes/ExecutorManager.php index 4c26624..dc409d0 100644 --- a/src/Classes/ExecutorManager.php +++ b/src/Classes/ExecutorManager.php @@ -13,6 +13,13 @@ class ExecutorManager */ public ExecutorPool $pool; + /** + * The server IPs. + * + * @var array + */ + protected array $ips = []; + public function __construct() { $this->pool = new ExecutorPool(); @@ -23,7 +30,7 @@ public function __construct() * * @return \Illuminate\Support\Collection */ - protected function getRecords() + public function getRecords() { return Executor::all()->keyBy('executor'); } @@ -41,12 +48,29 @@ public function info() } /** - * Run all the executors. + * Get the next batch number. + * + * @return int + */ + public function getNextBatchNumber() + { + return (Executor::orderBy('batch', 'desc')->first()?->batch ?? 0) + 1; + } + + /** + * Get the server IPs. * - * @return void + * @return array */ - public function run() + public function getIPs() { - // + if (! empty($this->ips)) { + return $this->ips; + } + + return $this->ips = array_filter( + dot(net_get_interfaces())->get('*.unicast.*.address'), + fn ($ip) => ! in_array($ip, ['127.0.0.1', '::1', null]) + ); } } diff --git a/src/Classes/ExecutorPool.php b/src/Classes/ExecutorPool.php index 0faaa9b..d9d06da 100644 --- a/src/Classes/ExecutorPool.php +++ b/src/Classes/ExecutorPool.php @@ -98,4 +98,14 @@ public function collect(Collection $records) return $this; } + + /** + * Get all collected executors items. + * + * @return array + */ + public function getItems() + { + return $this->items; + } } diff --git a/src/Console/ExecuteCommand.php b/src/Console/ExecuteCommand.php index 22201d6..439e6c5 100644 --- a/src/Console/ExecuteCommand.php +++ b/src/Console/ExecuteCommand.php @@ -3,7 +3,6 @@ namespace Pharaonic\Laravel\Executor\Console; use Illuminate\Console\Command; -use Pharaonic\Laravel\Executor\Services\ExecutorService; class ExecuteCommand extends Command { @@ -13,7 +12,7 @@ class ExecuteCommand extends Command * @var string */ protected $signature = 'execute {name?} - {--tag= : Execute the executor with the specified tag}'; + {--tags= : Execute the executor with the specified tags}'; /** * The console command description. @@ -25,31 +24,44 @@ class ExecuteCommand extends Command /** * Execute the console command. */ - public function handle(ExecutorService $service) + public function handle() { - if (! $service->isExists()) { - $this->warn('There are no executors need to be executed.'); - } + $manager = app('pharaonic.executor.manager'); + $items = $manager->pool->collect($manager->getRecords())->getItems(); - $list = $service->sync(); $name = $this->argument('name'); - $tag = $this->option('tag'); + $tags = $this->option('tags'); + $toRun = []; - $executors = $list - ->filter(fn ($executor) => $executor['model']->executable) - ->when($name, fn ($executors) => $executors->where('name', $name)) - ->when($tag, fn ($executors) => $executors->where('tag', $tag)); + if ($name) { + if (! isset($items[$name])) { + $this->warn("There is no executor with name [$name]."); + } - if ($executors->isEmpty()) { - $this->warn('There are no executors need to be executed.'); + if ($items[$name]->isExecutable($tags)) { + $toRun[] = $items[$name]; + } + } + + if ($tags) { + foreach ($items as $item) { + if ($item->isExecutable($tags) && ! in_array($item, $toRun)) { + $toRun[] = $item; + } + } } - $executors->each(function ($executor) { - $this->info("Executing {$executor['name']}..."); + if (empty($toRun)) { + $this->warn('There are no executors need to be executed.'); + } else { + $batch = $manager->getNextBatchNumber(); + + foreach ($toRun as $item) { + $this->info("Executing {$item->getName()}..."); - (include $executor['path'])->handle(); - $executor['model']->execute(); - }); + $item->run($batch); + } + } return 0; } diff --git a/src/Models/Executor.php b/src/Models/Executor.php index 9a7288d..3b42380 100644 --- a/src/Models/Executor.php +++ b/src/Models/Executor.php @@ -10,7 +10,7 @@ * @property ExecutorType $type * @property string $name * @property array|null $tags - * @property int $batch + * @property int|null $batch * @property int $executed * @property \Illuminate\Support\Carbon $last_executed_at * @method bool isNew() From 8199802addb0a672f3b9fed14ffb2515a29e533c Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 01:15:04 +0300 Subject: [PATCH 40/55] fix: uncomment ExecuteCommand and ExecuteFreshCommand in ExecutorServiceProvider --- src/ExecutorServiceProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ExecutorServiceProvider.php b/src/ExecutorServiceProvider.php index 7c3bafd..2096886 100644 --- a/src/ExecutorServiceProvider.php +++ b/src/ExecutorServiceProvider.php @@ -47,10 +47,10 @@ public function boot() ); $this->commands([ - // ExecuteCommand::class, + ExecuteCommand::class, ExecuteMakeCommand::class, // ExecuteRollbackCommand::class, - // ExecuteFreshCommand::class, + ExecuteFreshCommand::class, ExecuteStatusCommand::class, ]); } From e4a09cfbde33633e7e177039e97113518cd18db6 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 01:25:02 +0300 Subject: [PATCH 41/55] fix: add comments for clarity in ExecuteCommand handling logic --- src/Console/ExecuteCommand.php | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/Console/ExecuteCommand.php b/src/Console/ExecuteCommand.php index 439e6c5..1dc432e 100644 --- a/src/Console/ExecuteCommand.php +++ b/src/Console/ExecuteCommand.php @@ -33,31 +33,27 @@ public function handle() $tags = $this->option('tags'); $toRun = []; - if ($name) { - if (! isset($items[$name])) { - $this->warn("There is no executor with name [$name]."); + foreach ($items as $item) { + if ($name && $item->name != $name) { + continue; } - if ($items[$name]->isExecutable($tags)) { - $toRun[] = $items[$name]; + if ($tags && ! array_intersect(explode(',', $tags), $item->tags)) { + continue; } - } - if ($tags) { - foreach ($items as $item) { - if ($item->isExecutable($tags) && ! in_array($item, $toRun)) { - $toRun[] = $item; - } + if ($item->isExecutable()) { + $toRun[] = $item; } } - + if (empty($toRun)) { $this->warn('There are no executors need to be executed.'); } else { $batch = $manager->getNextBatchNumber(); foreach ($toRun as $item) { - $this->info("Executing {$item->getName()}..."); + $this->info("Executing {$item->name}..."); $item->run($batch); } From 0519cae74e14be9eb5cbd1400bd1fe314efc66c2 Mon Sep 17 00:00:00 2001 From: MoamenEltouny Date: Thu, 4 Sep 2025 22:25:17 +0000 Subject: [PATCH 42/55] Fixing php-cs --- src/Console/ExecuteCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/ExecuteCommand.php b/src/Console/ExecuteCommand.php index 1dc432e..e515554 100644 --- a/src/Console/ExecuteCommand.php +++ b/src/Console/ExecuteCommand.php @@ -46,7 +46,7 @@ public function handle() $toRun[] = $item; } } - + if (empty($toRun)) { $this->warn('There are no executors need to be executed.'); } else { From 496d65474d7b7901969a8234d98ab8ff632492a7 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 01:40:48 +0300 Subject: [PATCH 43/55] refactor: remove unused seed method and update model handling in ExecutorItem; fix keyBy in getRecords method of ExecutorManager --- src/Classes/Executor.php | 12 ------------ src/Classes/ExecutorItem.php | 25 ++++++++++--------------- src/Classes/ExecutorManager.php | 2 +- 3 files changed, 11 insertions(+), 28 deletions(-) diff --git a/src/Classes/Executor.php b/src/Classes/Executor.php index c9fb519..dae0d75 100644 --- a/src/Classes/Executor.php +++ b/src/Classes/Executor.php @@ -18,7 +18,6 @@ * @method void down() * @method PendingDispatch job(string|object $job, ...$arguments) * @method int command(string $command, array $parameters = []) - * @method int seed(string $class) */ abstract class Executor { @@ -108,15 +107,4 @@ final protected function command(string $command, array $parameters = []): int { return Artisan::call($command, $parameters, $this->output); } - - /** - * Seed the given class. - * - * @param string $class - * @return int - */ - final protected function seed(string $class): int - { - return $this->command('db:seed', ['--class' => $class]); - } }; diff --git a/src/Classes/ExecutorItem.php b/src/Classes/ExecutorItem.php index 646803c..3b1d6da 100644 --- a/src/Classes/ExecutorItem.php +++ b/src/Classes/ExecutorItem.php @@ -82,20 +82,13 @@ public function isExecutable() } } - return $this->model?->executable ?? true; - } - - /** - * Set the associated executor model. - * - * @param Model $model - * @return static - */ - public function setModel(Model $model) - { - $this->model = $model; + $this->model?->fill([ + 'type' => $this->executor->type, + 'tags' => $this->executor->tags, + 'servers' => $this->executor->servers, + ]); - return $this; + return $this->model?->isExecutable() ?? true; } /** @@ -108,7 +101,7 @@ public function run(int $nextBatch) $this->executor->up(); if (! $this->model) { - Model::create([ + $this->model = Model::create([ 'type' => $this->executor->type, 'name' => $this->name, 'tags' => $this->executor->tags, @@ -117,7 +110,9 @@ public function run(int $nextBatch) 'last_executed_at' => now(), ]); } else { - $this->model->execute(); + $this->model->executed += 1; + $this->model->last_executed_at = now(); + $this->model->save(); } } diff --git a/src/Classes/ExecutorManager.php b/src/Classes/ExecutorManager.php index dc409d0..ee6a010 100644 --- a/src/Classes/ExecutorManager.php +++ b/src/Classes/ExecutorManager.php @@ -32,7 +32,7 @@ public function __construct() */ public function getRecords() { - return Executor::all()->keyBy('executor'); + return Executor::all()->keyBy('name'); } /** From f5db08c2b4ad391981a64fd4a81947cd980dbd41 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 01:44:23 +0300 Subject: [PATCH 44/55] fix: improve rollback logic in ExecuteRollbackCommand and correct success message --- src/Console/ExecuteRollbackCommand.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Console/ExecuteRollbackCommand.php b/src/Console/ExecuteRollbackCommand.php index 2542624..6bc3257 100644 --- a/src/Console/ExecuteRollbackCommand.php +++ b/src/Console/ExecuteRollbackCommand.php @@ -37,9 +37,20 @@ public function handle() return 1; } + $manager = app('pharaonic.executor.manager'); + $items = $manager->pool->collect($manager->getRecords())->getItems(); + + foreach ($items as $item) { + if ($item->model && in_array($item->model->batch, $batches)) { + $this->info("Rolling back {$item->name}..."); + + $item->rollback(); + } + } + Executor::whereIn('batch', $batches)->delete(); - $this->info('Executors has been rollbacked successfully.'); + $this->info('Executors has been rollback successfully.'); return 0; } From eedfca0660f33fae25e4d36d08afb5c4ada4350d Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 01:45:44 +0300 Subject: [PATCH 45/55] fix: uncomment ExecuteRollbackCommand in ExecutorServiceProvider --- src/ExecutorServiceProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ExecutorServiceProvider.php b/src/ExecutorServiceProvider.php index 2096886..492ef78 100644 --- a/src/ExecutorServiceProvider.php +++ b/src/ExecutorServiceProvider.php @@ -49,7 +49,7 @@ public function boot() $this->commands([ ExecuteCommand::class, ExecuteMakeCommand::class, - // ExecuteRollbackCommand::class, + ExecuteRollbackCommand::class, ExecuteFreshCommand::class, ExecuteStatusCommand::class, ]); From dacef35ef05e941e4a6cdf4daf076576f00d8bba Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 01:48:18 +0300 Subject: [PATCH 46/55] fix: change model visibility to public in ExecutorItem --- src/Classes/ExecutorItem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Classes/ExecutorItem.php b/src/Classes/ExecutorItem.php index 3b1d6da..6c0f103 100644 --- a/src/Classes/ExecutorItem.php +++ b/src/Classes/ExecutorItem.php @@ -20,7 +20,7 @@ class ExecutorItem * * @var Model|null */ - protected ?Model $model = null; + public ?Model $model = null; /** * The name of the executor. From a9673bce6dac6fcbcda4e8a3cfa2a6e51595d993 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 01:50:55 +0300 Subject: [PATCH 47/55] fix: rename reverse method to rollback in ExecutorItem for clarity --- src/Classes/ExecutorItem.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Classes/ExecutorItem.php b/src/Classes/ExecutorItem.php index 6c0f103..6a4930d 100644 --- a/src/Classes/ExecutorItem.php +++ b/src/Classes/ExecutorItem.php @@ -117,13 +117,12 @@ public function run(int $nextBatch) } /** - * Reverse the executor. + * Rollback the executor. * * @return void */ - public function reverse() + public function rollback() { $this->executor->down(); - $this->model?->delete(); } } From 978986789a4a2ef0f07f26296e247b8dc62097a2 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 01:52:10 +0300 Subject: [PATCH 48/55] fix: change error message to warning and adjust return value in ExecuteRollbackCommand --- src/Console/ExecuteRollbackCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Console/ExecuteRollbackCommand.php b/src/Console/ExecuteRollbackCommand.php index 6bc3257..d3bcdd1 100644 --- a/src/Console/ExecuteRollbackCommand.php +++ b/src/Console/ExecuteRollbackCommand.php @@ -32,9 +32,9 @@ public function handle() $batches = Executor::orderBy('batch', 'desc')->groupBy('batch')->limit($this->option('steps'))->pluck('batch')->toArray(); if (empty($batches)) { - $this->error('There are no executors has been found.'); + $this->warn('There are no executors has been found.'); - return 1; + return 0; } $manager = app('pharaonic.executor.manager'); From 4d836c1a38e16c14561a00139155f3516c4b6612 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 12:25:50 +0300 Subject: [PATCH 49/55] refactor: remove ExecutorService and InteractsWithIO traits for code cleanup --- src/Services/ExecutorService.php | 123 ------------ src/Traits/InteractsWithIO.php | 331 ------------------------------- 2 files changed, 454 deletions(-) delete mode 100644 src/Services/ExecutorService.php delete mode 100644 src/Traits/InteractsWithIO.php diff --git a/src/Services/ExecutorService.php b/src/Services/ExecutorService.php deleted file mode 100644 index 8206876..0000000 --- a/src/Services/ExecutorService.php +++ /dev/null @@ -1,123 +0,0 @@ -dir = base_path('executors'); - } - - /** - * Check if the executors directory exists. - * - * @return bool - */ - public function isExists() - { - return is_dir($this->dir); - } - - /** - * Get the executors. - * - * @return \Illuminate\Support\Collection - */ - protected function prepareExecutors() - { - $db = Executor::all()->keyBy('executor'); - - return collect(array_map(function ($file) use ($db) { - $class = new ReflectionClass(include $file); - $name = basename($class->getFileName(), '.php'); - $executor = [ - 'name' => $name, - 'type' => $class->getProperty('type')->getDefaultValue(), - 'tag' => $class->getProperty('tag')->getDefaultValue(), - 'path' => $class->getFileName(), - 'model' => $db[$name] ?? null, - ]; - - if ($executor['model']) { - $executor['model']->fill([ - 'type' => $executor['type'], - 'tag' => $executor['tag'], - ]); - - if ($executor['model']->isDirty()) { - $executor['model']->save(); - } - } - - return $executor; - }, $this->getPaths()))->keyBy('name'); - } - - - /** - * Sync the executors. - * - * @return \Illuminate\Support\Collection - */ - public function sync() - { - $executors = $this->prepareExecutors(); - - $newExecutors = $executors->filter(fn ($executor) => $executor['model'] == null); - if ($newExecutors->isNotEmpty()) { - $batch = $this->getNextBatch(); - - $newExecutors->each(function ($executor) use (&$executors, $batch) { - $executor['model'] = new Executor([ - 'executor' => $executor['name'], - 'type' => $executor['type'], - 'tag' => $executor['tag'], - 'batch' => $batch, - ]); - - $executors->offsetSet($executor['name'], $executor); - }); - } - - return $executors; - } - - /** - * Get the next batch number. - * - * @return int - */ - protected function getNextBatch() - { - return (Executor::orderBy('batch', 'desc')->first()?->batch ?? 0) + 1; - } - - /** - * Get the paths of the executors. - * - * @return array - */ - protected function getPaths() - { - $collectPath = collect([]); - - foreach (ExecutorPool::getPaths() as $path) { - $collectPath = $collectPath->merge(File::glob($path . '/*')); - } - - return $collectPath->all(); - } -} diff --git a/src/Traits/InteractsWithIO.php b/src/Traits/InteractsWithIO.php deleted file mode 100644 index 2755c14..0000000 --- a/src/Traits/InteractsWithIO.php +++ /dev/null @@ -1,331 +0,0 @@ - OutputInterface::VERBOSITY_VERBOSE, - 'vv' => OutputInterface::VERBOSITY_VERY_VERBOSE, - 'vvv' => OutputInterface::VERBOSITY_DEBUG, - 'quiet' => OutputInterface::VERBOSITY_QUIET, - 'normal' => OutputInterface::VERBOSITY_NORMAL, - ]; - - /** - * Confirm a question with the user. - * - * @param string $question - * @param bool $default - * @return bool - */ - public function confirm($question, $default = false) - { - return $this->output->confirm($question, $default); - } - - /** - * Prompt the user for input. - * - * @param string $question - * @param string|null $default - * @return mixed - */ - public function ask($question, $default = null) - { - return $this->output->ask($question, $default); - } - - /** - * Prompt the user for input with auto completion. - * - * @param string $question - * @param array|callable $choices - * @param string|null $default - * @return mixed - */ - public function anticipate($question, $choices, $default = null) - { - return $this->askWithCompletion($question, $choices, $default); - } - - /** - * Prompt the user for input with auto completion. - * - * @param string $question - * @param array|callable $choices - * @param string|null $default - * @return mixed - */ - public function askWithCompletion($question, $choices, $default = null) - { - $question = new Question($question, $default); - - is_callable($choices) - ? $question->setAutocompleterCallback($choices) - : $question->setAutocompleterValues($choices); - - return $this->output->askQuestion($question); - } - - /** - * Prompt the user for input but hide the answer from the console. - * - * @param string $question - * @param bool $fallback - * @return mixed - */ - public function secret($question, $fallback = true) - { - $question = new Question($question); - - $question->setHidden(true)->setHiddenFallback($fallback); - - return $this->output->askQuestion($question); - } - - /** - * Give the user a single choice from an array of answers. - * - * @param string $question - * @param array $choices - * @param string|int|null $default - * @param mixed|null $attempts - * @param bool $multiple - * @return string|array - */ - public function choice($question, array $choices, $default = null, $attempts = null, $multiple = false) - { - $question = new ChoiceQuestion($question, $choices, $default); - - $question->setMaxAttempts($attempts)->setMultiselect($multiple); - - return $this->output->askQuestion($question); - } - - /** - * Format input to textual table. - * - * @param array $headers - * @param \Illuminate\Contracts\Support\Arrayable|array $rows - * @param \Symfony\Component\Console\Helper\TableStyle|string $tableStyle - * @param array $columnStyles - * @return void - */ - public function table($headers, $rows, $tableStyle = 'default', array $columnStyles = []) - { - $table = new Table($this->output); - - if ($rows instanceof Arrayable) { - $rows = $rows->toArray(); - } - - $table->setHeaders((array) $headers)->setRows($rows)->setStyle($tableStyle); - - foreach ($columnStyles as $columnIndex => $columnStyle) { - $table->setColumnStyle($columnIndex, $columnStyle); - } - - $table->render(); - } - - /** - * Execute a given callback while advancing a progress bar. - * - * @param iterable|int $totalSteps - * @param \Closure $callback - * @return mixed|void - */ - public function withProgressBar($totalSteps, Closure $callback) - { - $bar = $this->output->createProgressBar( - is_iterable($totalSteps) ? count($totalSteps) : $totalSteps - ); - - $bar->start(); - - if (is_iterable($totalSteps)) { - foreach ($totalSteps as $value) { - $callback($value, $bar); - - $bar->advance(); - } - } else { - $callback($bar); - } - - $bar->finish(); - - if (is_iterable($totalSteps)) { - return $totalSteps; - } - } - - /** - * Write a string as information output. - * - * @param string $string - * @param int|string|null $verbosity - * @return void - */ - public function info($string, $verbosity = null) - { - $this->line($string, 'info', $verbosity); - } - - /** - * Write a string as standard output. - * - * @param string $string - * @param string|null $style - * @param int|string|null $verbosity - * @return void - */ - public function line($string, $style = null, $verbosity = null) - { - $styled = $style ? "<$style>$string" : $string; - - $this->output->writeln($styled, $this->parseVerbosity($verbosity)); - } - - /** - * Write a string as comment output. - * - * @param string $string - * @param int|string|null $verbosity - * @return void - */ - public function comment($string, $verbosity = null) - { - $this->line($string, 'comment', $verbosity); - } - - /** - * Write a string as question output. - * - * @param string $string - * @param int|string|null $verbosity - * @return void - */ - public function question($string, $verbosity = null) - { - $this->line($string, 'question', $verbosity); - } - - /** - * Write a string as error output. - * - * @param string $string - * @param int|string|null $verbosity - * @return void - */ - public function error($string, $verbosity = null) - { - $this->line($string, 'error', $verbosity); - } - - /** - * Write a string as warning output. - * - * @param string $string - * @param int|string|null $verbosity - * @return void - */ - public function warn($string, $verbosity = null) - { - if (! $this->output->getFormatter()->hasStyle('warning')) { - $style = new OutputFormatterStyle('yellow'); - - $this->output->getFormatter()->setStyle('warning', $style); - } - - $this->line($string, 'warning', $verbosity); - } - - /** - * Write a string in an alert box. - * - * @param string $string - * @param int|string|null $verbosity - * @return void - */ - public function alert($string, $verbosity = null) - { - $length = Str::length(strip_tags($string)) + 12; - - $this->comment(str_repeat('*', $length), $verbosity); - $this->comment('* '.$string.' *', $verbosity); - $this->comment(str_repeat('*', $length), $verbosity); - - $this->comment('', $verbosity); - } - - /** - * Write a blank line. - * - * @param int $count - * @return $this - */ - public function newLine($count = 1) - { - $this->output->newLine($count); - - return $this; - } - - /** - * Set the verbosity level. - * - * @param string|int $level - * @return void - */ - protected function setVerbosity($level) - { - $this->verbosity = $this->parseVerbosity($level); - } - - /** - * Get the verbosity level in terms of Symfony's OutputInterface level. - * - * @param string|int|null $level - * @return int - */ - protected function parseVerbosity($level = null) - { - if (isset($this->verbosityMap[$level])) { - $level = $this->verbosityMap[$level]; - } elseif (! is_int($level)) { - $level = $this->verbosity; - } - - return $level; - } -} From 871821fc7c33daf6c05d72d8a3024d9087a3efae Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 12:36:10 +0300 Subject: [PATCH 50/55] fix: use strict comparison in isNew method for accuracy --- src/Models/Executor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Models/Executor.php b/src/Models/Executor.php index 3b42380..b2a93bf 100644 --- a/src/Models/Executor.php +++ b/src/Models/Executor.php @@ -80,7 +80,7 @@ public function getTable() */ public function isNew() { - return $this->executed == 0; + return $this->executed === 0; } /** From e40e09ecd5a5e7d76584f3816030a78360e96aed Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 12:37:21 +0300 Subject: [PATCH 51/55] fix: correct class closing syntax in Executor class --- src/Classes/Executor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Classes/Executor.php b/src/Classes/Executor.php index dae0d75..bfa00f4 100644 --- a/src/Classes/Executor.php +++ b/src/Classes/Executor.php @@ -107,4 +107,4 @@ final protected function command(string $command, array $parameters = []): int { return Artisan::call($command, $parameters, $this->output); } -}; +} From 246e569b3cc45b0c0931dddcff6bf994af07fd77 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 12:43:30 +0300 Subject: [PATCH 52/55] fix: update method documentation in Executor facade for clarity --- src/Facades/Executor.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Facades/Executor.php b/src/Facades/Executor.php index 604234c..0a27749 100644 --- a/src/Facades/Executor.php +++ b/src/Facades/Executor.php @@ -5,8 +5,11 @@ use Illuminate\Support\Facades\Facade; /** + * @method static \Pharaonic\Laravel\Executor\Classes\ExecutorPool getPool() + * @method static \Illuminate\Support\Collection getRecords() + * @method static int getNextBatchNumber() + * @method static array getIPs() * @method static array info() - * @method static void run() */ class Executor extends Facade { From c0b0be4831436d464293dae2c29e376646b64fb3 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 12:43:42 +0300 Subject: [PATCH 53/55] fix: replace manager references with ExecutorFacade for consistency --- src/Classes/ExecutorManager.php | 12 +++++++++++- src/Console/ExecuteCommand.php | 8 +++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Classes/ExecutorManager.php b/src/Classes/ExecutorManager.php index ee6a010..5a6627f 100644 --- a/src/Classes/ExecutorManager.php +++ b/src/Classes/ExecutorManager.php @@ -11,7 +11,7 @@ class ExecutorManager * * @var ExecutorPool */ - public ExecutorPool $pool; + protected ExecutorPool $pool; /** * The server IPs. @@ -25,6 +25,16 @@ public function __construct() $this->pool = new ExecutorPool(); } + /** + * Get the executor pool instance. + * + * @return ExecutorPool + */ + public function getPool() + { + return $this->pool; + } + /** * Get all executor records. * diff --git a/src/Console/ExecuteCommand.php b/src/Console/ExecuteCommand.php index e515554..abdf8f1 100644 --- a/src/Console/ExecuteCommand.php +++ b/src/Console/ExecuteCommand.php @@ -3,6 +3,7 @@ namespace Pharaonic\Laravel\Executor\Console; use Illuminate\Console\Command; +use Pharaonic\Laravel\Executor\Facades\Executor as ExecutorFacade; class ExecuteCommand extends Command { @@ -26,8 +27,9 @@ class ExecuteCommand extends Command */ public function handle() { - $manager = app('pharaonic.executor.manager'); - $items = $manager->pool->collect($manager->getRecords())->getItems(); + $items = ExecutorFacade::getPool() + ->collect(ExecutorFacade::getRecords()) + ->getItems(); $name = $this->argument('name'); $tags = $this->option('tags'); @@ -50,7 +52,7 @@ public function handle() if (empty($toRun)) { $this->warn('There are no executors need to be executed.'); } else { - $batch = $manager->getNextBatchNumber(); + $batch = ExecutorFacade::getNextBatchNumber(); foreach ($toRun as $item) { $this->info("Executing {$item->name}..."); From 87fd7cd1f09efac750b9ba7967d92bbee6a5bf07 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 12:43:50 +0300 Subject: [PATCH 54/55] fix: correct description text and replace manager references with ExecutorFacade --- src/Console/ExecuteRollbackCommand.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Console/ExecuteRollbackCommand.php b/src/Console/ExecuteRollbackCommand.php index d3bcdd1..bac453d 100644 --- a/src/Console/ExecuteRollbackCommand.php +++ b/src/Console/ExecuteRollbackCommand.php @@ -3,6 +3,7 @@ namespace Pharaonic\Laravel\Executor\Console; use Illuminate\Console\Command; +use Pharaonic\Laravel\Executor\Facades\Executor as ExecutorFacade; use Pharaonic\Laravel\Executor\Models\Executor; class ExecuteRollbackCommand extends Command @@ -20,7 +21,7 @@ class ExecuteRollbackCommand extends Command * * @var string */ - protected $description = 'Rollback the lastest executors that has been inserted.'; + protected $description = 'Rollback the latest executors that have been inserted.'; /** * Execute the console command. @@ -37,8 +38,9 @@ public function handle() return 0; } - $manager = app('pharaonic.executor.manager'); - $items = $manager->pool->collect($manager->getRecords())->getItems(); + $items = ExecutorFacade::getPool() + ->collect(ExecutorFacade::getRecords()) + ->getItems(); foreach ($items as $item) { if ($item->model && in_array($item->model->batch, $batches)) { From 23bdbe30a6f18b6d4b073c99636177093649d287 Mon Sep 17 00:00:00 2001 From: Moamen Eltouny Date: Fri, 5 Sep 2025 13:03:48 +0300 Subject: [PATCH 55/55] fix: refactor ExecutorTest and TestCase setup methods for clarity and consistency --- tests/ExecutorTest.php | 121 ++++++++++++++++------------------------- tests/TestCase.php | 10 ++-- 2 files changed, 53 insertions(+), 78 deletions(-) diff --git a/tests/ExecutorTest.php b/tests/ExecutorTest.php index b848e69..41177ee 100644 --- a/tests/ExecutorTest.php +++ b/tests/ExecutorTest.php @@ -2,91 +2,66 @@ namespace Pharaonic\Laravel\Executor\Tests; +use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Support\Facades\File; use Pharaonic\Laravel\Executor\Models\Executor; -use Pharaonic\Laravel\Executor\Services\ExecutorService; class ExecutorTest extends TestCase { + use RefreshDatabase; + public function testMakeExecutor() { $this->artisan('execute:make', ['name' => 'testMakeExecutor']) ->assertExitCode(0); } - // public function testMakeOnceExecutor() - // { - // $this->artisan('execute:make', [ - // 'name' => 'testMakeOnceExecutor', - // '--once' => true, - // ]) - // ->assertExitCode(0); - // } - - // public function testMakeExecutorWithTag() - // { - // $this->artisan('execute:make', [ - // 'name' => 'testMakeExecutorWithTag', - // '--tags' => 'test', - // ]) - // ->assertExitCode(0); - // } - - // public function testMakeOnceExecutorWithTag() - // { - // $this->artisan('execute:make', [ - // 'name' => 'testMakeOnceExecutorWithTag', - // '--once' => true, - // '--tags' => 'test,example', - // ]) - // ->assertExitCode(0); - // } - - // public function testExecute() - // { - // $this->testMakeExecutor(); - // $this->artisan('execute')->assertOk(); - // $this->assertEquals(1, Executor::count()); - // } - - // public function testRollbackSuccess() - // { - // $this->testMakeExecutor(); - // $this->artisan('execute')->assertOk(); - // $this->artisan('execute:rollback')->assertOk(); - // $this->assertEquals(0, Executor::count()); - // } - - // public function testRollbackFailed() - // { - // $this->artisan('execute:rollback')->assertFailed(); - // } + public function testExecute() + { + $this->artisan('execute:make', ['name' => 'testMakeExecutor']); + $this->artisan('execute')->assertOk(); + $this->assertEquals(1, Executor::count()); + } - // public function testFreshExecutors() - // { - // $this->testMakeExecutor(); - // $this->artisan('execute:fresh')->assertOk(); - // $this->assertEquals(1, Executor::count()); - // } + public function testRollbackSuccess() + { + $this->artisan('execute:make', ['name' => 'testMakeExecutor']); + $this->artisan('execute')->assertOk(); + $this->artisan('execute:rollback')->assertOk(); + $this->assertEquals(0, Executor::count()); + } - // public function testStatusOfExecutors() - // { - // $this->testMakeExecutor(); + public function testRollbackFailed() + { + $this->artisan('execute:rollback') + ->expectsOutput('There are no executors has been found.') + ->assertExitCode(0); + } - // $executors = (new ExecutorService())->sync(); + public function testFreshExecutors() + { + $this->artisan('execute:make', ['name' => 'testMakeExecutor']); + $this->artisan('execute:fresh')->assertOk(); + $this->assertEquals(1, Executor::count()); + } - // $this->artisan('execute:status') - // ->assertOk() - // ->expectsTable( - // ['Name', 'Type', 'Tag', 'Batch', 'Executed'], - // $executors->map(function ($executor) { - // return [ - // $executor['name'], - // ucfirst($executor['type']->name), - // $executor['tag'], - // $executor['model']->batch, - // $executor['model']->executed > 0 ? 'Yes' : 'No', - // ]; - // }) - // ); - // } + public function testStatusOfExecutors() + { + $this->artisan('execute:make', ['name' => 'testMakeExecutor']); + $this->artisan('execute:status') + ->assertOk() + ->expectsTable( + ['Name', 'Type', 'Tags', 'Servers', 'Batch', 'Executed'], + [ + [ + basename(File::files(base_path('executors'))[0]->getBasename(), '.php'), + 'Always', + 'None', + 'All', + 'N/A', + 'No', + ], + ] + ); + } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 5e07b17..820e273 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -8,12 +8,12 @@ class TestCase extends OrchestraTestCase { - // public function setUp(): void - // { - // parent::setUp(); + public function setUp(): void + { + parent::setUp(); - // File::deleteDirectory(base_path('executors')); - // } + File::deleteDirectory(base_path('executors')); + } /** * add the package provider