diff --git a/.cache/.gitkeep b/.cache/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/.gitattributes b/.gitattributes index 6b37862..dfb1fdc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13,3 +13,7 @@ /docker-compose.yml export-ignore /infection.json.dist export-ignore /psalm.xml export-ignore + +*.php text=auto eol=lf +*.sh text=auto eol=lf +/run text=auto eol=lf diff --git a/.idea/php.xml b/.idea/php.xml index 4cfc551..1c3c423 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -13,6 +13,10 @@ + + + + diff --git a/.idea/phpunit.xml b/.idea/phpunit.xml index 4f8104c..84ebdd2 100644 --- a/.idea/phpunit.xml +++ b/.idea/phpunit.xml @@ -4,6 +4,7 @@ diff --git a/composer.json b/composer.json index 83d668c..614863c 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "codenamephp/deploymentchecks.async", - "description": "", + "description": "Package that let's you run the deployment checks in parallel.", "type": "library", "license": "Apache-2.0", "authors": [ @@ -10,7 +10,9 @@ } ], "require": { - "php": "^8.2" + "php": "^8.2", + "codenamephp/deploymentchecks.base": "*", + "spatie/async": "^1.5" }, "autoload": { "psr-4": { diff --git a/docker/application/Dockerfile b/docker/application/Dockerfile index 9fc630c..db7acbc 100644 --- a/docker/application/Dockerfile +++ b/docker/application/Dockerfile @@ -1,4 +1,4 @@ -FROM webdevops/php-dev:8.1 +FROM webdevops/php-dev:8.2 COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer COPY --from=phario/phive:0.15.2 /usr/local/bin/phive /usr/local/bin/phive diff --git a/run b/run new file mode 100755 index 0000000..54bd5d9 --- /dev/null +++ b/run @@ -0,0 +1,19 @@ +#!/bin/bash + +# +# Copyright 2023 Bastian Schwarz . +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +docker compose exec -it -u application application bash -c "$*" diff --git a/src/Check/Factory/FromCheck/FromCheckFactoryInterface.php b/src/Check/Factory/FromCheck/FromCheckFactoryInterface.php new file mode 100644 index 0000000..2a88361 --- /dev/null +++ b/src/Check/Factory/FromCheck/FromCheckFactoryInterface.php @@ -0,0 +1,30 @@ +. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace de\codenamephp\deploymentchecks\async\Check\Factory\FromCheck; + +use de\codenamephp\deploymentchecks\async\Check\ParallelCheckInterface; +use de\codenamephp\deploymentchecks\base\Check\CheckInterface; + +/** + * Interface to create parallel checks from a regular check on the fly + */ +interface FromCheckFactoryInterface +{ + + public function build(CheckInterface $check): ParallelCheckInterface; +} diff --git a/src/Check/Factory/FromCheck/WithErrorHandlerFactory.php b/src/Check/Factory/FromCheck/WithErrorHandlerFactory.php new file mode 100644 index 0000000..326d8f0 --- /dev/null +++ b/src/Check/Factory/FromCheck/WithErrorHandlerFactory.php @@ -0,0 +1,46 @@ +. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace de\codenamephp\deploymentchecks\async\Check\Factory\FromCheck; + +use de\codenamephp\deploymentchecks\async\Check\ParallelCheckInterface; +use de\codenamephp\deploymentchecks\async\Check\ParallelCheckWithErrorHandler; +use de\codenamephp\deploymentchecks\async\ErrorHandler\ErrorHandlerInterface; +use de\codenamephp\deploymentchecks\async\ErrorHandler\RethrowException; +use de\codenamephp\deploymentchecks\async\SuccessHandler\AddToResultCollection; +use de\codenamephp\deploymentchecks\async\SuccessHandler\SuccessHandlerInterface; +use de\codenamephp\deploymentchecks\base\Check\CheckInterface; + +/** + * Factory to create ParallelCheckWithErrorHandler instances + * + * @psalm-api + */ +final class WithErrorHandlerFactory implements FromCheckFactoryInterface +{ + public function __construct( + public SuccessHandlerInterface $successHandler = new AddToResultCollection(), + public ErrorHandlerInterface $errorHandler = new RethrowException(), + ) {} + + public function build(CheckInterface $check): ParallelCheckInterface + { + return new ParallelCheckWithErrorHandler($check, $this->successHandler, $this->errorHandler); + } + + +} diff --git a/src/Check/ParallelCheckInterface.php b/src/Check/ParallelCheckInterface.php new file mode 100644 index 0000000..b17a792 --- /dev/null +++ b/src/Check/ParallelCheckInterface.php @@ -0,0 +1,42 @@ +. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace de\codenamephp\deploymentchecks\async\Check; + +use de\codenamephp\deploymentchecks\async\SuccessHandler\SuccessHandlerInterface; +use de\codenamephp\deploymentchecks\base\Check\Result\ResultInterface; + +/** + * Interface for checks that can be used in parallel using the async pool + */ +interface ParallelCheckInterface +{ + + /** + * Since the async pool only accepts callables, this method is used to invoke the check and return the result + * + * @return ResultInterface + */ + public function __invoke(): ResultInterface; + + /** + * Gets the success handler that is used to handle the result if the check was successful + * + * @return SuccessHandlerInterface + */ + public function successHandler(): SuccessHandlerInterface; +} diff --git a/src/Check/ParallelCheckWithErrorHandler.php b/src/Check/ParallelCheckWithErrorHandler.php new file mode 100644 index 0000000..3b05878 --- /dev/null +++ b/src/Check/ParallelCheckWithErrorHandler.php @@ -0,0 +1,59 @@ +. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace de\codenamephp\deploymentchecks\async\Check; + +use de\codenamephp\deploymentchecks\async\ErrorHandler\ErrorHandlerInterface; +use de\codenamephp\deploymentchecks\async\ErrorHandler\RethrowException; +use de\codenamephp\deploymentchecks\async\SuccessHandler\AddToResultCollection; +use de\codenamephp\deploymentchecks\async\SuccessHandler\SuccessHandlerInterface; +use de\codenamephp\deploymentchecks\base\Check\CheckInterface; +use de\codenamephp\deploymentchecks\base\Check\Result\ResultInterface; + +/** + * Basically a wrapper around a check that implements the ParallelCheckInterface and WithErrorHandlerInterface. The spatie async pool expects a callable so this + * class implements that interface and just calls the check. The check itself is passed in the constructor and the success and error handler are optional. + * + * The default AsyncCheckCollection will use this check to run the checks in parallel. It will use the success and error handler as quasi callbacks to handle the result + * of the check. + * + * The default success handler will add the result to the result collection and the default error handler will rethrow the exception. + */ +final readonly class ParallelCheckWithErrorHandler implements ParallelCheckInterface, WithErrorHandlerInterface +{ + + public function __construct( + public CheckInterface $check, + public SuccessHandlerInterface $successHandler = new AddToResultCollection(), + public ErrorHandlerInterface $errorHandler = new RethrowException(), + ) {} + + public function __invoke(): ResultInterface + { + return $this->check->run(); + } + + public function successHandler(): SuccessHandlerInterface + { + return $this->successHandler; + } + + public function errorHandler(): ErrorHandlerInterface + { + return $this->errorHandler; + } +} diff --git a/src/Check/WithErrorHandlerInterface.php b/src/Check/WithErrorHandlerInterface.php new file mode 100644 index 0000000..653068d --- /dev/null +++ b/src/Check/WithErrorHandlerInterface.php @@ -0,0 +1,29 @@ +. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace de\codenamephp\deploymentchecks\async\Check; + +use de\codenamephp\deploymentchecks\async\ErrorHandler\ErrorHandlerInterface; + +/** + * Interface for parallel checks that want to define a custom error handler + */ +interface WithErrorHandlerInterface +{ + + public function errorHandler(): ErrorHandlerInterface; +} diff --git a/src/Collection/AsyncCheckCollection.php b/src/Collection/AsyncCheckCollection.php new file mode 100644 index 0000000..9ad9b93 --- /dev/null +++ b/src/Collection/AsyncCheckCollection.php @@ -0,0 +1,61 @@ +. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace de\codenamephp\deploymentchecks\async\Collection; + +use de\codenamephp\deploymentchecks\async\Check\Factory\FromCheck\FromCheckFactoryInterface; +use de\codenamephp\deploymentchecks\async\Check\WithErrorHandlerInterface; +use de\codenamephp\deploymentchecks\base\Check\CheckInterface; +use de\codenamephp\deploymentchecks\base\Check\Result\Collection\ResultCollectionInterface; +use de\codenamephp\deploymentchecks\base\Check\Result\ResultInterface; +use Spatie\Async\Pool; +use Throwable; + +/** + * @psalm-api + */ +final readonly class AsyncCheckCollection implements CheckInterface +{ + + /** + * @var array + */ + public array $checks; + + public function __construct( + public Pool $pool, + public ResultCollectionInterface $resultCollection, + public FromCheckFactoryInterface $fromCheckFactory, + CheckInterface ...$checks) + { + $this->checks = $checks; + } + + public function run(): ResultInterface + { + foreach ($this->checks as $check) { + $parallelCheck = $this->fromCheckFactory->build($check); + $runnable = $this->pool + ->add($parallelCheck) + ->then(fn(ResultInterface $output) => $parallelCheck->successHandler()->handle($this->resultCollection, $output)); + if ($parallelCheck instanceof WithErrorHandlerInterface) $runnable->catch(fn(Throwable $exception): mixed => $parallelCheck->errorHandler()->handle($this->resultCollection, $exception)); + } + $this->pool->wait(); + return $this->resultCollection; + } +} diff --git a/src/ErrorHandler/ErrorHandlerInterface.php b/src/ErrorHandler/ErrorHandlerInterface.php new file mode 100644 index 0000000..1fb6af0 --- /dev/null +++ b/src/ErrorHandler/ErrorHandlerInterface.php @@ -0,0 +1,30 @@ +. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace de\codenamephp\deploymentchecks\async\ErrorHandler; + +use de\codenamephp\deploymentchecks\base\Check\Result\Collection\ResultCollectionInterface; +use Throwable; + +/** + * Interface for a simple error handler. It expects the result collection build in the async collection and the exception that was thrown. + */ +interface ErrorHandlerInterface +{ + + public function handle(ResultCollectionInterface $resultCollection, Throwable $exception): mixed; +} diff --git a/src/ErrorHandler/RethrowException.php b/src/ErrorHandler/RethrowException.php new file mode 100644 index 0000000..8ea2ed7 --- /dev/null +++ b/src/ErrorHandler/RethrowException.php @@ -0,0 +1,34 @@ +. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace de\codenamephp\deploymentchecks\async\ErrorHandler; + +use de\codenamephp\deploymentchecks\base\Check\Result\Collection\ResultCollectionInterface; +use Throwable; + +/** + * Just rethrows the exception + */ +final class RethrowException implements ErrorHandlerInterface +{ + + public function handle(ResultCollectionInterface $resultCollection, Throwable $exception): mixed + { + throw $exception; + } + +} diff --git a/src/SuccessHandler/AddToResultCollection.php b/src/SuccessHandler/AddToResultCollection.php new file mode 100644 index 0000000..abcb2b6 --- /dev/null +++ b/src/SuccessHandler/AddToResultCollection.php @@ -0,0 +1,34 @@ +. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace de\codenamephp\deploymentchecks\async\SuccessHandler; + +use de\codenamephp\deploymentchecks\base\Check\Result\Collection\ResultCollectionInterface; +use de\codenamephp\deploymentchecks\base\Check\Result\ResultInterface; + +/** + * Just adds the result to the collection + */ +final class AddToResultCollection implements SuccessHandlerInterface +{ + + public function handle(ResultCollectionInterface $resultCollection, ResultInterface $result): ResultInterface + { + $resultCollection->add($result); + return $result; + } +} diff --git a/src/SuccessHandler/SuccessHandlerInterface.php b/src/SuccessHandler/SuccessHandlerInterface.php new file mode 100644 index 0000000..c8a5b1d --- /dev/null +++ b/src/SuccessHandler/SuccessHandlerInterface.php @@ -0,0 +1,30 @@ +. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace de\codenamephp\deploymentchecks\async\SuccessHandler; + +use de\codenamephp\deploymentchecks\base\Check\Result\Collection\ResultCollectionInterface; +use de\codenamephp\deploymentchecks\base\Check\Result\ResultInterface; + +/** + * Interface for parallel checks that want to define a custom success handler + */ +interface SuccessHandlerInterface +{ + + public function handle(ResultCollectionInterface $resultCollection, ResultInterface $result): ResultInterface; +}