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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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; From 225af9233eeb3cbe1ff2408edd27f82f754f267a Mon Sep 17 00:00:00 2001 From: Alexander Grooff Date: Mon, 19 Sep 2022 17:52:32 +0200 Subject: [PATCH 23/25] feat: allow for errors in initial window The API call to fetch playbooks tends to crap out in the beginning, so this initial window of allowing commits is to give it some room. --- src/Ephemeral/EphemeralHypernodeManager.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Ephemeral/EphemeralHypernodeManager.php b/src/Ephemeral/EphemeralHypernodeManager.php index 2767d53..f466d10 100644 --- a/src/Ephemeral/EphemeralHypernodeManager.php +++ b/src/Ephemeral/EphemeralHypernodeManager.php @@ -51,6 +51,8 @@ public function waitForAvailability(string $ephemeralHypernode, int $timeout = 9 $latest = microtime(true); $timeElapsed = 0; $resolved = false; + $interval = 3; + $allowedErrorWindow = 3; while ($timeElapsed < $timeout && !$resolved) { $now = microtime(true); @@ -76,10 +78,19 @@ public function waitForAvailability(string $ephemeralHypernode, int $timeout = 9 // Otherwise, there's an error, and it should be propagated. if ($e->getCode() !== 404) { throw $e; + } elseif ($timeElapsed < $allowedErrorWindow) { + // Some times we get an error where the logbook is not yet available, but it will be soon. + // We allow a small window for this to happen, and then we throw an exception. + sprintf( + 'Got an excepted exception during the allowed error window of HTTP code %d, waiting for %s to become available', + $e->getCode(), + $ephemeralHypernode + ); + continue; } } - sleep(5); + sleep($interval); } if (!$resolved) { From 5a9e5903725c025ab3cd2dfdc6f9579e06ced3f5 Mon Sep 17 00:00:00 2001 From: Alexander Grooff Date: Tue, 20 Sep 2022 10:08:10 +0200 Subject: [PATCH 24/25] typo: excepted -> expected --- src/Ephemeral/EphemeralHypernodeManager.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ephemeral/EphemeralHypernodeManager.php b/src/Ephemeral/EphemeralHypernodeManager.php index f466d10..7690f53 100644 --- a/src/Ephemeral/EphemeralHypernodeManager.php +++ b/src/Ephemeral/EphemeralHypernodeManager.php @@ -79,10 +79,10 @@ public function waitForAvailability(string $ephemeralHypernode, int $timeout = 9 if ($e->getCode() !== 404) { throw $e; } elseif ($timeElapsed < $allowedErrorWindow) { - // Some times we get an error where the logbook is not yet available, but it will be soon. + // Sometimes we get an error where the logbook is not yet available, but it will be soon. // We allow a small window for this to happen, and then we throw an exception. sprintf( - 'Got an excepted exception during the allowed error window of HTTP code %d, waiting for %s to become available', + 'Got an expected exception during the allowed error window of HTTP code %d, waiting for %s to become available', $e->getCode(), $ephemeralHypernode ); From fd77a20ecdea4b626a3056ef6b45eaa08ce34a99 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Tue, 20 Sep 2022 14:11:05 +0200 Subject: [PATCH 25/25] ci: rename pubkey --- ci/{key.pub => hypernode-insecure-deploy-ci-key.pub} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ci/{key.pub => hypernode-insecure-deploy-ci-key.pub} (100%) diff --git a/ci/key.pub b/ci/hypernode-insecure-deploy-ci-key.pub similarity index 100% rename from ci/key.pub rename to ci/hypernode-insecure-deploy-ci-key.pub