From 5b7fb77e2be7027b6a874c927851773127b1ceb7 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Mon, 12 Sep 2022 15:39:40 +0200 Subject: [PATCH 01/22] Request ephemeral Hypernode when necessary --- composer.json | 4 +- composer.lock | 1765 ++++++++++++++++++++++++---- src/DeployRunner.php | 62 + src/Exception/TimeoutException.php | 11 + 4 files changed, 1627 insertions(+), 215 deletions(-) create mode 100644 src/Exception/TimeoutException.php diff --git a/composer.json b/composer.json index 48eaeb8..284c6b5 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,9 @@ "composer-runtime-api": "^2", "deployer/deployer": "dev-master#e374a8e as v7.0.0", "doctrine/annotations": "^1.6", - "hypernode/deploy-configuration": "^2.0", + "guzzlehttp/guzzle": "^7.5", + "hypernode/api-client": "dev-master", + "hypernode/deploy-configuration": "dev-ephemeral_servers", "php-di/php-di": "^6.0", "psr/log": "^1.0", "symfony/console": "^5.4", diff --git a/composer.lock b/composer.lock index 54b5f80..343fdb1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,74 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2b182d7312c6d76fb71d34b9cc24f323", + "content-hash": "68a8c995edb3ae6b3d40dfa09a4d1639", "packages": [ + { + "name": "clue/stream-filter", + "version": "v1.6.0", + "source": { + "type": "git", + "url": "https://github.com/clue/stream-filter.git", + "reference": "d6169430c7731d8509da7aecd0af756a5747b78e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/stream-filter/zipball/d6169430c7731d8509da7aecd0af756a5747b78e", + "reference": "d6169430c7731d8509da7aecd0af756a5747b78e", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "Clue\\StreamFilter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "description": "A simple and modern approach to stream filtering in PHP", + "homepage": "https://github.com/clue/php-stream-filter", + "keywords": [ + "bucket brigade", + "callback", + "filter", + "php_user_filter", + "stream", + "stream_filter_append", + "stream_filter_register" + ], + "support": { + "issues": "https://github.com/clue/stream-filter/issues", + "source": "https://github.com/clue/stream-filter/tree/v1.6.0" + }, + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2022-02-21T13:15:14+00:00" + }, { "name": "deployer/deployer", "version": "dev-master", @@ -76,7 +142,7 @@ "type": "github" } ], - "time": "2022-08-28T16:50:55+00:00" + "time": "2022-09-12T10:53:42+00:00" }, { "name": "doctrine/annotations", @@ -331,81 +397,165 @@ "time": "2020-11-24T22:02:12+00:00" }, { - "name": "hypernode/deploy-configuration", - "version": "2.0.1", + "name": "guzzlehttp/guzzle", + "version": "7.5.0", "source": { "type": "git", - "url": "https://github.com/ByteInternet/hypernode-deploy-configuration.git", - "reference": "dee95040dabe7e84cc888fb0960a7cdee4d562b9" + "url": "https://github.com/guzzle/guzzle.git", + "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ByteInternet/hypernode-deploy-configuration/zipball/dee95040dabe7e84cc888fb0960a7cdee4d562b9", - "reference": "dee95040dabe7e84cc888fb0960a7cdee4d562b9", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba", + "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba", "shasum": "" }, "require": { - "deployer/deployer": "^7.0", - "psr/log": "^1.0" + "ext-json": "*", + "guzzlehttp/promises": "^1.5", + "guzzlehttp/psr7": "^1.9 || ^2.4", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" }, - "conflict": { - "hipex/deploy-configuration": "*" + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "ext-curl": "*", + "php-http/client-integration-tests": "^3.0", + "phpunit/phpunit": "^8.5.29 || ^9.5.23", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" }, "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "7.5-dev" + } + }, "autoload": { "files": [ - "src/autoload.php", - "src/functions.php" + "src/functions_include.php" ], "psr-4": { - "Hypernode\\DeployConfiguration\\": "src/" + "GuzzleHttp\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "OSL-3.0" + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" ], - "description": "Hypernode deploy configuration files", "support": { - "issues": "https://github.com/ByteInternet/hypernode-deploy-configuration/issues", - "source": "https://github.com/ByteInternet/hypernode-deploy-configuration/tree/2.0.1" + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.5.0" }, - "time": "2022-08-29T07:51:23+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2022-08-28T15:39:27+00:00" }, { - "name": "justinrainbow/json-schema", - "version": "5.2.12", + "name": "guzzlehttp/promises", + "version": "1.5.2", "source": { "type": "git", - "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60" + "url": "https://github.com/guzzle/promises.git", + "reference": "b94b2807d85443f9719887892882d0329d1e2598" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", - "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", + "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598", + "reference": "b94b2807d85443f9719887892882d0329d1e2598", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.5" }, "require-dev": { - "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", - "json-schema/json-schema-test-suite": "1.2.0", - "phpunit/phpunit": "^4.8.35" + "symfony/phpunit-bridge": "^4.4 || ^5.1" }, - "bin": [ - "bin/validate-json" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0.x-dev" + "dev-master": "1.5-dev" } }, "autoload": { + "files": [ + "src/functions_include.php" + ], "psr-4": { - "JsonSchema\\": "src/JsonSchema/" + "GuzzleHttp\\Promise\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -414,65 +564,95 @@ ], "authors": [ { - "name": "Bruno Prieto Reis", - "email": "bruno.p.reis@gmail.com" + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" }, { - "name": "Justin Rainbow", - "email": "justin.rainbow@gmail.com" + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" }, { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" }, { - "name": "Robert Schönthal", - "email": "seroscho@googlemail.com" + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], - "description": "A library to validate a json schema.", - "homepage": "https://github.com/justinrainbow/json-schema", + "description": "Guzzle promises library", "keywords": [ - "json", - "schema" + "promise" ], "support": { - "issues": "https://github.com/justinrainbow/json-schema/issues", - "source": "https://github.com/justinrainbow/json-schema/tree/5.2.12" + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.5.2" }, - "time": "2022-04-13T08:02:27+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2022-08-28T14:55:35+00:00" }, { - "name": "laravel/serializable-closure", - "version": "v1.2.0", + "name": "guzzlehttp/psr7", + "version": "2.4.1", "source": { "type": "git", - "url": "https://github.com/laravel/serializable-closure.git", - "reference": "09f0e9fb61829f628205b7c94906c28740ff9540" + "url": "https://github.com/guzzle/psr7.git", + "reference": "69568e4293f4fa993f3b0e51c9723e1e17c41379" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/09f0e9fb61829f628205b7c94906c28740ff9540", - "reference": "09f0e9fb61829f628205b7c94906c28740ff9540", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/69568e4293f4fa993f3b0e51c9723e1e17c41379", + "reference": "69568e4293f4fa993f3b0e51c9723e1e17c41379", "shasum": "" }, "require": { - "php": "^7.3|^8.0" + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" }, "require-dev": { - "pestphp/pest": "^1.18", - "phpstan/phpstan": "^0.12.98", - "symfony/var-dumper": "^5.3" + "bamarni/composer-bin-plugin": "^1.8.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, "type": "library", "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, "branch-alias": { - "dev-master": "1.x-dev" + "dev-master": "2.4-dev" } }, "autoload": { "psr-4": { - "Laravel\\SerializableClosure\\": "src/" + "GuzzleHttp\\Psr7\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -481,198 +661,959 @@ ], "authors": [ { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" }, { - "name": "Nuno Maduro", - "email": "nuno@laravel.com" + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" } ], - "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "description": "PSR-7 message implementation that also provides common utility methods", "keywords": [ - "closure", - "laravel", - "serializable" + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" ], "support": { - "issues": "https://github.com/laravel/serializable-closure/issues", - "source": "https://github.com/laravel/serializable-closure" + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.4.1" }, - "time": "2022-05-16T17:09:47+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2022-08-28T14:45:39+00:00" }, { - "name": "php-di/invoker", - "version": "2.3.3", + "name": "hypernode/api-client", + "version": "dev-master", "source": { "type": "git", - "url": "https://github.com/PHP-DI/Invoker.git", - "reference": "cd6d9f267d1a3474bdddf1be1da079f01b942786" + "url": "https://github.com/ByteInternet/hypernode-api-php.git", + "reference": "309a671f5c9bd2bbc4fee8566daf5d8e48d1c01f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/cd6d9f267d1a3474bdddf1be1da079f01b942786", - "reference": "cd6d9f267d1a3474bdddf1be1da079f01b942786", + "url": "https://api.github.com/repos/ByteInternet/hypernode-api-php/zipball/309a671f5c9bd2bbc4fee8566daf5d8e48d1c01f", + "reference": "309a671f5c9bd2bbc4fee8566daf5d8e48d1c01f", "shasum": "" }, "require": { - "php": ">=7.3", - "psr/container": "^1.0|^2.0" + "ext-curl": "*", + "ext-json": "*", + "nesbot/carbon": "^2.0", + "php-http/client-common": "^2.5", + "php-http/discovery": "^1.14", + "psr/http-client-implementation": "^1.0", + "symfony/polyfill-php80": "^1.0" }, "require-dev": { - "athletic/athletic": "~0.1.8", - "mnapoli/hard-mode": "~0.3.0", - "phpunit/phpunit": "^9.0" + "friendsofphp/php-cs-fixer": "^3.9", + "guzzlehttp/guzzle": "^7.4", + "nikic/php-parser": "^4.14", + "phpunit/phpunit": "^9.5" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { - "Invoker\\": "src/" + "Hypernode\\Api\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Generic and extensible callable invoker", - "homepage": "https://github.com/PHP-DI/Invoker", - "keywords": [ - "callable", - "dependency", - "dependency-injection", - "injection", - "invoke", - "invoker" - ], - "support": { - "issues": "https://github.com/PHP-DI/Invoker/issues", - "source": "https://github.com/PHP-DI/Invoker/tree/2.3.3" - }, - "funding": [ + "authors": [ { - "url": "https://github.com/mnapoli", - "type": "github" + "name": "Hypernode" } ], - "time": "2021-12-13T09:22:56+00:00" + "description": "Hypernode API Client for PHP", + "support": { + "issues": "https://github.com/ByteInternet/hypernode-api-php/issues", + "source": "https://github.com/ByteInternet/hypernode-api-php/tree/master" + }, + "time": "2022-09-12T13:18:22+00:00" }, { - "name": "php-di/php-di", - "version": "6.4.0", + "name": "hypernode/deploy-configuration", + "version": "dev-ephemeral_servers", "source": { "type": "git", - "url": "https://github.com/PHP-DI/PHP-DI.git", - "reference": "ae0f1b3b03d8b29dff81747063cbfd6276246cc4" + "url": "https://github.com/ByteInternet/hypernode-deploy-configuration.git", + "reference": "bbb62227a92205b9f31548442c27ee27321debe5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/ae0f1b3b03d8b29dff81747063cbfd6276246cc4", + "url": "https://api.github.com/repos/ByteInternet/hypernode-deploy-configuration/zipball/bbb62227a92205b9f31548442c27ee27321debe5", + "reference": "bbb62227a92205b9f31548442c27ee27321debe5", + "shasum": "" + }, + "require": { + "deployer/deployer": "^7.0", + "psr/log": "^1.0" + }, + "conflict": { + "hipex/deploy-configuration": "*" + }, + "type": "library", + "autoload": { + "files": [ + "src/autoload.php", + "src/functions.php" + ], + "psr-4": { + "Hypernode\\DeployConfiguration\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "description": "Hypernode deploy configuration files", + "support": { + "issues": "https://github.com/ByteInternet/hypernode-deploy-configuration/issues", + "source": "https://github.com/ByteInternet/hypernode-deploy-configuration/tree/ephemeral_servers" + }, + "time": "2022-09-12T10:17:41+00:00" + }, + { + "name": "justinrainbow/json-schema", + "version": "5.2.12", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", + "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "support": { + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/5.2.12" + }, + "time": "2022-04-13T08:02:27+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v1.2.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "47afb7fae28ed29057fdca37e16a84f90cc62fae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/47afb7fae28ed29057fdca37e16a84f90cc62fae", + "reference": "47afb7fae28ed29057fdca37e16a84f90cc62fae", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "nesbot/carbon": "^2.61", + "pestphp/pest": "^1.21.3", + "phpstan/phpstan": "^1.8.2", + "symfony/var-dumper": "^5.4.11" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2022-09-08T13:45:54+00:00" + }, + { + "name": "nesbot/carbon", + "version": "2.62.1", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "01bc4cdefe98ef58d1f9cb31bdbbddddf2a88f7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/01bc4cdefe98ef58d1f9cb31bdbbddddf2a88f7a", + "reference": "01bc4cdefe98ef58d1f9cb31bdbbddddf2a88f7a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.1.8 || ^8.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" + }, + "require-dev": { + "doctrine/dbal": "^2.0 || ^3.0", + "doctrine/orm": "^2.7", + "friendsofphp/php-cs-fixer": "^3.0", + "kylekatarnls/multi-tester": "^2.0", + "ondrejmirtes/better-reflection": "*", + "phpmd/phpmd": "^2.9", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.99 || ^1.7.14", + "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6", + "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", + "squizlabs/php_codesniffer": "^3.4" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-3.x": "3.x-dev", + "dev-master": "2.x-dev" + }, + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/Carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2022-09-02T07:48:13+00:00" + }, + { + "name": "php-di/invoker", + "version": "2.3.3", + "source": { + "type": "git", + "url": "https://github.com/PHP-DI/Invoker.git", + "reference": "cd6d9f267d1a3474bdddf1be1da079f01b942786" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/cd6d9f267d1a3474bdddf1be1da079f01b942786", + "reference": "cd6d9f267d1a3474bdddf1be1da079f01b942786", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "psr/container": "^1.0|^2.0" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "mnapoli/hard-mode": "~0.3.0", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Invoker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Generic and extensible callable invoker", + "homepage": "https://github.com/PHP-DI/Invoker", + "keywords": [ + "callable", + "dependency", + "dependency-injection", + "injection", + "invoke", + "invoker" + ], + "support": { + "issues": "https://github.com/PHP-DI/Invoker/issues", + "source": "https://github.com/PHP-DI/Invoker/tree/2.3.3" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + } + ], + "time": "2021-12-13T09:22:56+00:00" + }, + { + "name": "php-di/php-di", + "version": "6.4.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-DI/PHP-DI.git", + "reference": "ae0f1b3b03d8b29dff81747063cbfd6276246cc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/ae0f1b3b03d8b29dff81747063cbfd6276246cc4", "reference": "ae0f1b3b03d8b29dff81747063cbfd6276246cc4", "shasum": "" }, "require": { - "laravel/serializable-closure": "^1.0", - "php": ">=7.4.0", - "php-di/invoker": "^2.0", - "php-di/phpdoc-reader": "^2.0.1", - "psr/container": "^1.0" + "laravel/serializable-closure": "^1.0", + "php": ">=7.4.0", + "php-di/invoker": "^2.0", + "php-di/phpdoc-reader": "^2.0.1", + "psr/container": "^1.0" + }, + "provide": { + "psr/container-implementation": "^1.0" + }, + "require-dev": { + "doctrine/annotations": "~1.10", + "friendsofphp/php-cs-fixer": "^2.4", + "mnapoli/phpunit-easymock": "^1.2", + "ocramius/proxy-manager": "^2.11.2", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "doctrine/annotations": "Install it if you want to use annotations (version ~1.2)", + "ocramius/proxy-manager": "Install it if you want to use lazy injection (version ~2.0)" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "DI\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The dependency injection container for humans", + "homepage": "https://php-di.org/", + "keywords": [ + "PSR-11", + "container", + "container-interop", + "dependency injection", + "di", + "ioc", + "psr11" + ], + "support": { + "issues": "https://github.com/PHP-DI/PHP-DI/issues", + "source": "https://github.com/PHP-DI/PHP-DI/tree/6.4.0" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/php-di/php-di", + "type": "tidelift" + } + ], + "time": "2022-04-09T16:46:38+00:00" + }, + { + "name": "php-di/phpdoc-reader", + "version": "2.2.1", + "source": { + "type": "git", + "url": "https://github.com/PHP-DI/PhpDocReader.git", + "reference": "66daff34cbd2627740ffec9469ffbac9f8c8185c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-DI/PhpDocReader/zipball/66daff34cbd2627740ffec9469ffbac9f8c8185c", + "reference": "66daff34cbd2627740ffec9469ffbac9f8c8185c", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "require-dev": { + "mnapoli/hard-mode": "~0.3.0", + "phpunit/phpunit": "^8.5|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpDocReader\\": "src/PhpDocReader" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PhpDocReader parses @var and @param values in PHP docblocks (supports namespaced class names with the same resolution rules as PHP)", + "keywords": [ + "phpdoc", + "reflection" + ], + "support": { + "issues": "https://github.com/PHP-DI/PhpDocReader/issues", + "source": "https://github.com/PHP-DI/PhpDocReader/tree/2.2.1" + }, + "time": "2020-10-12T12:39:22+00:00" + }, + { + "name": "php-http/client-common", + "version": "2.5.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/client-common.git", + "reference": "d135751167d57e27c74de674d6a30cef2dc8e054" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/client-common/zipball/d135751167d57e27c74de674d6a30cef2dc8e054", + "reference": "d135751167d57e27c74de674d6a30cef2dc8e054", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "php-http/httplug": "^2.0", + "php-http/message": "^1.6", + "php-http/message-factory": "^1.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "symfony/options-resolver": "~4.0.15 || ~4.1.9 || ^4.2.1 || ^5.0 || ^6.0", + "symfony/polyfill-php80": "^1.17" + }, + "require-dev": { + "doctrine/instantiator": "^1.1", + "guzzlehttp/psr7": "^1.4", + "nyholm/psr7": "^1.2", + "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1", + "phpspec/prophecy": "^1.10.2", + "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3" + }, + "suggest": { + "ext-json": "To detect JSON responses with the ContentTypePlugin", + "ext-libxml": "To detect XML responses with the ContentTypePlugin", + "php-http/cache-plugin": "PSR-6 Cache plugin", + "php-http/logger-plugin": "PSR-3 Logger plugin", + "php-http/stopwatch-plugin": "Symfony Stopwatch plugin" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Client\\Common\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Common HTTP Client implementations and tools for HTTPlug", + "homepage": "http://httplug.io", + "keywords": [ + "client", + "common", + "http", + "httplug" + ], + "support": { + "issues": "https://github.com/php-http/client-common/issues", + "source": "https://github.com/php-http/client-common/tree/2.5.0" + }, + "time": "2021-11-26T15:01:24+00:00" + }, + { + "name": "php-http/discovery", + "version": "1.14.3", + "source": { + "type": "git", + "url": "https://github.com/php-http/discovery.git", + "reference": "31d8ee46d0215108df16a8527c7438e96a4d7735" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/discovery/zipball/31d8ee46d0215108df16a8527c7438e96a4d7735", + "reference": "31d8ee46d0215108df16a8527c7438e96a4d7735", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "nyholm/psr7": "<1.0" + }, + "require-dev": { + "graham-campbell/phpspec-skip-example-extension": "^5.0", + "php-http/httplug": "^1.0 || ^2.0", + "php-http/message-factory": "^1.0", + "phpspec/phpspec": "^5.1 || ^6.1" + }, + "suggest": { + "php-http/message": "Allow to use Guzzle, Diactoros or Slim Framework factories" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Discovery\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Finds installed HTTPlug implementations and PSR-7 message factories", + "homepage": "http://php-http.org", + "keywords": [ + "adapter", + "client", + "discovery", + "factory", + "http", + "message", + "psr7" + ], + "support": { + "issues": "https://github.com/php-http/discovery/issues", + "source": "https://github.com/php-http/discovery/tree/1.14.3" + }, + "time": "2022-07-11T14:04:40+00:00" + }, + { + "name": "php-http/httplug", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/httplug.git", + "reference": "f640739f80dfa1152533976e3c112477f69274eb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/httplug/zipball/f640739f80dfa1152533976e3c112477f69274eb", + "reference": "f640739f80dfa1152533976e3c112477f69274eb", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "php-http/promise": "^1.1", + "psr/http-client": "^1.0", + "psr/http-message": "^1.0" + }, + "require-dev": { + "friends-of-phpspec/phpspec-code-coverage": "^4.1", + "phpspec/phpspec": "^5.1 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eric GELOEN", + "email": "geloen.eric@gmail.com" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "HTTPlug, the HTTP client abstraction for PHP", + "homepage": "http://httplug.io", + "keywords": [ + "client", + "http" + ], + "support": { + "issues": "https://github.com/php-http/httplug/issues", + "source": "https://github.com/php-http/httplug/tree/2.3.0" + }, + "time": "2022-02-21T09:52:22+00:00" + }, + { + "name": "php-http/message", + "version": "1.13.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/message.git", + "reference": "7886e647a30a966a1a8d1dad1845b71ca8678361" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/message/zipball/7886e647a30a966a1a8d1dad1845b71ca8678361", + "reference": "7886e647a30a966a1a8d1dad1845b71ca8678361", + "shasum": "" + }, + "require": { + "clue/stream-filter": "^1.5", + "php": "^7.1 || ^8.0", + "php-http/message-factory": "^1.0.2", + "psr/http-message": "^1.0" }, "provide": { - "psr/container-implementation": "^1.0" + "php-http/message-factory-implementation": "1.0" }, "require-dev": { - "doctrine/annotations": "~1.10", - "friendsofphp/php-cs-fixer": "^2.4", - "mnapoli/phpunit-easymock": "^1.2", - "ocramius/proxy-manager": "^2.11.2", - "phpstan/phpstan": "^0.12", - "phpunit/phpunit": "^9.5" + "ergebnis/composer-normalize": "^2.6", + "ext-zlib": "*", + "guzzlehttp/psr7": "^1.0", + "laminas/laminas-diactoros": "^2.0", + "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1", + "slim/slim": "^3.0" }, "suggest": { - "doctrine/annotations": "Install it if you want to use annotations (version ~1.2)", - "ocramius/proxy-manager": "Install it if you want to use lazy injection (version ~2.0)" + "ext-zlib": "Used with compressor/decompressor streams", + "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories", + "laminas/laminas-diactoros": "Used with Diactoros Factories", + "slim/slim": "Used with Slim Framework PSR-7 implementation" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, "autoload": { "files": [ - "src/functions.php" + "src/filters.php" ], "psr-4": { - "DI\\": "src/" + "Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "The dependency injection container for humans", - "homepage": "https://php-di.org/", + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "HTTP Message related tools", + "homepage": "http://php-http.org", "keywords": [ - "PSR-11", - "container", - "container-interop", - "dependency injection", - "di", - "ioc", - "psr11" + "http", + "message", + "psr-7" ], "support": { - "issues": "https://github.com/PHP-DI/PHP-DI/issues", - "source": "https://github.com/PHP-DI/PHP-DI/tree/6.4.0" + "issues": "https://github.com/php-http/message/issues", + "source": "https://github.com/php-http/message/tree/1.13.0" }, - "funding": [ - { - "url": "https://github.com/mnapoli", - "type": "github" - }, + "time": "2022-02-11T13:41:14+00:00" + }, + { + "name": "php-http/message-factory", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-http/message-factory.git", + "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/message-factory/zipball/a478cb11f66a6ac48d8954216cfed9aa06a501a1", + "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://tidelift.com/funding/github/packagist/php-di/php-di", - "type": "tidelift" + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" } ], - "time": "2022-04-09T16:46:38+00:00" + "description": "Factory interfaces for PSR-7 HTTP Message", + "homepage": "http://php-http.org", + "keywords": [ + "factory", + "http", + "message", + "stream", + "uri" + ], + "support": { + "issues": "https://github.com/php-http/message-factory/issues", + "source": "https://github.com/php-http/message-factory/tree/master" + }, + "time": "2015-12-19T14:08:53+00:00" }, { - "name": "php-di/phpdoc-reader", - "version": "2.2.1", + "name": "php-http/promise", + "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/PHP-DI/PhpDocReader.git", - "reference": "66daff34cbd2627740ffec9469ffbac9f8c8185c" + "url": "https://github.com/php-http/promise.git", + "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-DI/PhpDocReader/zipball/66daff34cbd2627740ffec9469ffbac9f8c8185c", - "reference": "66daff34cbd2627740ffec9469ffbac9f8c8185c", + "url": "https://api.github.com/repos/php-http/promise/zipball/4c4c1f9b7289a2ec57cde7f1e9762a5789506f88", + "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88", "shasum": "" }, "require": { - "php": ">=7.2.0" + "php": "^7.1 || ^8.0" }, "require-dev": { - "mnapoli/hard-mode": "~0.3.0", - "phpunit/phpunit": "^8.5|^9.0" + "friends-of-phpspec/phpspec-code-coverage": "^4.3.2", + "phpspec/phpspec": "^5.1.2 || ^6.2" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, "autoload": { "psr-4": { - "PhpDocReader\\": "src/PhpDocReader" + "Http\\Promise\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "PhpDocReader parses @var and @param values in PHP docblocks (supports namespaced class names with the same resolution rules as PHP)", + "authors": [ + { + "name": "Joel Wurtz", + "email": "joel.wurtz@gmail.com" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Promise used for asynchronous HTTP requests", + "homepage": "http://httplug.io", "keywords": [ - "phpdoc", - "reflection" + "promise" ], "support": { - "issues": "https://github.com/PHP-DI/PhpDocReader/issues", - "source": "https://github.com/PHP-DI/PhpDocReader/tree/2.2.1" + "issues": "https://github.com/php-http/promise/issues", + "source": "https://github.com/php-http/promise/tree/1.1.0" }, - "time": "2020-10-12T12:39:22+00:00" + "time": "2020-07-07T09:29:14+00:00" }, { "name": "psr/cache", @@ -728,22 +1669,128 @@ "version": "1.1.2", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + "url": "https://github.com/php-fig/container.git", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.2" + }, + "time": "2021-11-05T16:50:12+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client/tree/master" + }, + "time": "2020-06-29T06:28:15+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", "shasum": "" }, "require": { - "php": ">=7.4.0" + "php": ">=7.0.0", + "psr/http-message": "^1.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, "autoload": { "psr-4": { - "Psr\\Container\\": "src/" + "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -753,23 +1800,24 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "homepage": "http://www.php-fig.org/" } ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "Common interfaces for PSR-7 HTTP message factories", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" ], "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" + "source": "https://github.com/php-fig/http-factory/tree/master" }, - "time": "2021-11-05T16:50:12+00:00" + "time": "2019-04-30T12:38:16+00:00" }, { "name": "psr/http-message", @@ -874,6 +1922,50 @@ }, "time": "2021-05-03T11:20:27+00:00" }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, { "name": "react/cache", "version": "v1.1.1", @@ -952,16 +2044,16 @@ }, { "name": "react/dns", - "version": "v1.9.0", + "version": "v1.10.0", "source": { "type": "git", "url": "https://github.com/reactphp/dns.git", - "reference": "6d38296756fa644e6cb1bfe95eff0f9a4ed6edcb" + "reference": "a5427e7dfa47713e438016905605819d101f238c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/6d38296756fa644e6cb1bfe95eff0f9a4ed6edcb", - "reference": "6d38296756fa644e6cb1bfe95eff0f9a4ed6edcb", + "url": "https://api.github.com/repos/reactphp/dns/zipball/a5427e7dfa47713e438016905605819d101f238c", + "reference": "a5427e7dfa47713e438016905605819d101f238c", "shasum": "" }, "require": { @@ -969,11 +2061,11 @@ "react/cache": "^1.0 || ^0.6 || ^0.5", "react/event-loop": "^1.2", "react/promise": "^3.0 || ^2.7 || ^1.2.1", - "react/promise-timer": "^1.8" + "react/promise-timer": "^1.9" }, "require-dev": { - "clue/block-react": "^1.2", - "phpunit/phpunit": "^9.3 || ^4.8.35" + "phpunit/phpunit": "^9.3 || ^4.8.35", + "react/async": "^4 || ^3 || ^2" }, "type": "library", "autoload": { @@ -1016,7 +2108,7 @@ ], "support": { "issues": "https://github.com/reactphp/dns/issues", - "source": "https://github.com/reactphp/dns/tree/v1.9.0" + "source": "https://github.com/reactphp/dns/tree/v1.10.0" }, "funding": [ { @@ -1028,7 +2120,7 @@ "type": "github" } ], - "time": "2021-12-20T08:46:54+00:00" + "time": "2022-09-08T12:22:46+00:00" }, { "name": "react/event-loop", @@ -1282,16 +2374,16 @@ }, { "name": "react/promise-stream", - "version": "v1.4.0", + "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/reactphp/promise-stream.git", - "reference": "ef05517b99e4363beaa7993d4e2d6c50f1b22a09" + "reference": "e6d2805e09ad50c4896f65f5e8705fe4ee7731a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise-stream/zipball/ef05517b99e4363beaa7993d4e2d6c50f1b22a09", - "reference": "ef05517b99e4363beaa7993d4e2d6c50f1b22a09", + "url": "https://api.github.com/repos/reactphp/promise-stream/zipball/e6d2805e09ad50c4896f65f5e8705fe4ee7731a3", + "reference": "e6d2805e09ad50c4896f65f5e8705fe4ee7731a3", "shasum": "" }, "require": { @@ -1300,10 +2392,7 @@ "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.6" }, "require-dev": { - "clue/block-react": "^1.0", - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3", - "react/promise-timer": "^1.0" + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" }, "type": "library", "autoload": { @@ -1352,7 +2441,7 @@ ], "support": { "issues": "https://github.com/reactphp/promise-stream/issues", - "source": "https://github.com/reactphp/promise-stream/tree/v1.4.0" + "source": "https://github.com/reactphp/promise-stream/tree/v1.5.0" }, "funding": [ { @@ -1364,7 +2453,7 @@ "type": "github" } ], - "time": "2022-06-20T10:36:51+00:00" + "time": "2022-09-09T11:42:18+00:00" }, { "name": "react/promise-timer", @@ -2070,6 +3159,73 @@ ], "time": "2022-04-12T15:48:08+00:00" }, + { + "name": "symfony/options-resolver", + "version": "v6.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "a3016f5442e28386ded73c43a32a5b68586dd1c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a3016f5442e28386ded73c43a32a5b68586dd1c4", + "reference": "a3016f5442e28386ded73c43a32a5b68586dd1c4", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v6.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-02-25T11:15:52+00:00" + }, { "name": "symfony/polyfill-ctype", "version": "v1.26.0", @@ -2868,6 +4024,183 @@ ], "time": "2022-08-12T18:05:43+00:00" }, + { + "name": "symfony/translation", + "version": "v6.1.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "45d0f5bb8df7255651ca91c122fab604e776af03" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/45d0f5bb8df7255651ca91c122fab604e776af03", + "reference": "45d0f5bb8df7255651ca91c122fab604e776af03", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.3|^3.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/http-client-contracts": "^1.1|^2.0|^3.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/intl": "^5.4|^6.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^5.4|^6.0", + "symfony/service-contracts": "^1.1.2|^2|^3", + "symfony/yaml": "^5.4|^6.0" + }, + "suggest": { + "psr/log-implementation": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v6.1.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-08-02T16:17:38+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "606be0f48e05116baef052f7f3abdb345c8e02cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/606be0f48e05116baef052f7f3abdb345c8e02cc", + "reference": "606be0f48e05116baef052f7f3abdb345c8e02cc", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.1-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-06-27T17:24:16+00:00" + }, { "name": "symfony/yaml", "version": "v5.4.12", @@ -3860,16 +5193,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.14.0", + "version": "v4.15.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1" + "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/34bea19b6e03d8153165d8f30bba4c3be86184c1", - "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/0ef6c55a3f47f89d7a374e6f835197a0b5fcf900", + "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900", "shasum": "" }, "require": { @@ -3910,9 +5243,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.14.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.1" }, - "time": "2022-05-31T20:59:12+00:00" + "time": "2022-09-04T07:30:47+00:00" }, { "name": "openlss/lib-array2xml", @@ -4693,12 +6026,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "bd4a45f60f097f0c44f77c6d72caa24ebc9b9f66" + "reference": "6d260392fad173d6ee6e3a93c875d9327db1109b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/bd4a45f60f097f0c44f77c6d72caa24ebc9b9f66", - "reference": "bd4a45f60f097f0c44f77c6d72caa24ebc9b9f66", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/6d260392fad173d6ee6e3a93c875d9327db1109b", + "reference": "6d260392fad173d6ee6e3a93c875d9327db1109b", "shasum": "" }, "conflict": { @@ -4834,8 +6167,9 @@ "gaoming13/wechat-php-sdk": "<=1.10.2", "genix/cms": "<=1.1.11", "getgrav/grav": "<1.7.34", - "getkirby/cms": "<3.5.8", + "getkirby/cms": "<3.5.8.1|>=3.6,<3.6.6.1|>=3.7,<3.7.4", "getkirby/panel": "<2.5.14", + "getkirby/starterkit": "<=3.7.0.2", "gilacms/gila": "<=1.11.4", "globalpayments/php-sdk": "<2", "google/protobuf": "<3.15", @@ -4928,6 +6262,7 @@ "nette/application": ">=2,<2.0.19|>=2.1,<2.1.13|>=2.2,<2.2.10|>=2.3,<2.3.14|>=2.4,<2.4.16|>=3,<3.0.6", "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", "nilsteampassnet/teampass": "<=2.1.27.36", + "notrinos/notrinos-erp": "<=0.7", "noumo/easyii": "<=0.9", "nukeviet/nukeviet": "<4.5.2", "nystudio107/craft-seomatic": "<3.4.12", @@ -4980,7 +6315,7 @@ "prestashop/contactform": ">1.0.1,<4.3", "prestashop/gamification": "<2.3.2", "prestashop/prestashop": ">=1.6.0.10,<1.7.8.7", - "prestashop/productcomments": ">=4,<4.2.1", + "prestashop/productcomments": "<5.0.2", "prestashop/ps_emailsubscription": "<2.6.1", "prestashop/ps_facetedsearch": "<3.4.1", "prestashop/ps_linklist": "<3.1", @@ -5031,7 +6366,7 @@ "simplito/elliptic-php": "<1.0.6", "slim/slim": "<2.6", "smarty/smarty": "<3.1.45|>=4,<4.1.1", - "snipe/snipe-it": "<=6.0.2|>= 6.0.0-RC-1, <= 6.0.0-RC-5", + "snipe/snipe-it": "<6.0.10|>= 6.0.0-RC-1, <= 6.0.0-RC-5", "socalnick/scn-social-auth": "<1.15.2", "socialiteproviders/steam": "<1.1", "spipu/html2pdf": "<5.2.4", @@ -5201,7 +6536,7 @@ "type": "tidelift" } ], - "time": "2022-08-22T17:06:07+00:00" + "time": "2022-08-31T22:04:18+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -6040,16 +7375,16 @@ }, { "name": "vimeo/psalm", - "version": "4.26.0", + "version": "4.27.0", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "6998fabb2bf528b65777bf9941920888d23c03ac" + "reference": "faf106e717c37b8c81721845dba9de3d8deed8ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/6998fabb2bf528b65777bf9941920888d23c03ac", - "reference": "6998fabb2bf528b65777bf9941920888d23c03ac", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/faf106e717c37b8c81721845dba9de3d8deed8ff", + "reference": "faf106e717c37b8c81721845dba9de3d8deed8ff", "shasum": "" }, "require": { @@ -6141,9 +7476,9 @@ ], "support": { "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/4.26.0" + "source": "https://github.com/vimeo/psalm/tree/4.27.0" }, - "time": "2022-07-31T13:10:26+00:00" + "time": "2022-08-31T13:47:09+00:00" }, { "name": "webmozart/path-util", @@ -6208,6 +7543,8 @@ "minimum-stability": "stable", "stability-flags": { "deployer/deployer": 20, + "hypernode/api-client": 20, + "hypernode/deploy-configuration": 20, "roave/security-advisories": 20 }, "prefer-stable": false, diff --git a/src/DeployRunner.php b/src/DeployRunner.php index 2b736e1..3bb179c 100644 --- a/src/DeployRunner.php +++ b/src/DeployRunner.php @@ -5,11 +5,17 @@ use Deployer\Deployer; use Deployer\Exception\Exception; use Deployer\Exception\GracefulShutdownException; +use Hypernode\Api\Exception\HypernodeApiClientException; +use Hypernode\Api\Exception\HypernodeApiServerException; +use Hypernode\Api\HypernodeClient; +use Hypernode\Api\HypernodeClientFactory; +use Hypernode\Api\Resource\Logbook\Flow; use Hypernode\Deploy\Console\Output\OutputWatcher; use Hypernode\Deploy\Deployer\RecipeLoader; use Hypernode\Deploy\Exception\InvalidConfigurationException; use Hypernode\Deploy\Deployer\Task\ConfigurableTaskInterface; use Hypernode\Deploy\Deployer\Task\TaskFactory; +use Hypernode\Deploy\Exception\TimeoutException; use Hypernode\DeployConfiguration\Configuration; use Hypernode\DeployConfiguration\Server; use Hypernode\DeployConfiguration\ServerRoleConfigurableInterface; @@ -51,6 +57,8 @@ class DeployRunner */ private $recipeLoader; + private HypernodeClient $hypernodeClient; + public function __construct( TaskFactory $taskFactory, InputInterface $input, @@ -61,6 +69,7 @@ public function __construct( $this->input = $input; $this->log = $log; $this->recipeLoader = $recipeLoader; + $this->hypernodeClient = HypernodeClientFactory::create(getenv('HYPERNODE_API_TOKEN') ?: ''); } /** @@ -189,6 +198,8 @@ private function configureStages(Configuration $config): void private function configureStageServer(Stage $stage, Server $server, Configuration $config): void { + $this->maybeConfigureEphemeralServer($server); + $host = host($stage->getName() . ':' . $server->getHostname()); $host->setHostname($server->getHostname()); $host->setPort(22); @@ -242,6 +253,57 @@ private function configureStageServer(Stage $stage, Server $server, Configuratio } } + private function maybeConfigureEphemeralServer(Server $server): void + { + $serverOptions = $server->getOptions(); + $isEphemeral = $serverOptions[Server::OPTION_HN_EPHEMERAL] ?? false; + $parentApp = $serverOptions[Server::OPTION_HN_PARENT_APP] ?? null; + if ($isEphemeral && $parentApp) { + $this->log->info(sprintf('Creating an ephemeral Hypernode based on %s.', $parentApp)); + $ephemeralApp = $this->hypernodeClient->ephemeralApp->create($parentApp); + $server->setHostname(sprintf("%s.hypernode.io", $ephemeralApp)); + $this->log->info(sprintf('Successfully requested ephemeral Hypernode, name is %s.', $ephemeralApp)); + $this->log->info('Waiting for ephemeral Hypernode to become available...'); + $this->waitForEphemeralApp($ephemeralApp); + $this->log->info('Ephemeral Hypernode has become available!'); + } + } + + /** + * Poll and wait for ephemeral app to become available. + * + * @throws HypernodeApiClientException + * @throws HypernodeApiServerException + * @throws TimeoutException + */ + private function waitForEphemeralApp(string $ephemeralApp, int $timeout = 900): void + { + $latest = microtime(true); + $timeElapsed = 0; + $resolved = false; + + while ($timeElapsed < $timeout && !$resolved) { + $now = microtime(true); + $timeElapsed += $now - $latest; + $latest = $now; + + $flows = $this->hypernodeClient->logbook->getList($ephemeralApp); + $remaining = array_filter($flows, fn (Flow $flow) => !$flow->isComplete()); + if ($flows && !$remaining) { + $resolved = true; + break; + } + + sleep(5); + } + + if (!$resolved) { + throw new TimeoutException( + sprintf('Timed out waiting for ephemeral Hypernode %s to become available', $ephemeralApp) + ); + } + } + /** * Initialize build stage */ diff --git a/src/Exception/TimeoutException.php b/src/Exception/TimeoutException.php new file mode 100644 index 0000000..d77a245 --- /dev/null +++ b/src/Exception/TimeoutException.php @@ -0,0 +1,11 @@ + Date: Tue, 13 Sep 2022 09:24:41 +0200 Subject: [PATCH 02/22] Add CI testsuite for ephemeral nodes --- .github/workflows/test.yaml | 29 +++-- ci/key.pub | 1 + ci/test/magento/deploy_ephemeral.php | 17 +++ ci/test/run-ephemeral.sh | 40 +++++++ ci/test/run-general.sh | 163 ++++++++++++++++++++++++++ runtests.sh | 164 +-------------------------- 6 files changed, 249 insertions(+), 165 deletions(-) create mode 100644 ci/key.pub create mode 100644 ci/test/magento/deploy_ephemeral.php create mode 100755 ci/test/run-ephemeral.sh create mode 100755 ci/test/run-general.sh diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index dc94864..590fad7 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -7,15 +7,30 @@ jobs: strategy: matrix: php_version: [7.4, 8.1] + testsuite: [general, ephemeral] runs-on: ubuntu-latest steps: - - name: Checkout hypernode-deploy - uses: actions/checkout@v3 - - name: Run test script - run: MAGENTO_REPO=./magento2 ./runtests.sh - shell: bash - env: - PHP_VERSION: ${{ matrix.php_version }} + - name: Checkout hypernode-deploy + uses: actions/checkout@v3 + - name: Run general testsuite + if: ${{ matrix.testsuite == 'general' }} + run: MAGENTO_REPO=./magento2 ./runtests.sh general + shell: bash + env: + PHP_VERSION: ${{ matrix.php_version }} + - name: Start SSH agent for ephemeral testsuite + if: ${{ matrix.testsuite == 'ephemeral' && matrix.php_version == '8.1' }} + uses: webfactory/ssh-agent@v0.5.4 + with: + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + - name: Run ephemeral testsuite + if: ${{ matrix.testsuite == 'ephemeral' && matrix.php_version == '8.1' }} + run: ./runtests.sh ephemeral + shell: bash + env: + PHP_VERSION: ${{ matrix.php_version }} + HYPERNODE_API_TOKEN: ${{ secrets.HYPERNODE_API_TOKEN }} + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} code_quality: strategy: matrix: diff --git a/ci/key.pub b/ci/key.pub new file mode 100644 index 0000000..5e0a32e --- /dev/null +++ b/ci/key.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDqf5tGbhjIbRQJ40bTvw69z0kUHQEeDtGkX5DTzXJ1IPHcFpbq+nxc3f++45V8FA3fq31xTjCR3+TWy/AGCVmuntVwJVCEaC/U3X0YDscmzAA4LEOfmMHIGnrJdeTJS4+N6L5uIlZqyBIeJXyEEgFpcHm4i1HGm13DR/XG7lTaXSkRI9IVb0i3S0VdrRjtqkg4M3ZjgNfAwmoDE3dGNwIBH5EVy8t/YNj/RH0TNdhTDL3AGwne6h2bTWb5SNLHNXvu2Wc5aiJlvn2E2W7mcpnPxEkVs1AeDEaPHcLaGE2+Bt/I4ntyjOy9pFDcf28sPKj76S3Sq0Wwdb8rYmT+lerIPfjwj1VPXTU1dhvDJ1ffTufp3Sn67qi7NfBMYayYxLJWh9q84RHgYhog2SNXM+autsTU40GDZUtDtORRZ+Rg5i1cGxUCRT+Fpcx3aFCM/yTLvU80qZkmfgkQwKnGCCFygDsXPuFbyRXRz9MI1+lYGg17ufW5HSHrAw34Rw2yd8k= hypernode-deploy-ci diff --git a/ci/test/magento/deploy_ephemeral.php b/ci/test/magento/deploy_ephemeral.php new file mode 100644 index 0000000..cda7fbe --- /dev/null +++ b/ci/test/magento/deploy_ephemeral.php @@ -0,0 +1,17 @@ +addStage('test', 'banaan.store'); +$productionStage->addEphemeralServer('hndeployintegr8'); + +return $configuration; diff --git a/ci/test/run-ephemeral.sh b/ci/test/run-ephemeral.sh new file mode 100755 index 0000000..b601fdc --- /dev/null +++ b/ci/test/run-ephemeral.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +set -e +set -x + +# Handy aliases +HN="ssh app@hndeployintegr8.hypernode.io -o StrictHostKeyChecking=no" +DP="docker run -v /tmp/m2build:/web -e HYPERNODE_API_TOKEN -e SSH_PRIVATE_KEY hndeploy" + +# Build Docker image +docker build \ + -f ci/build/Dockerfile \ + --build-arg NODE_VERSION=16 \ + --build-arg PHP_VERSION="${PHP_VERSION:-8.1}" \ + -t hndeploy \ + . + +# Copy application from remote to local +$HN /data/web/magento2/bin/magento app:config:dump scopes themes +echo "Waiting for SSH to be available on the Hypernode container" +mkdir /tmp/m2build +mkdir -p "$HOME/.ssh" +cp ci/test/magento/deploy_ephemeral.php /tmp/m2build/deploy.php +rsync -a -e "ssh -o StrictHostKeyChecking=no" app@hndeployintegr8.hypernode.io:magento2/ /tmp/m2build +rm /tmp/m2build/app/etc/env.php + +# Build application +$DP hypernode-deploy build -f /web/deploy.php --verbose + +# Prepare env +$HN mkdir -p /data/web/apps/banaan.store/shared/app/etc/ +$HN cp /data/web/magento2/app/etc/env.php /data/web/apps/banaan.store/shared/app/etc/env.php + +########################################## +# DEPLOY WITHOUT PLATFORM CONFIGURATIONS # +# This should pass, but not generate any # +# Nginx/Supervisor/etc configs # +########################################## +# SSH from deploy container to hypernode container +$DP hypernode-deploy deploy test -f /web/deploy.php -v diff --git a/ci/test/run-general.sh b/ci/test/run-general.sh new file mode 100755 index 0000000..b699fdb --- /dev/null +++ b/ci/test/run-general.sh @@ -0,0 +1,163 @@ +#!/usr/bin/env bash + +set -e +set -x + +export PHP_VERSION_SHORT=$(echo "${PHP_VERSION:-8.1}" | sed 's/\.//') + +# Handy aliases +HN="docker-compose exec -T hypernode" +DP="docker-compose exec -T deploy" +DP1="docker-compose exec --workdir=/web1 -T deploy" +DP2="docker-compose exec --workdir=/web2 -T deploy" + +function install_magento() { + $HN mysql -e "DROP DATABASE IF EXISTS dummytag_preinstalled_magento" + $HN mysql -e "CREATE DATABASE dummytag_preinstalled_magento" + local pw=$($HN bash -c "grep password /data/web/.my.cnf | cut -d' ' -f3") + + # Strip carriage return of pw and saves it in a new variable + pw=$(echo $pw | tr -d '\r') + + $HN bash -c "/data/web/magento2/bin/magento setup:install \ + --base-url=http://banaan1.store \ + --db-host=mysqlmaster.dummytag.hypernode.io \ + --db-name=dummytag_preinstalled_magento --db-user=app \ + --db-password=$pw \ + --admin-firstname=admin --admin-lastname=admin \ + --admin-email=admin@admin.com --admin-user=admin \ + --admin-password=admin123 --language=en_US --currency=USD \ + --timezone=America/Chicago --elasticsearch-host=localhost" +} + +# Install docker-compose if it's not installed +if ! [ -x "$(command -v docker-compose)" ]; then + pip install docker-compose +fi + +# Clear up env +trap "docker-compose down -v" EXIT + +docker-compose up -d + +# Create working initial Magento install on the Hypernode container +$HN composer create-project --repository=https://mage-os.hypernode.com/mirror/ magento/project-community-edition /data/web/magento2 +echo "Waiting for MySQL to be available on the Hypernode container" +$HN bash -c "until mysql -e 'select 1' ; do sleep 1; done" +install_magento + +# Copy env to the deploy container +$HN /data/web/magento2/bin/magento app:config:dump scopes themes +echo "Waiting for SSH to be available on the Hypernode container" +chmod 0600 ci/test/.ssh/id_rsa +chmod 0600 ci/test/.ssh/authorized_keys +$DP rsync -a app@hypernode:/data/web/magento2/ /web +$DP rsync -v -a /config/ /web +$DP rm /web/app/etc/env.php + +# Create second app +$DP cp -ra /web /web1 +$DP cp -ra /web /web2 + +# Build both apps +$DP1 hypernode-deploy build -v -f /web1/deploy1.php +$DP2 hypernode-deploy build -v -f /web2/deploy2.php + +# Prepare env +$HN mkdir -p /data/web/apps/banaan1.store/shared/app/etc/ +$HN cp /data/web/magento2/app/etc/env.php /data/web/apps/banaan1.store/shared/app/etc/env.php +$HN mkdir -p /data/web/apps/banaan2.store/shared/app/etc/ +$HN cp /data/web/magento2/app/etc/env.php /data/web/apps/banaan2.store/shared/app/etc/env.php +$HN chown -R app:app /data/web/apps + +########################################## +# DEPLOY WITHOUT PLATFORM CONFIGURATIONS # +# This should pass, but not generate any # +# Nginx/Supervisor/etc configs # +########################################## +# SSH from deploy container to hypernode container +$DP1 hypernode-deploy deploy production -f /web1/deploy1_without_platformconfig.php -v + +# Check if deployment made only one release for store1 +test $($HN ls /data/web/apps/banaan1.store/releases/ | wc -l) = 1 + +# Platform configs shouldn't be present yet +$HN test ! -d /data/web/nginx/banaan1.store +$HN test ! -d /data/web/supervisor/banaan1.store +$HN crontab -l -u app | grep "### BEGIN banaan1.store ###" && exit 1 +$HN test ! -d /data/web/varnish/banaan1.store + +################## +# DEPLOY STORE 2 # +################## +# Store 2 +$DP2 hypernode-deploy deploy production -f /web2/deploy2.php -v + +# Check if deployment made only one release for store2 +test $($HN ls /data/web/apps/banaan2.store/releases/ | wc -l) = 1 +$HN ls -al /data/web/nginx/banaan2.store/ +$HN ls -al /data/web/apps/banaan2.store/current/ +$HN ls -al /data/web/apps/banaan2.store/current/nginx/ +$HN test -f /data/web/nginx/banaan2.store/server.example.conf || ($HN ls -al /data/web/nginx && $HN ls -al /data/web/nginx/banaan2.store && exit 1) +$HN test $($HN readlink -f /data/web/nginx/banaan2.store) = /data/web/apps/banaan2.store/releases/1/nginx + +################################## +# DEPLOY PLATFORM CONFIGURATIONS # +# Now we should get revisions of # +# all platform configs. # +################################## +$DP1 hypernode-deploy deploy production -v -f /web1/deploy1.php + +# Check if example location block was placed +$HN ls -al /data/web/nginx/banaan1.store/ +$HN ls -al /data/web/apps/banaan1.store/current/ +$HN ls -al /data/web/apps/banaan1.store/current/nginx/ +$HN test -f /data/web/nginx/banaan1.store/server.example.conf || ($HN ls -al /data/web/nginx && $HN ls -al /data/web/nginx/banaan1.store && exit 1) +$HN test $($HN readlink -f /data/web/nginx/banaan1.store) = /data/web/apps/banaan1.store/releases/2/nginx + +$HN test -f /data/web/supervisor/banaan1.store/example.conf || ($HN ls -al /data/web/supervisor/ && exit 1) +$HN test $($HN readlink -f /data/web/supervisor/banaan1.store) = /data/web/apps/banaan1.store/releases/2/supervisor + +# Test this once we enable supervisor in the hypernode docker image +# $HN supervisorctl status | grep example | grep -v FATAL || ($HN supervisorctl status && exit 1) + +# Test if varnish dirs exists and vcl has been placed +$HN ls -al /data/web/varnish/banaan1.store/ +$HN ls -al /data/web/apps/banaan1.store/current/varnish/ + +$HN test -f /data/web/varnish/banaan1.store/varnish.vcl || ($HN ls -al /data/web/varnish/ && exit 1) +$HN test $($HN readlink -f /data/web/varnish/banaan1.store/varnish.vcl) = /data/web/apps/banaan1.store/releases/2/varnish/varnish.vcl + +# Check the content of the crontab block +$HN crontab -l -u app | grep "### BEGIN banaan1.store ###" +$HN crontab -l -u app | grep "### END banaan1.store ###" +$HN crontab -l -u app | sed -n -e '/### BEGIN banaan1.store ###/,/### END banaan1.store ###/ p' | grep "banaan" + +###################################### +# REMOVE A NGINX LOCATION # +# Create a new release but make sure # +# that the file is removed in the # +# new release. # +###################################### +# Remove example location +$DP rm /web1/etc/nginx/server.example.conf + +# Deploy again +$DP1 hypernode-deploy deploy production -f /web1/deploy1.php + +# Check if another deployment was made +test $($HN ls /data/web/apps/banaan1.store/releases/ | wc -l) = 3 +$HN test $($HN readlink -f /data/web/nginx/banaan1.store) = /data/web/apps/banaan1.store/releases/3/nginx +$HN test $($HN readlink -f /data/web/supervisor/banaan1.store) = /data/web/apps/banaan1.store/releases/3/supervisor +$HN test $($HN readlink -f /data/web/varnish/banaan1.store/varnish.vcl) = /data/web/apps/banaan1.store/releases/3/varnish/varnish.vcl + +# Verify example location block is removed +$HN test ! -f /data/web/nginx/banaan1.store/server.example.conf || ($HN ls -al /data/web/nginx/banaan1.store && exit 1) + +# Check if the second application is still working as intended +test $($HN ls /data/web/apps/banaan2.store/releases/ | wc -l) = 1 +$HN ls -al /data/web/nginx/banaan2.store/ +$HN ls -al /data/web/apps/banaan2.store/current/ +$HN ls -al /data/web/apps/banaan2.store/current/nginx/ +$HN test -f /data/web/nginx/banaan2.store/server.example.conf || ($HN ls -al /data/web/nginx && $HN ls -al /data/web/nginx/banaan2.store && exit 1) +$HN test $($HN readlink -f /data/web/nginx/banaan2.store) = /data/web/apps/banaan2.store/releases/1/nginx diff --git a/runtests.sh b/runtests.sh index b699fdb..ce7ff37 100755 --- a/runtests.sh +++ b/runtests.sh @@ -1,163 +1,11 @@ #!/usr/bin/env bash -set -e -set -x +ACTION=${1:-general} +RUNNER="ci/test/run-${ACTION}.sh" -export PHP_VERSION_SHORT=$(echo "${PHP_VERSION:-8.1}" | sed 's/\.//') - -# Handy aliases -HN="docker-compose exec -T hypernode" -DP="docker-compose exec -T deploy" -DP1="docker-compose exec --workdir=/web1 -T deploy" -DP2="docker-compose exec --workdir=/web2 -T deploy" - -function install_magento() { - $HN mysql -e "DROP DATABASE IF EXISTS dummytag_preinstalled_magento" - $HN mysql -e "CREATE DATABASE dummytag_preinstalled_magento" - local pw=$($HN bash -c "grep password /data/web/.my.cnf | cut -d' ' -f3") - - # Strip carriage return of pw and saves it in a new variable - pw=$(echo $pw | tr -d '\r') - - $HN bash -c "/data/web/magento2/bin/magento setup:install \ - --base-url=http://banaan1.store \ - --db-host=mysqlmaster.dummytag.hypernode.io \ - --db-name=dummytag_preinstalled_magento --db-user=app \ - --db-password=$pw \ - --admin-firstname=admin --admin-lastname=admin \ - --admin-email=admin@admin.com --admin-user=admin \ - --admin-password=admin123 --language=en_US --currency=USD \ - --timezone=America/Chicago --elasticsearch-host=localhost" -} - -# Install docker-compose if it's not installed -if ! [ -x "$(command -v docker-compose)" ]; then - pip install docker-compose +if [[ ! -f "${RUNNER}" ]]; then + echo "Testsuite runner ${RUNNER} does not exist!" + exit 1 fi -# Clear up env -trap "docker-compose down -v" EXIT - -docker-compose up -d - -# Create working initial Magento install on the Hypernode container -$HN composer create-project --repository=https://mage-os.hypernode.com/mirror/ magento/project-community-edition /data/web/magento2 -echo "Waiting for MySQL to be available on the Hypernode container" -$HN bash -c "until mysql -e 'select 1' ; do sleep 1; done" -install_magento - -# Copy env to the deploy container -$HN /data/web/magento2/bin/magento app:config:dump scopes themes -echo "Waiting for SSH to be available on the Hypernode container" -chmod 0600 ci/test/.ssh/id_rsa -chmod 0600 ci/test/.ssh/authorized_keys -$DP rsync -a app@hypernode:/data/web/magento2/ /web -$DP rsync -v -a /config/ /web -$DP rm /web/app/etc/env.php - -# Create second app -$DP cp -ra /web /web1 -$DP cp -ra /web /web2 - -# Build both apps -$DP1 hypernode-deploy build -v -f /web1/deploy1.php -$DP2 hypernode-deploy build -v -f /web2/deploy2.php - -# Prepare env -$HN mkdir -p /data/web/apps/banaan1.store/shared/app/etc/ -$HN cp /data/web/magento2/app/etc/env.php /data/web/apps/banaan1.store/shared/app/etc/env.php -$HN mkdir -p /data/web/apps/banaan2.store/shared/app/etc/ -$HN cp /data/web/magento2/app/etc/env.php /data/web/apps/banaan2.store/shared/app/etc/env.php -$HN chown -R app:app /data/web/apps - -########################################## -# DEPLOY WITHOUT PLATFORM CONFIGURATIONS # -# This should pass, but not generate any # -# Nginx/Supervisor/etc configs # -########################################## -# SSH from deploy container to hypernode container -$DP1 hypernode-deploy deploy production -f /web1/deploy1_without_platformconfig.php -v - -# Check if deployment made only one release for store1 -test $($HN ls /data/web/apps/banaan1.store/releases/ | wc -l) = 1 - -# Platform configs shouldn't be present yet -$HN test ! -d /data/web/nginx/banaan1.store -$HN test ! -d /data/web/supervisor/banaan1.store -$HN crontab -l -u app | grep "### BEGIN banaan1.store ###" && exit 1 -$HN test ! -d /data/web/varnish/banaan1.store - -################## -# DEPLOY STORE 2 # -################## -# Store 2 -$DP2 hypernode-deploy deploy production -f /web2/deploy2.php -v - -# Check if deployment made only one release for store2 -test $($HN ls /data/web/apps/banaan2.store/releases/ | wc -l) = 1 -$HN ls -al /data/web/nginx/banaan2.store/ -$HN ls -al /data/web/apps/banaan2.store/current/ -$HN ls -al /data/web/apps/banaan2.store/current/nginx/ -$HN test -f /data/web/nginx/banaan2.store/server.example.conf || ($HN ls -al /data/web/nginx && $HN ls -al /data/web/nginx/banaan2.store && exit 1) -$HN test $($HN readlink -f /data/web/nginx/banaan2.store) = /data/web/apps/banaan2.store/releases/1/nginx - -################################## -# DEPLOY PLATFORM CONFIGURATIONS # -# Now we should get revisions of # -# all platform configs. # -################################## -$DP1 hypernode-deploy deploy production -v -f /web1/deploy1.php - -# Check if example location block was placed -$HN ls -al /data/web/nginx/banaan1.store/ -$HN ls -al /data/web/apps/banaan1.store/current/ -$HN ls -al /data/web/apps/banaan1.store/current/nginx/ -$HN test -f /data/web/nginx/banaan1.store/server.example.conf || ($HN ls -al /data/web/nginx && $HN ls -al /data/web/nginx/banaan1.store && exit 1) -$HN test $($HN readlink -f /data/web/nginx/banaan1.store) = /data/web/apps/banaan1.store/releases/2/nginx - -$HN test -f /data/web/supervisor/banaan1.store/example.conf || ($HN ls -al /data/web/supervisor/ && exit 1) -$HN test $($HN readlink -f /data/web/supervisor/banaan1.store) = /data/web/apps/banaan1.store/releases/2/supervisor - -# Test this once we enable supervisor in the hypernode docker image -# $HN supervisorctl status | grep example | grep -v FATAL || ($HN supervisorctl status && exit 1) - -# Test if varnish dirs exists and vcl has been placed -$HN ls -al /data/web/varnish/banaan1.store/ -$HN ls -al /data/web/apps/banaan1.store/current/varnish/ - -$HN test -f /data/web/varnish/banaan1.store/varnish.vcl || ($HN ls -al /data/web/varnish/ && exit 1) -$HN test $($HN readlink -f /data/web/varnish/banaan1.store/varnish.vcl) = /data/web/apps/banaan1.store/releases/2/varnish/varnish.vcl - -# Check the content of the crontab block -$HN crontab -l -u app | grep "### BEGIN banaan1.store ###" -$HN crontab -l -u app | grep "### END banaan1.store ###" -$HN crontab -l -u app | sed -n -e '/### BEGIN banaan1.store ###/,/### END banaan1.store ###/ p' | grep "banaan" - -###################################### -# REMOVE A NGINX LOCATION # -# Create a new release but make sure # -# that the file is removed in the # -# new release. # -###################################### -# Remove example location -$DP rm /web1/etc/nginx/server.example.conf - -# Deploy again -$DP1 hypernode-deploy deploy production -f /web1/deploy1.php - -# Check if another deployment was made -test $($HN ls /data/web/apps/banaan1.store/releases/ | wc -l) = 3 -$HN test $($HN readlink -f /data/web/nginx/banaan1.store) = /data/web/apps/banaan1.store/releases/3/nginx -$HN test $($HN readlink -f /data/web/supervisor/banaan1.store) = /data/web/apps/banaan1.store/releases/3/supervisor -$HN test $($HN readlink -f /data/web/varnish/banaan1.store/varnish.vcl) = /data/web/apps/banaan1.store/releases/3/varnish/varnish.vcl - -# Verify example location block is removed -$HN test ! -f /data/web/nginx/banaan1.store/server.example.conf || ($HN ls -al /data/web/nginx/banaan1.store && exit 1) - -# Check if the second application is still working as intended -test $($HN ls /data/web/apps/banaan2.store/releases/ | wc -l) = 1 -$HN ls -al /data/web/nginx/banaan2.store/ -$HN ls -al /data/web/apps/banaan2.store/current/ -$HN ls -al /data/web/apps/banaan2.store/current/nginx/ -$HN test -f /data/web/nginx/banaan2.store/server.example.conf || ($HN ls -al /data/web/nginx && $HN ls -al /data/web/nginx/banaan2.store && exit 1) -$HN test $($HN readlink -f /data/web/nginx/banaan2.store) = /data/web/apps/banaan2.store/releases/1/nginx +$RUNNER From c96089e9e0b3d759171ee03df9dc2305c717a95a Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Tue, 13 Sep 2022 11:24:23 +0200 Subject: [PATCH 03/22] Allow 404 when querying the ephemeral hypernode logbook --- src/DeployRunner.php | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/DeployRunner.php b/src/DeployRunner.php index 3bb179c..566e4fd 100644 --- a/src/DeployRunner.php +++ b/src/DeployRunner.php @@ -287,11 +287,19 @@ private function waitForEphemeralApp(string $ephemeralApp, int $timeout = 900): $timeElapsed += $now - $latest; $latest = $now; - $flows = $this->hypernodeClient->logbook->getList($ephemeralApp); - $remaining = array_filter($flows, fn (Flow $flow) => !$flow->isComplete()); - if ($flows && !$remaining) { - $resolved = true; - break; + try { + $flows = $this->hypernodeClient->logbook->getList($ephemeralApp); + $remaining = array_filter($flows, fn (Flow $flow) => !$flow->isComplete()); + if ($flows && !$remaining) { + $resolved = true; + break; + } + } catch (HypernodeApiClientException $e) { + // A 404 not found means there are no flows in the logbook yet, we should wait. + // Otherwise, there's an error, and it should be propagated. + if ($e->getCode() !== 404) { + throw $e; + } } sleep(5); From 4ee2f383d10416c298f336b3e15e56633a15c045 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Tue, 13 Sep 2022 14:14:17 +0200 Subject: [PATCH 04/22] Cleanup ephemeral hypernodes after running --- src/Command/Build.php | 2 +- src/Command/Deploy.php | 2 +- src/DeployRunner.php | 57 +++++++++++++++++++++++------------------- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/Command/Build.php b/src/Command/Build.php index 2251c19..f3dfb87 100644 --- a/src/Command/Build.php +++ b/src/Command/Build.php @@ -33,7 +33,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $this->deployRunner->run($output, 'build', 'build'); + $this->deployRunner->run($output, 'build', DeployRunner::TASK_BUILD); return 0; } } diff --git a/src/Command/Deploy.php b/src/Command/Deploy.php index 767b49b..44b2edf 100644 --- a/src/Command/Deploy.php +++ b/src/Command/Deploy.php @@ -35,7 +35,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $this->deployRunner->run($output, $input->getArgument('stage'), 'deploy'); + $this->deployRunner->run($output, $input->getArgument('stage'), DeployRunner::TASK_DEPLOY); return 0; } } diff --git a/src/DeployRunner.php b/src/DeployRunner.php index 566e4fd..03c59d9 100644 --- a/src/DeployRunner.php +++ b/src/DeployRunner.php @@ -37,27 +37,21 @@ class DeployRunner { - /** - * @var TaskFactory - */ - private $taskFactory; - - /** - * @var InputInterface - */ - private $input; + public const TASK_BUILD = 'build'; + public const TASK_DEPLOY = 'deploy'; - /** - * @var LoggerInterface - */ - private $log; + private TaskFactory $taskFactory; + private InputInterface $input; + private LoggerInterface $log; + private RecipeLoader $recipeLoader; + private HypernodeClient $hypernodeClient; /** - * @var RecipeLoader + * Registered ephemeral Hypernodes to stop/cancel after running. + * + * @var string[] */ - private $recipeLoader; - - private HypernodeClient $hypernodeClient; + private array $ephemeralHypernodesRegistered = []; public function __construct( TaskFactory $taskFactory, @@ -79,7 +73,7 @@ public function __construct( * * @return void */ - public function run(OutputInterface $output, string $stage, string $task = 'deploy') + public function run(OutputInterface $output, string $stage, string $task = self::TASK_DEPLOY) { $console = new Application(); $deployer = new Deployer($console); @@ -93,7 +87,7 @@ public function run(OutputInterface $output, string $stage, string $task = 'depl ); try { - $this->initializeDeployer($deployer); + $this->initializeDeployer($deployer, $task); } catch (InvalidConfigurationException $e) { $output->write($e->getMessage()); return; @@ -109,13 +103,13 @@ public function run(OutputInterface $output, string $stage, string $task = 'depl * @throws Throwable * @throws InvalidConfigurationException */ - private function initializeDeployer(Deployer $deployer): void + private function initializeDeployer(Deployer $deployer, string $task): void { $this->recipeLoader->load('common.php'); $tasks = $this->taskFactory->loadAll(); $config = $this->getConfiguration($deployer); $config->setLogger($this->log); - $this->configureStages($config); + $this->configureStages($config, $task); foreach ($tasks as $task) { $task->configure($config); @@ -185,13 +179,17 @@ private function getConfiguration(Deployer $deployer): Configuration } } - private function configureStages(Configuration $config): void + private function configureStages(Configuration $config, string $task): void { - $this->initializeBuildStage($config); + if ($task === self::TASK_BUILD) { + $this->initializeBuildStage($config); + } - foreach ($config->getStages() as $stage) { - foreach ($stage->getServers() as $server) { - $this->configureStageServer($stage, $server, $config); + if ($task === self::TASK_DEPLOY) { + foreach ($config->getStages() as $stage) { + foreach ($stage->getServers() as $server) { + $this->configureStageServer($stage, $server, $config); + } } } } @@ -262,7 +260,9 @@ private function maybeConfigureEphemeralServer(Server $server): void $this->log->info(sprintf('Creating an ephemeral Hypernode based on %s.', $parentApp)); $ephemeralApp = $this->hypernodeClient->ephemeralApp->create($parentApp); $server->setHostname(sprintf("%s.hypernode.io", $ephemeralApp)); + $this->ephemeralHypernodesRegistered[] = $ephemeralApp; $this->log->info(sprintf('Successfully requested ephemeral Hypernode, name is %s.', $ephemeralApp)); + $this->log->info('Waiting for ephemeral Hypernode to become available...'); $this->waitForEphemeralApp($ephemeralApp); $this->log->info('Ephemeral Hypernode has become available!'); @@ -376,6 +376,11 @@ private function runStage(Deployer $deployer, string $stage, string $task = 'dep $executor->run($tasks, $hosts); } throw $exception; + } finally { + foreach ($this->ephemeralHypernodesRegistered as $ephemeralHypernode) { + $this->log->info(sprintf('Stopping ephemeral Hypernode %s...', $ephemeralHypernode)); + $this->hypernodeClient->ephemeralApp->cancel($ephemeralHypernode); + } } } From 7f35ed5333ad39ff5764347694207568fd631e45 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Tue, 13 Sep 2022 14:25:10 +0200 Subject: [PATCH 05/22] Add support for openssh private keys --- src/Deployer/Task/Common/PrepareSshTaskGlobal.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Deployer/Task/Common/PrepareSshTaskGlobal.php b/src/Deployer/Task/Common/PrepareSshTaskGlobal.php index 9d2e954..5f39d5d 100644 --- a/src/Deployer/Task/Common/PrepareSshTaskGlobal.php +++ b/src/Deployer/Task/Common/PrepareSshTaskGlobal.php @@ -70,7 +70,7 @@ private function configureKey(): void return; } - if (strpos($key, 'BEGIN RSA PRIVATE KEY') === false) { + if (!preg_match('/BEGIN (OPENSSH|RSA) PRIVATE KEY/', $key)) { runLocally('echo "$SSH_PRIVATE_KEY" | base64 -d > {{ssh_key_file}}'); } else { runLocally('echo "$SSH_PRIVATE_KEY" > {{ssh_key_file}}'); From b9c7ff5aa7c8103c0981de5e2184296db462bc4c Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Tue, 13 Sep 2022 14:28:04 +0200 Subject: [PATCH 06/22] Only cancel ephemeral hypernode on failure --- src/DeployRunner.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/DeployRunner.php b/src/DeployRunner.php index 03c59d9..8419281 100644 --- a/src/DeployRunner.php +++ b/src/DeployRunner.php @@ -352,6 +352,7 @@ private function runStage(Deployer $deployer, string $stage, string $task = 'dep $tasks = $deployer->scriptManager->getTasks($task); $executor = $deployer->master; + $failed = false; try { /** @@ -363,6 +364,7 @@ private function runStage(Deployer $deployer, string $stage, string $task = 'dep } catch (Throwable $exception) { $deployer->output->writeln('[' . \get_class($exception) . '] ' . $exception->getMessage()); $deployer->output->writeln($exception->getTraceAsString()); + $failed = true; if ($exception instanceof GracefulShutdownException) { throw $exception; @@ -377,9 +379,11 @@ private function runStage(Deployer $deployer, string $stage, string $task = 'dep } throw $exception; } finally { - foreach ($this->ephemeralHypernodesRegistered as $ephemeralHypernode) { - $this->log->info(sprintf('Stopping ephemeral Hypernode %s...', $ephemeralHypernode)); - $this->hypernodeClient->ephemeralApp->cancel($ephemeralHypernode); + if ($failed) { + foreach ($this->ephemeralHypernodesRegistered as $ephemeralHypernode) { + $this->log->info(sprintf('Stopping ephemeral Hypernode %s...', $ephemeralHypernode)); + $this->hypernodeClient->ephemeralApp->cancel($ephemeralHypernode); + } } } } From b913e28d0f0a19b07053f4a812f9aabb15c949f6 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Tue, 13 Sep 2022 14:38:17 +0200 Subject: [PATCH 07/22] Set working directory for ephemeral testsuite --- ci/test/run-ephemeral.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/test/run-ephemeral.sh b/ci/test/run-ephemeral.sh index b601fdc..3219e76 100755 --- a/ci/test/run-ephemeral.sh +++ b/ci/test/run-ephemeral.sh @@ -5,7 +5,7 @@ set -x # Handy aliases HN="ssh app@hndeployintegr8.hypernode.io -o StrictHostKeyChecking=no" -DP="docker run -v /tmp/m2build:/web -e HYPERNODE_API_TOKEN -e SSH_PRIVATE_KEY hndeploy" +DP="docker run -v /tmp/m2build:/web -e HYPERNODE_API_TOKEN -e SSH_PRIVATE_KEY -w /web hndeploy" # Build Docker image docker build \ From c3b64a521f607518665dcdabacef95b42f6dee23 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Wed, 14 Sep 2022 08:16:59 +0200 Subject: [PATCH 08/22] Resolve waiting for ephemeral app only on relevant flows --- src/DeployRunner.php | 13 +++++++++++-- .../CreateEphemeralHypernodeFailedException.php | 11 +++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 src/Exception/CreateEphemeralHypernodeFailedException.php diff --git a/src/DeployRunner.php b/src/DeployRunner.php index 8419281..0d692b7 100644 --- a/src/DeployRunner.php +++ b/src/DeployRunner.php @@ -12,6 +12,7 @@ use Hypernode\Api\Resource\Logbook\Flow; use Hypernode\Deploy\Console\Output\OutputWatcher; use Hypernode\Deploy\Deployer\RecipeLoader; +use Hypernode\Deploy\Exception\CreateEphemeralHypernodeFailedException; use Hypernode\Deploy\Exception\InvalidConfigurationException; use Hypernode\Deploy\Deployer\Task\ConfigurableTaskInterface; use Hypernode\Deploy\Deployer\Task\TaskFactory; @@ -275,6 +276,7 @@ private function maybeConfigureEphemeralServer(Server $server): void * @throws HypernodeApiClientException * @throws HypernodeApiServerException * @throws TimeoutException + * @throws CreateEphemeralHypernodeFailedException */ private function waitForEphemeralApp(string $ephemeralApp, int $timeout = 900): void { @@ -289,8 +291,15 @@ private function waitForEphemeralApp(string $ephemeralApp, int $timeout = 900): try { $flows = $this->hypernodeClient->logbook->getList($ephemeralApp); - $remaining = array_filter($flows, fn (Flow $flow) => !$flow->isComplete()); - if ($flows && !$remaining) { + $relevantFlows = array_filter($flows, fn (Flow $flow) => $flow->name === 'ensure_app'); + $failedFlows = array_filter($flows, fn (Flow $flow) => $flow->isReverted()); + $completedFlows = array_filter($flows, fn (Flow $flow) => $flow->isComplete()); + + if (count($failedFlows) === count($relevantFlows)) { + throw new CreateEphemeralHypernodeFailedException(); + } + + if ($relevantFlows && count($completedFlows) === count($relevantFlows)) { $resolved = true; break; } diff --git a/src/Exception/CreateEphemeralHypernodeFailedException.php b/src/Exception/CreateEphemeralHypernodeFailedException.php new file mode 100644 index 0000000..dd068b0 --- /dev/null +++ b/src/Exception/CreateEphemeralHypernodeFailedException.php @@ -0,0 +1,11 @@ + Date: Wed, 14 Sep 2022 08:43:30 +0200 Subject: [PATCH 09/22] Fix always exiting with code 0 --- src/Command/Build.php | 3 +- src/Command/ComposerAuth.php | 3 +- src/Command/Deploy.php | 3 +- src/Command/RunTask.php | 3 +- src/DeployRunner.php | 72 ++++++++++++++++++------------------ 5 files changed, 41 insertions(+), 43 deletions(-) diff --git a/src/Command/Build.php b/src/Command/Build.php index f3dfb87..f979b19 100644 --- a/src/Command/Build.php +++ b/src/Command/Build.php @@ -33,7 +33,6 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $this->deployRunner->run($output, 'build', DeployRunner::TASK_BUILD); - return 0; + return $this->deployRunner->run($output, 'build', DeployRunner::TASK_BUILD); } } diff --git a/src/Command/ComposerAuth.php b/src/Command/ComposerAuth.php index e1cc1dd..6b5ea07 100644 --- a/src/Command/ComposerAuth.php +++ b/src/Command/ComposerAuth.php @@ -34,7 +34,6 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $this->deployRunner->run($output, 'build', 'deploy:vendors:auth'); - return 0; + return $this->deployRunner->run($output, 'build', 'deploy:vendors:auth'); } } diff --git a/src/Command/Deploy.php b/src/Command/Deploy.php index 44b2edf..366c70e 100644 --- a/src/Command/Deploy.php +++ b/src/Command/Deploy.php @@ -35,7 +35,6 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $this->deployRunner->run($output, $input->getArgument('stage'), DeployRunner::TASK_DEPLOY); - return 0; + return $this->deployRunner->run($output, $input->getArgument('stage'), DeployRunner::TASK_DEPLOY); } } diff --git a/src/Command/RunTask.php b/src/Command/RunTask.php index a25616f..e7e42d7 100644 --- a/src/Command/RunTask.php +++ b/src/Command/RunTask.php @@ -36,7 +36,6 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $this->deployRunner->run($output, $input->getArgument('stage'), $input->getArgument('task')); - return 0; + return $this->deployRunner->run($output, $input->getArgument('stage'), $input->getArgument('task')); } } diff --git a/src/DeployRunner.php b/src/DeployRunner.php index 0d692b7..22c745b 100644 --- a/src/DeployRunner.php +++ b/src/DeployRunner.php @@ -72,9 +72,9 @@ public function __construct( * @throws Throwable * @throws Exception * - * @return void + * @return int */ - public function run(OutputInterface $output, string $stage, string $task = self::TASK_DEPLOY) + public function run(OutputInterface $output, string $stage, string $task = self::TASK_DEPLOY): int { $console = new Application(); $deployer = new Deployer($console); @@ -91,9 +91,10 @@ public function run(OutputInterface $output, string $stage, string $task = self: $this->initializeDeployer($deployer, $task); } catch (InvalidConfigurationException $e) { $output->write($e->getMessage()); - return; + return 1; } - $this->runStage($deployer, $stage, $task); + + return $this->runStage($deployer, $stage, $task); } /** @@ -265,7 +266,12 @@ private function maybeConfigureEphemeralServer(Server $server): void $this->log->info(sprintf('Successfully requested ephemeral Hypernode, name is %s.', $ephemeralApp)); $this->log->info('Waiting for ephemeral Hypernode to become available...'); - $this->waitForEphemeralApp($ephemeralApp); + try { + $this->waitForEphemeralApp($ephemeralApp); + } catch (CreateEphemeralHypernodeFailedException | TimeoutException $e) { + $this->hypernodeClient->ephemeralApp->cancel($ephemeralApp); + throw $e; + } $this->log->info('Ephemeral Hypernode has become available!'); } } @@ -352,7 +358,7 @@ private function initializeBuildStage(Configuration $config): void * @throws Throwable * @throws Exception */ - private function runStage(Deployer $deployer, string $stage, string $task = 'deploy'): void + private function runStage(Deployer $deployer, string $stage, string $task = 'deploy'): int { $hosts = $deployer->selector->select("stage=$stage"); if (empty($hosts)) { @@ -361,40 +367,36 @@ private function runStage(Deployer $deployer, string $stage, string $task = 'dep $tasks = $deployer->scriptManager->getTasks($task); $executor = $deployer->master; - $failed = false; - try { - /** - * Set the env variable to tell deployer to deploy the hosts sequentially instead of parallel. - * @see \Deployer\Executor\Master::runTask() - */ - putenv('DEPLOYER_LOCAL_WORKER=true'); - $executor->run($tasks, $hosts); - } catch (Throwable $exception) { - $deployer->output->writeln('[' . \get_class($exception) . '] ' . $exception->getMessage()); - $deployer->output->writeln($exception->getTraceAsString()); - $failed = true; + /** + * Set the env variable to tell deployer to deploy the hosts sequentially instead of parallel. + * @see \Deployer\Executor\Master::runTask() + */ + putenv('DEPLOYER_LOCAL_WORKER=true'); + $exitCode = $executor->run($tasks, $hosts); - if ($exception instanceof GracefulShutdownException) { - throw $exception; - } + if ($exitCode === 0) { + return 0; + } + + if ($exitCode === GracefulShutdownException::EXIT_CODE) { + return 1; + } - // Check if we have tasks to execute on failure - if ($deployer['fail']->has($task)) { - $taskName = $deployer['fail']->get($task); - $tasks = $deployer->scriptManager->getTasks($taskName); + // Check if we have tasks to execute on failure + if ($deployer['fail']->has($task)) { + $taskName = $deployer['fail']->get($task); + $tasks = $deployer->scriptManager->getTasks($taskName); - $executor->run($tasks, $hosts); - } - throw $exception; - } finally { - if ($failed) { - foreach ($this->ephemeralHypernodesRegistered as $ephemeralHypernode) { - $this->log->info(sprintf('Stopping ephemeral Hypernode %s...', $ephemeralHypernode)); - $this->hypernodeClient->ephemeralApp->cancel($ephemeralHypernode); - } - } + $executor->run($tasks, $hosts); + } + + foreach ($this->ephemeralHypernodesRegistered as $ephemeralHypernode) { + $this->log->info(sprintf('Stopping ephemeral Hypernode %s...', $ephemeralHypernode)); + $this->hypernodeClient->ephemeralApp->cancel($ephemeralHypernode); } + + return $exitCode; } private function tryGetConfiguration(): Configuration From 9ed9ad4802ea85485d208b3159652f9f00f95ee7 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Wed, 14 Sep 2022 09:22:13 +0200 Subject: [PATCH 10/22] Update hypernode/api-client --- composer.lock | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/composer.lock b/composer.lock index 343fdb1..fcecbf6 100644 --- a/composer.lock +++ b/composer.lock @@ -733,12 +733,12 @@ "source": { "type": "git", "url": "https://github.com/ByteInternet/hypernode-api-php.git", - "reference": "309a671f5c9bd2bbc4fee8566daf5d8e48d1c01f" + "reference": "0a40102d119f5678447b6e0682d939fcb077a622" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ByteInternet/hypernode-api-php/zipball/309a671f5c9bd2bbc4fee8566daf5d8e48d1c01f", - "reference": "309a671f5c9bd2bbc4fee8566daf5d8e48d1c01f", + "url": "https://api.github.com/repos/ByteInternet/hypernode-api-php/zipball/0a40102d119f5678447b6e0682d939fcb077a622", + "reference": "0a40102d119f5678447b6e0682d939fcb077a622", "shasum": "" }, "require": { @@ -777,7 +777,7 @@ "issues": "https://github.com/ByteInternet/hypernode-api-php/issues", "source": "https://github.com/ByteInternet/hypernode-api-php/tree/master" }, - "time": "2022-09-12T13:18:22+00:00" + "time": "2022-09-14T07:21:34+00:00" }, { "name": "hypernode/deploy-configuration", @@ -6026,12 +6026,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "6d260392fad173d6ee6e3a93c875d9327db1109b" + "reference": "5cf4af16905d08d88c26fbcdf929a8f53e0780da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/6d260392fad173d6ee6e3a93c875d9327db1109b", - "reference": "6d260392fad173d6ee6e3a93c875d9327db1109b", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/5cf4af16905d08d88c26fbcdf929a8f53e0780da", + "reference": "5cf4af16905d08d88c26fbcdf929a8f53e0780da", "shasum": "" }, "conflict": { @@ -6434,11 +6434,12 @@ "tribalsystems/zenario": "<9.2.55826", "truckersmp/phpwhois": "<=4.3.1", "twig/twig": "<1.38|>=2,<2.14.11|>=3,<3.3.8", - "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.38|>=9,<9.5.29|>=10,<10.4.29|>=11,<11.5.11", + "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.38|>=9,<9.5.29|>=10,<10.4.32|>=11,<11.5.16", "typo3/cms-backend": ">=7,<=7.6.50|>=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1", - "typo3/cms-core": ">=6.2,<=6.2.56|>=7,<7.6.57|>=8,<8.7.47|>=9,<9.5.35|>=10,<10.4.29|>=11,<11.5.11", + "typo3/cms-core": ">=6.2,<=6.2.56|>=7,<7.6.57|>=8,<8.7.47|>=9,<9.5.35|>=10,<10.4.32|>=11,<11.5.16", "typo3/cms-form": ">=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1", "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", + "typo3/html-sanitizer": ">=1,<1.0.7|>=2,<2.0.16", "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.3.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<3.3.23|>=4,<4.0.17|>=4.1,<4.1.16|>=4.2,<4.2.12|>=4.3,<4.3.3", "typo3/phar-stream-wrapper": ">=1,<2.1.1|>=3,<3.1.1", "typo3/swiftmailer": ">=4.1,<4.1.99|>=5.4,<5.4.5", @@ -6536,7 +6537,7 @@ "type": "tidelift" } ], - "time": "2022-08-31T22:04:18+00:00" + "time": "2022-09-13T13:19:06+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -6595,16 +6596,16 @@ }, { "name": "sebastian/comparator", - "version": "3.0.3", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" + "reference": "c05dd1878ec74058a28a569c59fc5c53a8cc1c7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/c05dd1878ec74058a28a569c59fc5c53a8cc1c7a", + "reference": "c05dd1878ec74058a28a569c59fc5c53a8cc1c7a", "shasum": "" }, "require": { @@ -6657,7 +6658,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.4" }, "funding": [ { @@ -6665,7 +6666,7 @@ "type": "github" } ], - "time": "2020-11-30T08:04:30+00:00" + "time": "2022-09-14T06:27:54+00:00" }, { "name": "sebastian/diff", @@ -6798,16 +6799,16 @@ }, { "name": "sebastian/exporter", - "version": "3.1.4", + "version": "3.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db" + "reference": "73a9676f2833b9a7c36968f9d882589cd75511e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", - "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/73a9676f2833b9a7c36968f9d882589cd75511e6", + "reference": "73a9676f2833b9a7c36968f9d882589cd75511e6", "shasum": "" }, "require": { @@ -6863,7 +6864,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.4" + "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.5" }, "funding": [ { @@ -6871,7 +6872,7 @@ "type": "github" } ], - "time": "2021-11-11T13:51:24+00:00" + "time": "2022-09-14T06:00:17+00:00" }, { "name": "sebastian/global-state", From b26a349a92147cf2f6ac7c22a995024e21e66c5a Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Wed, 14 Sep 2022 10:09:55 +0200 Subject: [PATCH 11/22] ci/ephemeral: remove obsolete env.php copy from source hypernode --- ci/test/run-ephemeral.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ci/test/run-ephemeral.sh b/ci/test/run-ephemeral.sh index 3219e76..e61521e 100755 --- a/ci/test/run-ephemeral.sh +++ b/ci/test/run-ephemeral.sh @@ -17,7 +17,6 @@ docker build \ # Copy application from remote to local $HN /data/web/magento2/bin/magento app:config:dump scopes themes -echo "Waiting for SSH to be available on the Hypernode container" mkdir /tmp/m2build mkdir -p "$HOME/.ssh" cp ci/test/magento/deploy_ephemeral.php /tmp/m2build/deploy.php @@ -27,10 +26,6 @@ rm /tmp/m2build/app/etc/env.php # Build application $DP hypernode-deploy build -f /web/deploy.php --verbose -# Prepare env -$HN mkdir -p /data/web/apps/banaan.store/shared/app/etc/ -$HN cp /data/web/magento2/app/etc/env.php /data/web/apps/banaan.store/shared/app/etc/env.php - ########################################## # DEPLOY WITHOUT PLATFORM CONFIGURATIONS # # This should pass, but not generate any # From 82ec23f50157ede1e8b6356f95629ee198b4edcd Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Wed, 14 Sep 2022 12:12:40 +0200 Subject: [PATCH 12/22] Add deployment reporting --- src/Command/Deploy.php | 13 ++- src/DeployRunner.php | 98 ++++++------------ src/Ephemeral/EphemeralHypernodeManager.php | 106 ++++++++++++++++++++ src/Report/Report.php | 81 +++++++++++++++ src/Report/ReportLoader.php | 22 ++++ src/Report/ReportWriter.php | 13 +++ 6 files changed, 263 insertions(+), 70 deletions(-) create mode 100644 src/Ephemeral/EphemeralHypernodeManager.php create mode 100644 src/Report/Report.php create mode 100644 src/Report/ReportLoader.php create mode 100644 src/Report/ReportWriter.php diff --git a/src/Command/Deploy.php b/src/Command/Deploy.php index 366c70e..e816d47 100644 --- a/src/Command/Deploy.php +++ b/src/Command/Deploy.php @@ -3,6 +3,7 @@ namespace Hypernode\Deploy\Command; use Hypernode\Deploy\DeployRunner; +use Hypernode\Deploy\Report\ReportWriter; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -15,11 +16,13 @@ class Deploy extends Command * @var DeployRunner */ private $deployRunner; + private ReportWriter $reportWriter; - public function __construct(DeployRunner $deployRunner) + public function __construct(DeployRunner $deployRunner, ReportWriter $reportWriter) { parent::__construct(); $this->deployRunner = $deployRunner; + $this->reportWriter = $reportWriter; } protected function configure() @@ -35,6 +38,12 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - return $this->deployRunner->run($output, $input->getArgument('stage'), DeployRunner::TASK_DEPLOY); + $result = $this->deployRunner->run($output, $input->getArgument('stage'), DeployRunner::TASK_DEPLOY); + + if ($result === 0) { + $this->reportWriter->write($this->deployRunner->getDeploymentReport()); + } + + return $result; } } diff --git a/src/DeployRunner.php b/src/DeployRunner.php index bdbe7a4..f7e5363 100644 --- a/src/DeployRunner.php +++ b/src/DeployRunner.php @@ -5,13 +5,10 @@ use Deployer\Deployer; use Deployer\Exception\Exception; use Deployer\Exception\GracefulShutdownException; -use Hypernode\Api\Exception\HypernodeApiClientException; -use Hypernode\Api\Exception\HypernodeApiServerException; -use Hypernode\Api\HypernodeClient; -use Hypernode\Api\HypernodeClientFactory; -use Hypernode\Api\Resource\Logbook\Flow; +use Deployer\Host\Host; use Hypernode\Deploy\Console\Output\OutputWatcher; use Hypernode\Deploy\Deployer\RecipeLoader; +use Hypernode\Deploy\Ephemeral\EphemeralHypernodeManager; use Hypernode\Deploy\Exception\CreateEphemeralHypernodeFailedException; use Hypernode\Deploy\Exception\InvalidConfigurationException; use Hypernode\Deploy\Deployer\Task\ConfigurableTaskInterface; @@ -45,7 +42,7 @@ class DeployRunner private InputInterface $input; private LoggerInterface $log; private RecipeLoader $recipeLoader; - private HypernodeClient $hypernodeClient; + private EphemeralHypernodeManager $ephemeralHypernodeManager; /** * Registered ephemeral Hypernodes to stop/cancel after running. @@ -54,17 +51,24 @@ class DeployRunner */ private array $ephemeralHypernodesRegistered = []; + private string $version; + private array $deployedHostnames = []; + private string $deployedStage = ''; + public function __construct( TaskFactory $taskFactory, InputInterface $input, LoggerInterface $log, - RecipeLoader $recipeLoader + RecipeLoader $recipeLoader, + EphemeralHypernodeManager $ephemeralHypernodeManager, + string $version ) { $this->taskFactory = $taskFactory; $this->input = $input; $this->log = $log; $this->recipeLoader = $recipeLoader; - $this->hypernodeClient = HypernodeClientFactory::create(getenv('HYPERNODE_API_TOKEN') ?: ''); + $this->ephemeralHypernodeManager = $ephemeralHypernodeManager; + $this->version = $version; } /** @@ -258,70 +262,19 @@ private function maybeConfigureEphemeralServer(Server $server): void $parentApp = $serverOptions[Server::OPTION_HN_PARENT_APP] ?? null; if ($isEphemeral && $parentApp) { $this->log->info(sprintf('Creating an ephemeral Hypernode based on %s.', $parentApp)); - $ephemeralApp = $this->hypernodeClient->ephemeralApp->create($parentApp); + $ephemeralApp = $this->ephemeralHypernodeManager->createForHypernode($parentApp); $server->setHostname(sprintf("%s.hypernode.io", $ephemeralApp)); $this->ephemeralHypernodesRegistered[] = $ephemeralApp; $this->log->info(sprintf('Successfully requested ephemeral Hypernode, name is %s.', $ephemeralApp)); - $this->log->info('Waiting for ephemeral Hypernode to become available...'); try { - $this->waitForEphemeralApp($ephemeralApp); + $this->log->info('Waiting for ephemeral Hypernode to become available...'); + $this->ephemeralHypernodeManager->waitForAvailability($ephemeralApp); + $this->log->info('Ephemeral Hypernode has become available!'); } catch (CreateEphemeralHypernodeFailedException | TimeoutException $e) { - $this->hypernodeClient->ephemeralApp->cancel($ephemeralApp); + $this->ephemeralHypernodeManager->cancel($ephemeralApp); throw $e; } - $this->log->info('Ephemeral Hypernode has become available!'); - } - } - - /** - * Poll and wait for ephemeral app to become available. - * - * @throws HypernodeApiClientException - * @throws HypernodeApiServerException - * @throws TimeoutException - * @throws CreateEphemeralHypernodeFailedException - */ - private function waitForEphemeralApp(string $ephemeralApp, int $timeout = 900): void - { - $latest = microtime(true); - $timeElapsed = 0; - $resolved = false; - - while ($timeElapsed < $timeout && !$resolved) { - $now = microtime(true); - $timeElapsed += $now - $latest; - $latest = $now; - - try { - $flows = $this->hypernodeClient->logbook->getList($ephemeralApp); - $relevantFlows = array_filter($flows, fn (Flow $flow) => $flow->name === 'ensure_app'); - $failedFlows = array_filter($flows, fn (Flow $flow) => $flow->isReverted()); - $completedFlows = array_filter($flows, fn (Flow $flow) => $flow->isComplete()); - - if (count($failedFlows) === count($relevantFlows)) { - throw new CreateEphemeralHypernodeFailedException(); - } - - if ($relevantFlows && count($completedFlows) === count($relevantFlows)) { - $resolved = true; - break; - } - } catch (HypernodeApiClientException $e) { - // A 404 not found means there are no flows in the logbook yet, we should wait. - // Otherwise, there's an error, and it should be propagated. - if ($e->getCode() !== 404) { - throw $e; - } - } - - sleep(5); - } - - if (!$resolved) { - throw new TimeoutException( - sprintf('Timed out waiting for ephemeral Hypernode %s to become available', $ephemeralApp) - ); } } @@ -374,6 +327,8 @@ private function runStage(Deployer $deployer, string $stage, string $task = 'dep $exitCode = $executor->run($tasks, $hosts); if ($exitCode === 0) { + $this->deployedHostnames = array_map(fn (Host $host) => $host->getHostname(), $hosts); + $this->deployedStage = $stage; return 0; } @@ -389,10 +344,7 @@ private function runStage(Deployer $deployer, string $stage, string $task = 'dep $executor->run($tasks, $hosts); } - foreach ($this->ephemeralHypernodesRegistered as $ephemeralHypernode) { - $this->log->info(sprintf('Stopping ephemeral Hypernode %s...', $ephemeralHypernode)); - $this->hypernodeClient->ephemeralApp->cancel($ephemeralHypernode); - } + $this->ephemeralHypernodeManager->cancel(...$this->ephemeralHypernodesRegistered); return $exitCode; } @@ -455,4 +407,14 @@ private function initializeAppAutoloader(): void require_once WORKING_DIR . '/vendor/autoload.php'; } } + + public function getDeploymentReport() + { + return new Report\Report( + $this->version, + $this->deployedStage, + $this->deployedHostnames, + $this->ephemeralHypernodesRegistered, + ); + } } diff --git a/src/Ephemeral/EphemeralHypernodeManager.php b/src/Ephemeral/EphemeralHypernodeManager.php new file mode 100644 index 0000000..2767d53 --- /dev/null +++ b/src/Ephemeral/EphemeralHypernodeManager.php @@ -0,0 +1,106 @@ +log = $log; + $this->hypernodeClient = HypernodeClientFactory::create(getenv('HYPERNODE_API_TOKEN') ?: ''); + } + + /** + * Create ephemeral Hypernode instance for given Hypernode. + * + * @param string $hypernode Name of the Hypernode + * @return string Name of the created ephemeral Hypernode + * @throws HypernodeApiClientException + * @throws HypernodeApiServerException + */ + public function createForHypernode(string $hypernode): string + { + return $this->hypernodeClient->ephemeralApp->create($hypernode); + } + + /** + * Wait for ephemeral Hypernode to become available. + * + * @param string $ephemeralHypernode Name of the ephemeral Hypernode + * @param int $timeout Maximum time to wait for availability + * @return void + * @throws CreateEphemeralHypernodeFailedException + * @throws HypernodeApiClientException + * @throws HypernodeApiServerException + * @throws TimeoutException + */ + public function waitForAvailability(string $ephemeralHypernode, int $timeout = 900): void + { + $latest = microtime(true); + $timeElapsed = 0; + $resolved = false; + + while ($timeElapsed < $timeout && !$resolved) { + $now = microtime(true); + $timeElapsed += $now - $latest; + $latest = $now; + + try { + $flows = $this->hypernodeClient->logbook->getList($ephemeralHypernode); + $relevantFlows = array_filter($flows, fn (Flow $flow) => $flow->name === 'ensure_app'); + $failedFlows = array_filter($flows, fn (Flow $flow) => $flow->isReverted()); + $completedFlows = array_filter($flows, fn (Flow $flow) => $flow->isComplete()); + + if (count($failedFlows) === count($relevantFlows)) { + throw new CreateEphemeralHypernodeFailedException(); + } + + if ($relevantFlows && count($completedFlows) === count($relevantFlows)) { + $resolved = true; + break; + } + } catch (HypernodeApiClientException $e) { + // A 404 not found means there are no flows in the logbook yet, we should wait. + // Otherwise, there's an error, and it should be propagated. + if ($e->getCode() !== 404) { + throw $e; + } + } + + sleep(5); + } + + if (!$resolved) { + throw new TimeoutException( + sprintf('Timed out waiting for ephemeral Hypernode %s to become available', $ephemeralHypernode) + ); + } + } + + /** + * Cancel one or multiple ephemeral Hypernodes. + * + * @param string ...$ephemeralHypernodes Name(s) of the ephemeral Hypernode(s) + * @throws HypernodeApiClientException + * @throws HypernodeApiServerException + */ + public function cancel(string ...$ephemeralHypernodes): void + { + foreach ($ephemeralHypernodes as $ephemeralHypernode) { + $this->log->info(sprintf('Stopping ephemeral Hypernode %s...', $ephemeralHypernode)); + $this->hypernodeClient->ephemeralApp->cancel($ephemeralHypernode); + } + } +} diff --git a/src/Report/Report.php b/src/Report/Report.php new file mode 100644 index 0000000..7fb222b --- /dev/null +++ b/src/Report/Report.php @@ -0,0 +1,81 @@ +version = $version; + $this->stage = $stage; + $this->hostnames = $hostnames; + $this->ephemeralHypernodes = $ephemeralHypernodes; + } + + public function getVersion(): string + { + return $this->version; + } + + public function getStage(): string + { + return $this->stage; + } + + /** + * @return string[] + */ + public function getHostnames(): array + { + return $this->hostnames; + } + + /** + * @return string[] + */ + public function getEphemeralHypernodes(): array + { + return $this->ephemeralHypernodes; + } + + public function toArray(): array + { + return [ + 'version' => $this->version, + 'stage' => $this->stage, + 'hostnames' => $this->hostnames, + 'ephemeral_hypernodes' => $this->ephemeralHypernodes, + ]; + } + + public static function fromArray(array $data): Report + { + return new Report( + $data['version'], + $data['stage'], + $data['hostnames'], + $data['ephemeral_hypernodes'], + ); + } +} diff --git a/src/Report/ReportLoader.php b/src/Report/ReportLoader.php new file mode 100644 index 0000000..b4b6e26 --- /dev/null +++ b/src/Report/ReportLoader.php @@ -0,0 +1,22 @@ +toArray())); + } +} From 1743ad731b3c250d6d2728663ffd4872acd7f2d7 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Wed, 14 Sep 2022 12:13:10 +0200 Subject: [PATCH 13/22] Add Cleanup command for cancelling ephemeral Hypernodes --- src/Command/Cleanup.php | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/Command/Cleanup.php diff --git a/src/Command/Cleanup.php b/src/Command/Cleanup.php new file mode 100644 index 0000000..d71e9bf --- /dev/null +++ b/src/Command/Cleanup.php @@ -0,0 +1,43 @@ +reportLoader = $reportLoader; + $this->ephemeralHypernodeManager = $ephemeralHypernodeManager; + } + + /** + * @throws Throwable + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $report = $this->reportLoader->loadReport(); + + if ($report === null) { + $output->writeln('No report found, skipping cleanup.'); + return 0; + } + + $this->ephemeralHypernodeManager->cancel(...$report->getEphemeralHypernodes()); + + return 0; + } +} From 38abf49bdad53c638a9ae9c00ea9113217d90b53 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Wed, 14 Sep 2022 12:13:45 +0200 Subject: [PATCH 14/22] ci/ephemeral: test for deployment report --- ci/build/Dockerfile | 1 + ci/test/run-ephemeral.sh | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ci/build/Dockerfile b/ci/build/Dockerfile index 77be092..518e671 100644 --- a/ci/build/Dockerfile +++ b/ci/build/Dockerfile @@ -37,6 +37,7 @@ RUN apt-get update && \ git \ patch \ bash \ + jq \ ca-certificates \ wget \ curl \ diff --git a/ci/test/run-ephemeral.sh b/ci/test/run-ephemeral.sh index e61521e..a3710e1 100755 --- a/ci/test/run-ephemeral.sh +++ b/ci/test/run-ephemeral.sh @@ -5,7 +5,7 @@ set -x # Handy aliases HN="ssh app@hndeployintegr8.hypernode.io -o StrictHostKeyChecking=no" -DP="docker run -v /tmp/m2build:/web -e HYPERNODE_API_TOKEN -e SSH_PRIVATE_KEY -w /web hndeploy" +DP="docker run --rm -v /tmp/m2build:/web -e HYPERNODE_API_TOKEN -e SSH_PRIVATE_KEY -w /web hndeploy" # Build Docker image docker build \ @@ -33,3 +33,11 @@ $DP hypernode-deploy build -f /web/deploy.php --verbose ########################################## # SSH from deploy container to hypernode container $DP hypernode-deploy deploy test -f /web/deploy.php -v + +# Run some tests + +$DP "ls -l" +$DP "test -f deployment-report.json" +$DP "cat deployment-report.json | jq" + +$DP hypernode-deploy cleanup From 5c4f7c721f919ea449a7a5ca959524066f26b2ef Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Wed, 14 Sep 2022 13:14:58 +0200 Subject: [PATCH 15/22] Remove usage version DI variable for report version --- src/DeployRunner.php | 8 ++------ src/Report/Report.php | 17 +++++++++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/DeployRunner.php b/src/DeployRunner.php index f7e5363..fb1d68d 100644 --- a/src/DeployRunner.php +++ b/src/DeployRunner.php @@ -51,7 +51,6 @@ class DeployRunner */ private array $ephemeralHypernodesRegistered = []; - private string $version; private array $deployedHostnames = []; private string $deployedStage = ''; @@ -60,15 +59,13 @@ public function __construct( InputInterface $input, LoggerInterface $log, RecipeLoader $recipeLoader, - EphemeralHypernodeManager $ephemeralHypernodeManager, - string $version + EphemeralHypernodeManager $ephemeralHypernodeManager ) { $this->taskFactory = $taskFactory; $this->input = $input; $this->log = $log; $this->recipeLoader = $recipeLoader; $this->ephemeralHypernodeManager = $ephemeralHypernodeManager; - $this->version = $version; } /** @@ -411,10 +408,9 @@ private function initializeAppAutoloader(): void public function getDeploymentReport() { return new Report\Report( - $this->version, $this->deployedStage, $this->deployedHostnames, - $this->ephemeralHypernodesRegistered, + $this->ephemeralHypernodesRegistered ); } } diff --git a/src/Report/Report.php b/src/Report/Report.php index 7fb222b..b79c838 100644 --- a/src/Report/Report.php +++ b/src/Report/Report.php @@ -7,8 +7,8 @@ class Report { public const REPORT_FILENAME = 'deployment-report.json'; + public const REPORT_VERSION = 'v1'; - private string $version; private string $stage; /** * @var string[] @@ -18,19 +18,24 @@ class Report * @var string[] */ private array $ephemeralHypernodes; + private string $version; /** - * @param string $version * @param string $stage * @param string[] $hostnames * @param string[] $ephemeralHypernodes + * @param string $version Version of the report file */ - public function __construct(string $version, string $stage, array $hostnames, array $ephemeralHypernodes) - { - $this->version = $version; + public function __construct( + string $stage, + array $hostnames, + array $ephemeralHypernodes, + string $version = self::REPORT_VERSION + ) { $this->stage = $stage; $this->hostnames = $hostnames; $this->ephemeralHypernodes = $ephemeralHypernodes; + $this->version = $version; } public function getVersion(): string @@ -72,10 +77,10 @@ public function toArray(): array public static function fromArray(array $data): Report { return new Report( - $data['version'], $data['stage'], $data['hostnames'], $data['ephemeral_hypernodes'], + $data['version'], ); } } From f6bd6021017752de786bf6657086c81da3bdded3 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Wed, 14 Sep 2022 14:18:58 +0200 Subject: [PATCH 16/22] Add missing name and description for Cleanup command --- src/Command/Cleanup.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Command/Cleanup.php b/src/Command/Cleanup.php index d71e9bf..e9608d5 100644 --- a/src/Command/Cleanup.php +++ b/src/Command/Cleanup.php @@ -24,6 +24,15 @@ public function __construct(ReportLoader $reportLoader, EphemeralHypernodeManage $this->ephemeralHypernodeManager = $ephemeralHypernodeManager; } + protected function configure() + { + parent::configure(); + $this->setName('cleanup'); + $this->setDescription( + 'Clean up any acquired resources during the deployment, like ephemeral Hypernodes.' + ); + } + /** * @throws Throwable */ From 7d82205634c53c5c35137684c1f32ee726a93789 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Wed, 14 Sep 2022 14:44:45 +0200 Subject: [PATCH 17/22] ci/ephemeral: fix deployment report tests --- ci/test/run-ephemeral.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/test/run-ephemeral.sh b/ci/test/run-ephemeral.sh index a3710e1..b6926cd 100755 --- a/ci/test/run-ephemeral.sh +++ b/ci/test/run-ephemeral.sh @@ -36,8 +36,8 @@ $DP hypernode-deploy deploy test -f /web/deploy.php -v # Run some tests -$DP "ls -l" -$DP "test -f deployment-report.json" -$DP "cat deployment-report.json | jq" +$DP ls -l +$DP test -f deployment-report.json +$DP jq deployment-report.json $DP hypernode-deploy cleanup From 6ebb15bcb39f6591a21318ab61a79b2471569523 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Wed, 14 Sep 2022 15:13:09 +0200 Subject: [PATCH 18/22] ci/ephemeral: verbose and output deployment-report.json --- ci/test/run-ephemeral.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ci/test/run-ephemeral.sh b/ci/test/run-ephemeral.sh index b6926cd..0778f48 100755 --- a/ci/test/run-ephemeral.sh +++ b/ci/test/run-ephemeral.sh @@ -24,7 +24,7 @@ rsync -a -e "ssh -o StrictHostKeyChecking=no" app@hndeployintegr8.hypernode.io:m rm /tmp/m2build/app/etc/env.php # Build application -$DP hypernode-deploy build -f /web/deploy.php --verbose +$DP hypernode-deploy build -f /web/deploy.php -vvv ########################################## # DEPLOY WITHOUT PLATFORM CONFIGURATIONS # @@ -32,12 +32,13 @@ $DP hypernode-deploy build -f /web/deploy.php --verbose # Nginx/Supervisor/etc configs # ########################################## # SSH from deploy container to hypernode container -$DP hypernode-deploy deploy test -f /web/deploy.php -v +$DP hypernode-deploy deploy test -f /web/deploy.php -vvv # Run some tests $DP ls -l $DP test -f deployment-report.json +$DP cat deployment-report.json $DP jq deployment-report.json -$DP hypernode-deploy cleanup +$DP hypernode-deploy cleanup -vvv From 088379929313dbbc54b988f1f315fab9a014be21 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Wed, 14 Sep 2022 15:30:46 +0200 Subject: [PATCH 19/22] ci/ephemeral: fix deployment report tests --- ci/test/run-ephemeral.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ci/test/run-ephemeral.sh b/ci/test/run-ephemeral.sh index 0778f48..7a1b00f 100755 --- a/ci/test/run-ephemeral.sh +++ b/ci/test/run-ephemeral.sh @@ -38,7 +38,10 @@ $DP hypernode-deploy deploy test -f /web/deploy.php -vvv $DP ls -l $DP test -f deployment-report.json -$DP cat deployment-report.json -$DP jq deployment-report.json +$DP jq . deployment-report.json +$DP jq .version deployment-report.json -r +$DP jq .stage deployment-report.json -r +$DP jq .hostnames[0] deployment-report.json -r +$DP jq .ephemeral_hypernodes[0] deployment-report.json -r $DP hypernode-deploy cleanup -vvv From 2aac9001f0d559c4ec0468c3646956e74a9f5b84 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Mon, 19 Sep 2022 08:51:56 +0200 Subject: [PATCH 20/22] composer: set hypernode/deploy-configuration to v3.x-dev --- composer.json | 2 +- composer.lock | 56 +++++++++++++++++++++++++++------------------------ 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/composer.json b/composer.json index 284c6b5..59e7c49 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "doctrine/annotations": "^1.6", "guzzlehttp/guzzle": "^7.5", "hypernode/api-client": "dev-master", - "hypernode/deploy-configuration": "dev-ephemeral_servers", + "hypernode/deploy-configuration": "v3.x-dev", "php-di/php-di": "^6.0", "psr/log": "^1.0", "symfony/console": "^5.4", diff --git a/composer.lock b/composer.lock index fcecbf6..6f5c4b7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "68a8c995edb3ae6b3d40dfa09a4d1639", + "content-hash": "c2050586df39ec657cd256f09544ea60", "packages": [ { "name": "clue/stream-filter", @@ -781,16 +781,16 @@ }, { "name": "hypernode/deploy-configuration", - "version": "dev-ephemeral_servers", + "version": "v3.x-dev", "source": { "type": "git", "url": "https://github.com/ByteInternet/hypernode-deploy-configuration.git", - "reference": "bbb62227a92205b9f31548442c27ee27321debe5" + "reference": "39ba384b779da41e3f7c6e12056c6a2bdb9b62b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ByteInternet/hypernode-deploy-configuration/zipball/bbb62227a92205b9f31548442c27ee27321debe5", - "reference": "bbb62227a92205b9f31548442c27ee27321debe5", + "url": "https://api.github.com/repos/ByteInternet/hypernode-deploy-configuration/zipball/39ba384b779da41e3f7c6e12056c6a2bdb9b62b4", + "reference": "39ba384b779da41e3f7c6e12056c6a2bdb9b62b4", "shasum": "" }, "require": { @@ -817,9 +817,9 @@ "description": "Hypernode deploy configuration files", "support": { "issues": "https://github.com/ByteInternet/hypernode-deploy-configuration/issues", - "source": "https://github.com/ByteInternet/hypernode-deploy-configuration/tree/ephemeral_servers" + "source": "https://github.com/ByteInternet/hypernode-deploy-configuration/tree/v3" }, - "time": "2022-09-12T10:17:41+00:00" + "time": "2022-09-16T13:49:04+00:00" }, { "name": "justinrainbow/json-schema", @@ -6026,12 +6026,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "5cf4af16905d08d88c26fbcdf929a8f53e0780da" + "reference": "264a5085afd6e127daba3f47751fa971d3c29c3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/5cf4af16905d08d88c26fbcdf929a8f53e0780da", - "reference": "5cf4af16905d08d88c26fbcdf929a8f53e0780da", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/264a5085afd6e127daba3f47751fa971d3c29c3d", + "reference": "264a5085afd6e127daba3f47751fa971d3c29c3d", "shasum": "" }, "conflict": { @@ -6124,6 +6124,8 @@ "enshrined/svg-sanitize": "<0.15", "erusev/parsedown": "<1.7.2", "ether/logs": "<3.0.4", + "exceedone/exment": "<4.4.3|>=5,<5.0.3", + "exceedone/laravel-admin": "= 3.0.0|<2.2.3", "ezsystems/demobundle": ">=5.4,<5.4.6.1", "ezsystems/ez-support-tools": ">=2.2,<2.2.3", "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1", @@ -6156,13 +6158,13 @@ "fooman/tcpdf": "<6.2.22", "forkcms/forkcms": "<5.11.1", "fossar/tcpdf-parser": "<6.2.22", - "francoisjacquet/rosariosis": "<9.1", + "francoisjacquet/rosariosis": "<10.1", "friendsofsymfony/oauth2-php": "<1.3", "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", "friendsoftypo3/mediace": ">=7.6.2,<7.6.5", "froala/wysiwyg-editor": "<3.2.7", - "froxlor/froxlor": "<=0.10.22", + "froxlor/froxlor": "<0.10.38", "fuel/core": "<1.8.1", "gaoming13/wechat-php-sdk": "<=1.10.2", "genix/cms": "<=1.1.11", @@ -6306,7 +6308,7 @@ "phpwhois/phpwhois": "<=4.2.5", "phpxmlrpc/extras": "<0.6.1", "pimcore/data-hub": "<1.2.4", - "pimcore/pimcore": "<10.4.4", + "pimcore/pimcore": "<10.5.6", "pocketmine/bedrock-protocol": "<8.0.2", "pocketmine/pocketmine-mp": "<4.7.2|>= 4.0.0-BETA5, < 4.4.2", "pressbooks/pressbooks": "<5.18", @@ -6327,6 +6329,8 @@ "pusher/pusher-php-server": "<2.2.1", "pwweb/laravel-core": "<=0.3.6-beta", "rainlab/debugbar-plugin": "<3.1", + "rankmath/seo-by-rank-math": "<=1.0.95", + "react/http": ">=0.7,<1.7", "remdex/livehelperchat": "<3.99", "rmccue/requests": ">=1.6,<1.8", "robrichards/xmlseclibs": "<3.0.4", @@ -6341,7 +6345,7 @@ "shopware/core": "<=6.4.9", "shopware/platform": "<=6.4.9", "shopware/production": "<=6.3.5.2", - "shopware/shopware": "<=5.7.13", + "shopware/shopware": "<=5.7.14", "shopware/storefront": "<=6.4.8.1", "shopxo/shopxo": "<2.2.6", "showdoc/showdoc": "<2.10.4", @@ -6365,8 +6369,8 @@ "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", "simplito/elliptic-php": "<1.0.6", "slim/slim": "<2.6", - "smarty/smarty": "<3.1.45|>=4,<4.1.1", - "snipe/snipe-it": "<6.0.10|>= 6.0.0-RC-1, <= 6.0.0-RC-5", + "smarty/smarty": "<3.1.47|>=4,<4.2.1", + "snipe/snipe-it": "<6.0.11|>= 6.0.0-RC-1, <= 6.0.0-RC-5", "socalnick/scn-social-auth": "<1.15.2", "socialiteproviders/steam": "<1.1", "spipu/html2pdf": "<5.2.4", @@ -6428,7 +6432,7 @@ "thinkcmf/thinkcmf": "<=5.1.7", "tinymce/tinymce": "<5.10", "titon/framework": ">=0,<9.9.99", - "topthink/framework": "<=6.0.12", + "topthink/framework": "<=6.0.13", "topthink/think": "<=6.0.9", "topthink/thinkphp": "<=3.2.3", "tribalsystems/zenario": "<9.2.55826", @@ -6436,7 +6440,7 @@ "twig/twig": "<1.38|>=2,<2.14.11|>=3,<3.3.8", "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.38|>=9,<9.5.29|>=10,<10.4.32|>=11,<11.5.16", "typo3/cms-backend": ">=7,<=7.6.50|>=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1", - "typo3/cms-core": ">=6.2,<=6.2.56|>=7,<7.6.57|>=8,<8.7.47|>=9,<9.5.35|>=10,<10.4.32|>=11,<11.5.16", + "typo3/cms-core": ">=6.2,<=6.2.56|>=7,<7.6.58|>=8,<8.7.48|>=9,<9.5.37|>=10,<10.4.32|>=11,<11.5.16", "typo3/cms-form": ">=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1", "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", "typo3/html-sanitizer": ">=1,<1.0.7|>=2,<2.0.16", @@ -6445,7 +6449,7 @@ "typo3/swiftmailer": ">=4.1,<4.1.99|>=5.4,<5.4.5", "typo3fluid/fluid": ">=2,<2.0.8|>=2.1,<2.1.7|>=2.2,<2.2.4|>=2.3,<2.3.7|>=2.4,<2.4.4|>=2.5,<2.5.11|>=2.6,<2.6.10", "ua-parser/uap-php": "<3.8", - "unisharp/laravel-filemanager": "<=2.3", + "unisharp/laravel-filemanager": "<=2.5.1", "userfrosting/userfrosting": ">=0.3.1,<4.6.3", "usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2", "vanilla/safecurl": "<0.9.2", @@ -6537,7 +6541,7 @@ "type": "tidelift" } ], - "time": "2022-09-13T13:19:06+00:00" + "time": "2022-09-16T22:04:31+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -6596,16 +6600,16 @@ }, { "name": "sebastian/comparator", - "version": "3.0.4", + "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "c05dd1878ec74058a28a569c59fc5c53a8cc1c7a" + "reference": "1dc7ceb4a24aede938c7af2a9ed1de09609ca770" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/c05dd1878ec74058a28a569c59fc5c53a8cc1c7a", - "reference": "c05dd1878ec74058a28a569c59fc5c53a8cc1c7a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dc7ceb4a24aede938c7af2a9ed1de09609ca770", + "reference": "1dc7ceb4a24aede938c7af2a9ed1de09609ca770", "shasum": "" }, "require": { @@ -6658,7 +6662,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.4" + "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.5" }, "funding": [ { @@ -6666,7 +6670,7 @@ "type": "github" } ], - "time": "2022-09-14T06:27:54+00:00" + "time": "2022-09-14T12:31:48+00:00" }, { "name": "sebastian/diff", From b1c4a056f3ef7d2443a3edb413fbb0d235ba9d36 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Mon, 19 Sep 2022 08:58:37 +0200 Subject: [PATCH 21/22] DeployRunner: fix imports after configurable interface refactorings --- src/DeployRunner.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DeployRunner.php b/src/DeployRunner.php index fb1d68d..6314532 100644 --- a/src/DeployRunner.php +++ b/src/DeployRunner.php @@ -8,17 +8,17 @@ use Deployer\Host\Host; use Hypernode\Deploy\Console\Output\OutputWatcher; use Hypernode\Deploy\Deployer\RecipeLoader; +use Hypernode\Deploy\Deployer\Task\ConfigurableTaskInterface; +use Hypernode\Deploy\Deployer\Task\TaskFactory; use Hypernode\Deploy\Ephemeral\EphemeralHypernodeManager; use Hypernode\Deploy\Exception\CreateEphemeralHypernodeFailedException; use Hypernode\Deploy\Exception\InvalidConfigurationException; -use Hypernode\Deploy\Deployer\Task\ConfigurableTaskInterface; -use Hypernode\Deploy\Deployer\Task\TaskFactory; use Hypernode\Deploy\Exception\TimeoutException; +use Hypernode\DeployConfiguration\Configurable\ServerRoleConfigurableInterface; +use Hypernode\DeployConfiguration\Configurable\StageConfigurableInterface; use Hypernode\DeployConfiguration\Configuration; use Hypernode\DeployConfiguration\Server; -use Hypernode\DeployConfiguration\ServerRoleConfigurableInterface; use Hypernode\DeployConfiguration\Stage; -use Hypernode\DeployConfiguration\StageConfigurableInterface; use Psr\Log\LoggerInterface; use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\ArrayInput; From 3f7e33b3cd8b4d32a56f6802aeeb6c7aa4b5b960 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Mon, 19 Sep 2022 09:00:43 +0200 Subject: [PATCH 22/22] TaskBuilder: fix imports after configurable interface refactorings --- src/Deployer/TaskBuilder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Deployer/TaskBuilder.php b/src/Deployer/TaskBuilder.php index 2b9b384..83cc2db 100644 --- a/src/Deployer/TaskBuilder.php +++ b/src/Deployer/TaskBuilder.php @@ -8,8 +8,8 @@ use Deployer\Task\Task; use Hypernode\DeployConfiguration\Command\Command; use Hypernode\DeployConfiguration\Command\DeployCommand; -use Hypernode\DeployConfiguration\ServerRoleConfigurableInterface; -use Hypernode\DeployConfiguration\StageConfigurableInterface; +use Hypernode\DeployConfiguration\Configurable\ServerRoleConfigurableInterface; +use Hypernode\DeployConfiguration\Configurable\StageConfigurableInterface; use Hypernode\DeployConfiguration\TaskConfigurationInterface; use function Deployer\parse;