diff --git a/.atoum.php b/.atoum.php new file mode 100644 index 0000000..5465a6d --- /dev/null +++ b/.atoum.php @@ -0,0 +1,13 @@ +addDefaultReport(); +// This will add a green or red logo after each run depending on its status. +$report->addField(new atoum\report\fields\runner\result\logo()); +$runner->addTestsFromDirectory(__DIR__.'/tests/Units'); diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..87d322a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +Makefile +README.md +LICENCE +Dockerfile \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d41ad65 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ + +###> friendsofphp/php-cs-fixer ### +.php_cs +.php_cs.cache +###< friendsofphp/php-cs-fixer ### diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..09eb3ef --- /dev/null +++ b/.travis.yml @@ -0,0 +1,59 @@ +language: c + +sudo: required +dist: trusty + +env: + global: + - DOCKER_VERSION=17.10.0~ce-0~ubuntu + - DOCKER_EXPERIMENTAL=true + +before_install: + - | + docker_setup () { + if [ ! -z "$DOCKER_EXPERIMENTAL" ]; then + printf '{"experimental":true}\n' | sudo tee /etc/docker/daemon.json + fi + INSTALLED_DOCKER_VERSION=$(docker version --format '{{.Server.Version}}' | sed -e 's/-ce$//') + SIMPLIFIED_DOCKER_VERSION=$(echo $DOCKER_VERSION | sed -e 's/~ce.*//') + if [ "$INSTALLED_DOCKER_VERSION" != "$SIMPLIFIED_DOCKER_VERSION" ] ; then + apt-cache madison docker-ce + sudo apt-get -o Dpkg::Options::="--force-confnew" install -y docker-ce=${DOCKER_VERSION} + else + sudo service docker restart + fi + } + export -f docker_setup + - docker_setup + - docker version + +install: + - make pull + - make build + +script: + - make qa + - make test + +deploy: + provider: script + script: >- + docker login --username="${DOCKER_HUB_USERNAME}" + --password="${DOCKER_HUB_PASSWORD}" && make push + skip_cleanup: true + 'on': + branch: master + +after_success: + - docker ps -a --format "table {{.Names}}\t{{.Image}}\t{{.Status}}" + +after_failure: + - docker ps -a --format "table {{.Names}}\t{{.Image}}\t{{.Status}}" + +notifications: + slack: verylastroom:yqs3UqNXq6H3L9p8npeVFWgh + email: + recipients: + - ci@karibbu.com + on_success: change + on_failure: change diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1181a9e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +FROM karibbu/php-docker:7.1.11-stable as latest + +USER php + +ENV PATH="$PATH:/home/php/app/bin" \ + APP_ENV="prod" \ + APP_DEBUG="0" + +COPY --chown=php:php composer.json composer.lock symfony.lock ./ +RUN composer install --ansi --no-autoloader --no-dev --no-scripts \ + && composer clear-cache --ansi + +COPY --chown=php:php config ./config +COPY --chown=php:php bin ./bin +COPY --chown=php:php src ./src + +RUN composer dump-autoload --ansi --no-dev --classmap-authoritative + +ENTRYPOINT ["console"] + +FROM latest as dev + +ENV APP_ENV="dev" \ + APP_DEBUG="1" + +RUN composer install --ansi --no-autoloader \ + && composer clear-cache --ansi + +COPY --chown=php:php .*atoum.php .php_cs* ./ +COPY --chown=php:php tests ./tests + +RUN composer dump-autoload --ansi + +ENTRYPOINT ["composer"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8785800 --- /dev/null +++ b/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2013-2017 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..abc8423 --- /dev/null +++ b/Makefile @@ -0,0 +1,50 @@ +CURRENT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) +APP_IMAGE_NAME=karibbu/docker-php +TAGS_TO_PULL?=latest dev + +.PHONY: pull +pull: $(TAGS_TO_PULL) + +.PHONY: $(TAGS_TO_PULL) +$(TAGS_TO_PULL): + docker image pull $(APP_IMAGE_NAME):$@ || echo "can't pull image $(APP_IMAGE_NAME):$@" + +.PHONY: push +push: + @docker image push $(APP_IMAGE_NAME) + +.PHONY: build +build: + @docker build \ + --cache-from $(APP_IMAGE_NAME):latest \ + --target latest \ + --tag $(APP_IMAGE_NAME):latest . + @docker build \ + --cache-from $(APP_IMAGE_NAME):latest \ + --cache-from $(APP_IMAGE_NAME):dev \ + --target dev \ + --tag $(APP_IMAGE_NAME):dev . + +.PHONY: qa +qa: cs static + +.PHONY: test +test: unit + +.PHONY: cs static unit +cs static unit: + $(call run,$@,--env APP_ENV=test) + +.PHONY: shell +shell: + @$(call run,ash,--entrypoint= --tty) + +define run +docker run \ + --rm \ + --interactive \ + --volume $(CURRENT_DIR):/home/php/app \ + --volume /var/run/docker.sock:/var/run/docker.sock \ + $(2) \ + $(APP_IMAGE_NAME):dev $(1) +endef diff --git a/README.md b/README.md new file mode 100644 index 0000000..cb74997 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +docker-php +========== + +docker-php is a docker client writent in php. You can see it as a hack for some currently missing features in the official docker client. + +## Born + +The primary need was to find a workaround about docker stack deploy as at the time of the creation of this project it don't support some `--detach=false` option (see [docker/cli#373](https://github.com/docker/cli/issues/373)), so when it come to run end to end (e2e) tests on your `ci` (or your local machine) with some freshly deployed docker stack... you probably then launch your tests on a not ready stack! + +## Commands + +There's currently only one command: +```shell +docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock karibbu/docker-php:latest stack:converge +``` + +For help and more options: +```shell +docker run --rm -it karibbu/docker-php:latest stack:converge --help +``` + +## Audience + +docker-php is intended for people that want to hack around docker client for missing/not merged features. + +Licensing +========= + +docker-php is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/moby/moby/blob/master/LICENSE) for the full license text. + diff --git a/bin/console b/bin/console new file mode 100755 index 0000000..dcd2059 --- /dev/null +++ b/bin/console @@ -0,0 +1,31 @@ +#!/usr/bin/env php +getParameterOption(['--env', '-e'], $_SERVER['APP_ENV'] ?? 'dev'); +$debug = ($_SERVER['APP_DEBUG'] ?? ('prod' !== $env)) && !$input->hasParameterOption(['--no-debug', '']); + +if ($debug) { + umask(0000); + + if (class_exists(Debug::class)) { + Debug::enable(); + } +} + +$kernel = new Kernel($env, $debug); +$application = new Application($kernel); +$application->run($input); diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..38ab7b1 --- /dev/null +++ b/composer.json @@ -0,0 +1,62 @@ +{ + "type": "project", + "license": "proprietary", + "minimum-stability": "beta", + "require": { + "php": "^7.1.3", + "beberlei/assert": "^2.7", + "symfony/console": "^4.0", + "symfony/flex": "^1.0", + "symfony/framework-bundle": "^4.0", + "symfony/lts": "^4@dev", + "symfony/maker-bundle": "dev-master", + "symfony/process": "^4.0@beta", + "symfony/yaml": "^4.0" + }, + "require-dev": { + "atoum/atoum": "^3.2", + "friendsofphp/php-cs-fixer": "^2.8", + "phpstan/phpstan": "dev-master" + }, + "config": { + "vendor-dir": "/home/php/vendor", + "preferred-install": { + "*": "dist" + }, + "sort-packages": true + }, + "autoload": { + "psr-4": { + "App\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "App\\Tests\\": "tests/" + } + }, + "scripts": { + "auto-scripts": { + "cache:clear": "symfony-cmd" + }, + "post-install-cmd": [ + "@auto-scripts" + ], + "post-update-cmd": [ + "@auto-scripts" + ], + "cs": "php-cs-fixer fix --config=.php_cs -v --dry-run --using-cache=no", + "cs-fix": "php-cs-fixer fix --config=.php_cs -v --using-cache=no", + "static": "phpstan analyse --no-progress -l 4 src", + "unit": "atoum -force-terminal" + }, + "conflict": { + "symfony/symfony": "*" + }, + "extra": { + "symfony": { + "id": "01BZ586340JQWE0N9WGEJRDDQR", + "allow-contrib": false + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..9a19c38 --- /dev/null +++ b/composer.lock @@ -0,0 +1,2815 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "content-hash": "53180285e7e4d96c3435623e837c94d1", + "packages": [ + { + "name": "beberlei/assert", + "version": "v2.7.11", + "source": { + "type": "git", + "url": "https://github.com/beberlei/assert.git", + "reference": "53991547d6f0b8c81354fb2d098dcb71e81678cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/beberlei/assert/zipball/53991547d6f0b8c81354fb2d098dcb71e81678cb", + "reference": "53991547d6f0b8c81354fb2d098dcb71e81678cb", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=5.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.1.1", + "phpunit/phpunit": "^4.8.35|^5.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Assert\\": "lib/Assert" + }, + "files": [ + "lib/Assert/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de", + "role": "Lead Developer" + }, + { + "name": "Richard Quadling", + "email": "rquadling@gmail.com", + "role": "Collaborator" + } + ], + "description": "Thin assertion library for input validation in business models.", + "keywords": [ + "assert", + "assertion", + "validation" + ], + "time": "2017-11-13T18:35:09+00:00" + }, + { + "name": "psr/cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "time": "2016-08-06T20:24:11+00:00" + }, + { + "name": "psr/container", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://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" + ], + "time": "2017-02-14T16:28:37+00:00" + }, + { + "name": "psr/log", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2016-10-10T12:19:37+00:00" + }, + { + "name": "psr/simple-cache", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/753fa598e8f3b9966c886fe13f370baa45ef0e24", + "reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "time": "2017-01-02T13:31:39+00:00" + }, + { + "name": "symfony/cache", + "version": "v4.0.0-BETA4", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache.git", + "reference": "5b2fffcad066341f9168513ed08e56cf95d377c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache/zipball/5b2fffcad066341f9168513ed08e56cf95d377c6", + "reference": "5b2fffcad066341f9168513ed08e56cf95d377c6", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/cache": "~1.0", + "psr/log": "~1.0", + "psr/simple-cache": "^1.0" + }, + "conflict": { + "symfony/var-dumper": "<3.4" + }, + "provide": { + "psr/cache-implementation": "1.0", + "psr/simple-cache-implementation": "1.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/cache": "~1.6", + "doctrine/dbal": "~2.4", + "predis/predis": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Cache\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "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": "Symfony Cache component with PSR-6, PSR-16, and tags", + "homepage": "https://symfony.com", + "keywords": [ + "caching", + "psr6" + ], + "time": "2017-11-10T19:37:45+00:00" + }, + { + "name": "symfony/config", + "version": "v4.0.0-BETA4", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "5737a596f9aecded1dc60ea4096a741f2862bf21" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/5737a596f9aecded1dc60ea4096a741f2862bf21", + "reference": "5737a596f9aecded1dc60ea4096a741f2862bf21", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/filesystem": "~3.4|~4.0" + }, + "conflict": { + "symfony/finder": "<3.4" + }, + "require-dev": { + "symfony/finder": "~3.4|~4.0", + "symfony/yaml": "~3.4|~4.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "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": "Symfony Config Component", + "homepage": "https://symfony.com", + "time": "2017-11-11T15:54:51+00:00" + }, + { + "name": "symfony/console", + "version": "v4.0.0-BETA4", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "481dcfc9c0be0ed91be456ab72960ee8a07168e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/481dcfc9c0be0ed91be456ab72960ee8a07168e2", + "reference": "481dcfc9c0be0ed91be456ab72960ee8a07168e2", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/debug": "~3.4|~4.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.4|~4.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "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": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2017-11-12T16:54:20+00:00" + }, + { + "name": "symfony/debug", + "version": "v4.0.0-BETA4", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "92697a71cd55c5852d0f19cd80e7fb6d1eb12b88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/92697a71cd55c5852d0f19cd80e7fb6d1eb12b88", + "reference": "92697a71cd55c5852d0f19cd80e7fb6d1eb12b88", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": "<3.4" + }, + "require-dev": { + "symfony/http-kernel": "~3.4|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "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": "Symfony Debug Component", + "homepage": "https://symfony.com", + "time": "2017-11-12T16:47:31+00:00" + }, + { + "name": "symfony/dependency-injection", + "version": "v4.0.0-BETA4", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "55d1d0001b0594273bfa62e955b8af0deaf96564" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/55d1d0001b0594273bfa62e955b8af0deaf96564", + "reference": "55d1d0001b0594273bfa62e955b8af0deaf96564", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/container": "^1.0" + }, + "conflict": { + "symfony/config": "<3.4", + "symfony/finder": "<3.4", + "symfony/proxy-manager-bridge": "<3.4", + "symfony/yaml": "<3.4" + }, + "provide": { + "psr/container-implementation": "1.0" + }, + "require-dev": { + "symfony/config": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/yaml": "~3.4|~4.0" + }, + "suggest": { + "symfony/config": "", + "symfony/expression-language": "For using expressions in service container configuration", + "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "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": "Symfony DependencyInjection Component", + "homepage": "https://symfony.com", + "time": "2017-11-12T16:47:31+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v4.0.0-BETA4", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "6223fb2b68e7059e8d5843c0103999a84e7275cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/6223fb2b68e7059e8d5843c0103999a84e7275cf", + "reference": "6223fb2b68e7059e8d5843c0103999a84e7275cf", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "conflict": { + "symfony/dependency-injection": "<3.4" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/stopwatch": "~3.4|~4.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "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": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2017-11-09T17:30:28+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v4.0.0-BETA4", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "1e8e2a3a34c9d1e85ecd3e71c77587417111fb95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/1e8e2a3a34c9d1e85ecd3e71c77587417111fb95", + "reference": "1e8e2a3a34c9d1e85ecd3e71c77587417111fb95", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "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": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2017-11-07T14:45:01+00:00" + }, + { + "name": "symfony/finder", + "version": "v4.0.0-BETA4", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "c9cdda4dc4a3182d8d6daeebce4a25fef078ea4c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/c9cdda4dc4a3182d8d6daeebce4a25fef078ea4c", + "reference": "c9cdda4dc4a3182d8d6daeebce4a25fef078ea4c", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "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": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2017-11-07T14:45:01+00:00" + }, + { + "name": "symfony/flex", + "version": "v1.0.41", + "source": { + "type": "git", + "url": "https://github.com/symfony/flex.git", + "reference": "f3ac6de242d0dd7f0a36abfcc413e6cee1442702" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/flex/zipball/f3ac6de242d0dd7f0a36abfcc413e6cee1442702", + "reference": "f3ac6de242d0dd7f0a36abfcc413e6cee1442702", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1", + "php": "^7.0" + }, + "require-dev": { + "composer/composer": "^1.4", + "symfony/phpunit-bridge": "^3.2.8" + }, + "type": "composer-plugin", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + }, + "class": "Symfony\\Flex\\Flex" + }, + "autoload": { + "psr-4": { + "Symfony\\Flex\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien.potencier@gmail.com" + } + ], + "time": "2017-11-09T19:21:33+00:00" + }, + { + "name": "symfony/framework-bundle", + "version": "v4.0.0-BETA4", + "source": { + "type": "git", + "url": "https://github.com/symfony/framework-bundle.git", + "reference": "2554dd997931860dd487753486901c70c0e981bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/2554dd997931860dd487753486901c70c0e981bb", + "reference": "2554dd997931860dd487753486901c70c0e981bb", + "shasum": "" + }, + "require": { + "ext-xml": "*", + "php": "^7.1.3", + "symfony/cache": "~3.4|~4.0", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4-beta4|~4.0-beta4", + "symfony/filesystem": "~3.4|~4.0", + "symfony/finder": "~3.4|~4.0", + "symfony/http-foundation": "~3.4|~4.0", + "symfony/http-kernel": "~3.4|~4.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/routing": "~3.4|~4.0" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.0", + "phpdocumentor/type-resolver": "<0.2.1", + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", + "symfony/asset": "<3.4", + "symfony/console": "<3.4", + "symfony/form": "<3.4", + "symfony/property-info": "<3.4", + "symfony/serializer": "<3.4", + "symfony/stopwatch": "<3.4", + "symfony/translation": "<3.4", + "symfony/validator": "<3.4", + "symfony/workflow": "<3.4" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "doctrine/cache": "~1.0", + "fig/link-util": "^1.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0", + "symfony/asset": "~3.4|~4.0", + "symfony/browser-kit": "~3.4|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/css-selector": "~3.4|~4.0", + "symfony/dom-crawler": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/form": "~3.4|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/process": "~3.4|~4.0", + "symfony/property-info": "~3.4|~4.0", + "symfony/security": "~3.4|~4.0", + "symfony/security-core": "~3.4|~4.0", + "symfony/security-csrf": "~3.4|~4.0", + "symfony/serializer": "~3.4|~4.0", + "symfony/stopwatch": "~3.4|~4.0", + "symfony/templating": "~3.4|~4.0", + "symfony/translation": "~3.4|~4.0", + "symfony/validator": "~3.4|~4.0", + "symfony/var-dumper": "~3.4|~4.0", + "symfony/web-link": "~3.4|~4.0", + "symfony/workflow": "~3.4|~4.0", + "symfony/yaml": "~3.4|~4.0", + "twig/twig": "~1.34|~2.4" + }, + "suggest": { + "ext-apcu": "For best performance of the system caches", + "symfony/console": "For using the console commands", + "symfony/form": "For using forms", + "symfony/property-info": "For using the property_info service", + "symfony/serializer": "For using the serializer service", + "symfony/validator": "For using validation", + "symfony/web-link": "For using web links, features such as preloading, prefetching or prerendering", + "symfony/yaml": "For using the debug:config and lint:yaml commands" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\FrameworkBundle\\": "" + }, + "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": "Symfony FrameworkBundle", + "homepage": "https://symfony.com", + "time": "2017-11-12T16:47:31+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v4.0.0-BETA4", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "58c81dabc0b873800a6aeb6d7fed92b7ade28c8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/58c81dabc0b873800a6aeb6d7fed92b7ade28c8a", + "reference": "58c81dabc0b873800a6aeb6d7fed92b7ade28c8a", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.1" + }, + "require-dev": { + "symfony/expression-language": "~3.4|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "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": "Symfony HttpFoundation Component", + "homepage": "https://symfony.com", + "time": "2017-11-10T19:37:45+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v4.0.0-BETA4", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "9a9e4d013f7a871a2695577350b077ac959e479c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/9a9e4d013f7a871a2695577350b077ac959e479c", + "reference": "9a9e4d013f7a871a2695577350b077ac959e479c", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/log": "~1.0", + "symfony/debug": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/http-foundation": "~3.4|~4.0" + }, + "conflict": { + "symfony/config": "<3.4", + "symfony/dependency-injection": "<3.4", + "symfony/var-dumper": "<3.4", + "twig/twig": "<1.34|<2.4,>=2" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/cache": "~1.0", + "symfony/browser-kit": "~3.4|~4.0", + "symfony/config": "~3.4|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/css-selector": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/dom-crawler": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/finder": "~3.4|~4.0", + "symfony/process": "~3.4|~4.0", + "symfony/routing": "~3.4|~4.0", + "symfony/stopwatch": "~3.4|~4.0", + "symfony/templating": "~3.4|~4.0", + "symfony/translation": "~3.4|~4.0", + "symfony/var-dumper": "~3.4|~4.0" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "", + "symfony/var-dumper": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "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": "Symfony HttpKernel Component", + "homepage": "https://symfony.com", + "time": "2017-11-12T18:08:08+00:00" + }, + { + "name": "symfony/lts", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/lts.git", + "reference": "396c5fca8d73d01186df37d7031321a3c0c2bf92" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/lts/zipball/396c5fca8d73d01186df37d7031321a3c0c2bf92", + "reference": "396c5fca8d73d01186df37d7031321a3c0c2bf92", + "shasum": "" + }, + "conflict": { + "symfony/asset": ">=5", + "symfony/browser-kit": ">=5", + "symfony/cache": ">=5", + "symfony/class-loader": ">=5", + "symfony/config": ">=5", + "symfony/console": ">=5", + "symfony/css-selector": ">=5", + "symfony/debug": ">=5", + "symfony/debug-bundle": ">=5", + "symfony/dependency-injection": ">=5", + "symfony/doctrine-bridge": ">=5", + "symfony/dom-crawler": ">=5", + "symfony/dotenv": ">=5", + "symfony/event-dispatcher": ">=5", + "symfony/expression-language": ">=5", + "symfony/filesystem": ">=5", + "symfony/finder": ">=5", + "symfony/form": ">=5", + "symfony/framework-bundle": ">=5", + "symfony/http-foundation": ">=5", + "symfony/http-kernel": ">=5", + "symfony/inflector": ">=5", + "symfony/intl": ">=5", + "symfony/ldap": ">=5", + "symfony/lock": ">=5", + "symfony/monolog-bridge": ">=5", + "symfony/options-resolver": ">=5", + "symfony/process": ">=5", + "symfony/property-access": ">=5", + "symfony/property-info": ">=5", + "symfony/proxy-manager-bridge": ">=5", + "symfony/routing": ">=5", + "symfony/security": ">=5", + "symfony/security-bundle": ">=5", + "symfony/security-core": ">=5", + "symfony/security-csrf": ">=5", + "symfony/security-guard": ">=5", + "symfony/security-http": ">=5", + "symfony/serializer": ">=5", + "symfony/stopwatch": ">=5", + "symfony/symfony": ">=5", + "symfony/templating": ">=5", + "symfony/translation": ">=5", + "symfony/twig-bridge": ">=5", + "symfony/twig-bundle": ">=5", + "symfony/validator": ">=5", + "symfony/var-dumper": ">=5", + "symfony/web-link": ">=5", + "symfony/web-profiler-bundle": ">=5", + "symfony/web-server-bundle": ">=5", + "symfony/workflow": ">=5", + "symfony/yaml": ">=5" + }, + "type": "metapackage", + "extra": { + "branch-alias": { + "dev-master": "4-dev" + } + }, + "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": "Enforces Long Term Supported versions of Symfony components", + "homepage": "https://symfony.com", + "time": "2017-10-19T02:16:32+00:00" + }, + { + "name": "symfony/maker-bundle", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/maker-bundle.git", + "reference": "39a5604a95cbc91f4e66af629467d455817ca661" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/39a5604a95cbc91f4e66af629467d455817ca661", + "reference": "39a5604a95cbc91f4e66af629467d455817ca661", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/config": "^3.4|^4.0", + "symfony/console": "^3.4|^4.0", + "symfony/dependency-injection": "^3.4|^4.0", + "symfony/filesystem": "^3.4|^4.0", + "symfony/framework-bundle": "^3.4|^4.0", + "symfony/http-kernel": "^3.4|^4.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^3.4|^4.0", + "symfony/process": "^3.4|^4.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\MakerBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "keywords": [ + "code generator", + "generator", + "scaffold", + "scaffolding" + ], + "time": "2017-11-19T08:41:39+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", + "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "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": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2017-10-11T12:05:26+00:00" + }, + { + "name": "symfony/process", + "version": "v4.0.0-BETA4", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "14ec41fb318afb39b11fd5d24b880f29b085600b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/14ec41fb318afb39b11fd5d24b880f29b085600b", + "reference": "14ec41fb318afb39b11fd5d24b880f29b085600b", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "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": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2017-11-07T14:45:01+00:00" + }, + { + "name": "symfony/routing", + "version": "v4.0.0-BETA4", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "faffea9e427c2ab337eba8419de6f5a7ade636ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/faffea9e427c2ab337eba8419de6f5a7ade636ce", + "reference": "faffea9e427c2ab337eba8419de6f5a7ade636ce", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "conflict": { + "symfony/config": "<3.4", + "symfony/dependency-injection": "<3.4", + "symfony/yaml": "<3.4" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "doctrine/common": "~2.2", + "psr/log": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/http-foundation": "~3.4|~4.0", + "symfony/yaml": "~3.4|~4.0" + }, + "suggest": { + "doctrine/annotations": "For using the annotation loader", + "symfony/config": "For using the all-in-one router or any loader", + "symfony/dependency-injection": "For loading routes from a service", + "symfony/expression-language": "For using expression matching", + "symfony/http-foundation": "For using a Symfony Request object", + "symfony/yaml": "For using the YAML loader" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "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": "Symfony Routing Component", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "time": "2017-11-07T14:45:01+00:00" + }, + { + "name": "symfony/yaml", + "version": "v4.0.0-BETA4", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "686c7fc416b454fe4019f1a225206b133ceb4fc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/686c7fc416b454fe4019f1a225206b133ceb4fc1", + "reference": "686c7fc416b454fe4019f1a225206b133ceb4fc1", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "conflict": { + "symfony/console": "<3.4" + }, + "require-dev": { + "symfony/console": "~3.4|~4.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "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": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2017-11-10T19:37:45+00:00" + } + ], + "packages-dev": [ + { + "name": "atoum/atoum", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/atoum/atoum.git", + "reference": "6f4ab88f3a1826aed2372c9f7f0d53b9303f672a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/atoum/atoum/zipball/6f4ab88f3a1826aed2372c9f7f0d53b9303f672a", + "reference": "6f4ab88f3a1826aed2372c9f7f0d53b9303f672a", + "shasum": "" + }, + "require": { + "ext-hash": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^5.6.0 || ^7.0.0" + }, + "replace": { + "mageekguy/atoum": "*" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~1.12" + }, + "suggest": { + "atoum/stubs": "Provides IDE support (like autocompletion) for atoum", + "ext-mbstring": "Provides support for UTF-8 strings", + "ext-xdebug": "Provides code coverage report (>= 2.3)" + }, + "bin": [ + "bin/atoum" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "classmap": [ + "classes/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "FrĆ©dĆ©ric Hardy", + "email": "frederic.hardy@atoum.org", + "homepage": "http://blog.mageekbox.net" + }, + { + "name": "FranƧois Dussert", + "email": "francois.dussert@atoum.org" + }, + { + "name": "GĆ©rald Croes", + "email": "gerald.croes@atoum.org" + }, + { + "name": "Julien Bianchi", + "email": "julien.bianchi@atoum.org" + }, + { + "name": "Ludovic Fleury", + "email": "ludovic.fleury@atoum.org" + } + ], + "description": "Simple modern and intuitive unit testing framework for PHP 5.3+", + "homepage": "http://www.atoum.org", + "keywords": [ + "TDD", + "atoum", + "test", + "unit testing" + ], + "time": "2017-09-07T08:27:13+00:00" + }, + { + "name": "composer/semver", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573", + "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.5 || ^5.0.5", + "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "time": "2016-08-30T16:08:34+00:00" + }, + { + "name": "doctrine/annotations", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "5beebb01b025c94e93686b7a0ed3edae81fe3e7f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/5beebb01b025c94e93686b7a0ed3edae81fe3e7f", + "reference": "5beebb01b025c94e93686b7a0ed3edae81fe3e7f", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": "^7.1" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "time": "2017-07-22T10:58:02+00:00" + }, + { + "name": "doctrine/lexer", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Lexer\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "lexer", + "parser" + ], + "time": "2014-09-09T13:34:57+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v2.8.2", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", + "reference": "b331701944cbe492e466d2b46b2880068803eb08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/b331701944cbe492e466d2b46b2880068803eb08", + "reference": "b331701944cbe492e466d2b46b2880068803eb08", + "shasum": "" + }, + "require": { + "composer/semver": "^1.4", + "doctrine/annotations": "^1.2", + "ext-json": "*", + "ext-tokenizer": "*", + "gecko-packages/gecko-php-unit": "^2.0 || ^3.0", + "php": "^5.6 || >=7.0 <7.3", + "php-cs-fixer/diff": "^1.2", + "symfony/console": "^3.2 || ^4.0", + "symfony/event-dispatcher": "^3.0 || ^4.0", + "symfony/filesystem": "^3.0 || ^4.0", + "symfony/finder": "^3.0 || ^4.0", + "symfony/options-resolver": "^3.0 || ^4.0", + "symfony/polyfill-php70": "^1.0", + "symfony/polyfill-php72": "^1.4", + "symfony/process": "^3.0 || ^4.0", + "symfony/stopwatch": "^3.0 || ^4.0" + }, + "conflict": { + "hhvm": "*" + }, + "require-dev": { + "johnkary/phpunit-speedtrap": "^1.1 || ^2.0@dev", + "justinrainbow/json-schema": "^5.0", + "php-coveralls/php-coveralls": "^1.0.2", + "php-cs-fixer/accessible-object": "^1.0", + "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "symfony/phpunit-bridge": "^3.2.2 || ^4.0" + }, + "suggest": { + "ext-mbstring": "For handling non-UTF8 characters in cache signature.", + "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + }, + "classmap": [ + "tests/Test/Assert/AssertTokensTrait.php", + "tests/Test/AbstractFixerTestCase.php", + "tests/Test/AbstractIntegrationTestCase.php", + "tests/Test/IntegrationCase.php", + "tests/Test/IntegrationCaseFactory.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "time": "2017-11-19T22:51:25+00:00" + }, + { + "name": "gecko-packages/gecko-php-unit", + "version": "v3.0", + "source": { + "type": "git", + "url": "https://github.com/GeckoPackages/GeckoPHPUnit.git", + "reference": "6a866551dffc2154c1b091bae3a7877d39c25ca3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GeckoPackages/GeckoPHPUnit/zipball/6a866551dffc2154c1b091bae3a7877d39c25ca3", + "reference": "6a866551dffc2154c1b091bae3a7877d39c25ca3", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-dom": "When testing with xml.", + "ext-libxml": "When testing with xml.", + "phpunit/phpunit": "This is an extension for it so make sure you have it some way." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "GeckoPackages\\PHPUnit\\": "src/PHPUnit" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Additional PHPUnit asserts and constraints.", + "homepage": "https://github.com/GeckoPackages", + "keywords": [ + "extension", + "filesystem", + "phpunit" + ], + "time": "2017-08-23T07:46:41+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "cda6ed1bfbcf7a3736b8943466ad8b1b5c0cc7c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/cda6ed1bfbcf7a3736b8943466ad8b1b5c0cc7c9", + "reference": "cda6ed1bfbcf7a3736b8943466ad8b1b5c0cc7c9", + "shasum": "" + }, + "require": { + "ocramius/package-versions": "^1.1.3", + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A wrapper for ocramius/pretty-package-versions to get pretty versions strings", + "keywords": [ + "package versions" + ], + "time": "2017-09-06T15:48:57+00:00" + }, + { + "name": "nette/bootstrap", + "version": "v3.0.0-beta", + "source": { + "type": "git", + "url": "https://github.com/nette/bootstrap.git", + "reference": "336a48268c7a58ccb8240dc333e4a54b05a958d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/bootstrap/zipball/336a48268c7a58ccb8240dc333e4a54b05a958d7", + "reference": "336a48268c7a58ccb8240dc333e4a54b05a958d7", + "shasum": "" + }, + "require": { + "nette/di": "^2.4 || ^3.0", + "nette/utils": "^2.4 || ^3.0", + "php": ">=7.1" + }, + "conflict": { + "nette/nette": "<2.2" + }, + "require-dev": { + "latte/latte": "^2.2", + "nette/application": "^2.3 || ^3.0", + "nette/caching": "^2.3 || ^3.0", + "nette/database": "^2.3 || ^3.0", + "nette/forms": "^2.3 || ^3.0", + "nette/http": "^2.4.0 || ^3.0", + "nette/mail": "^2.3 || ^3.0", + "nette/robot-loader": "^2.4.2 || ^3.0", + "nette/safe-stream": "^2.2", + "nette/security": "^2.3 || ^3.0", + "nette/tester": "^2.0", + "tracy/tracy": "^2.4.1" + }, + "suggest": { + "nette/robot-loader": "to use Configurator::createRobotLoader()", + "tracy/tracy": "to use Configurator::enableTracy()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "Nette Bootstrap", + "homepage": "https://nette.org", + "time": "2017-02-02T03:24:19+00:00" + }, + { + "name": "nette/di", + "version": "v2.4.10", + "source": { + "type": "git", + "url": "https://github.com/nette/di.git", + "reference": "a4b3be935b755f23aebea1ce33d7e3c832cdff98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/di/zipball/a4b3be935b755f23aebea1ce33d7e3c832cdff98", + "reference": "a4b3be935b755f23aebea1ce33d7e3c832cdff98", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "nette/neon": "^2.3.3 || ~3.0.0", + "nette/php-generator": "^2.6.1 || ~3.0.0", + "nette/utils": "^2.4.3 || ~3.0.0", + "php": ">=5.6.0" + }, + "conflict": { + "nette/bootstrap": "<2.4", + "nette/nette": "<2.2" + }, + "require-dev": { + "nette/tester": "^2.0", + "tracy/tracy": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "šŸ’Ž Nette Dependency Injection Container: Flexible, compiled and full-featured DIC with perfectly usable autowiring and support for all new PHP 7.1 features.", + "homepage": "https://nette.org", + "keywords": [ + "compiled", + "di", + "dic", + "factory", + "ioc", + "nette", + "static" + ], + "time": "2017-08-31T22:42:00+00:00" + }, + { + "name": "nette/finder", + "version": "v3.0.0-RC", + "source": { + "type": "git", + "url": "https://github.com/nette/finder.git", + "reference": "21d4aa7834e8cb58f3a1666607acdf00d7486edb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/finder/zipball/21d4aa7834e8cb58f3a1666607acdf00d7486edb", + "reference": "21d4aa7834e8cb58f3a1666607acdf00d7486edb", + "shasum": "" + }, + "require": { + "nette/utils": "^2.4 || ~3.0.0", + "php": ">=7.0" + }, + "conflict": { + "nette/nette": "<2.2" + }, + "require-dev": { + "nette/tester": "^2.0", + "tracy/tracy": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "Nette Finder: Files Searching", + "homepage": "https://nette.org", + "time": "2017-02-02T02:09:35+00:00" + }, + { + "name": "nette/neon", + "version": "v3.0.0-beta1", + "source": { + "type": "git", + "url": "https://github.com/nette/neon.git", + "reference": "2c8b4195ca6a1be3d903f1c344b5afb412862155" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/neon/zipball/2c8b4195ca6a1be3d903f1c344b5afb412862155", + "reference": "2c8b4195ca6a1be3d903f1c344b5afb412862155", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "ext-json": "*", + "php": ">=7.0" + }, + "require-dev": { + "nette/tester": "^2.0", + "tracy/tracy": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "Nette NEON: parser & generator for Nette Object Notation", + "homepage": "http://ne-on.org", + "time": "2017-02-02T01:20:49+00:00" + }, + { + "name": "nette/php-generator", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/nette/php-generator.git", + "reference": "eb2dbc9c3409e9db40568109ca4994d51373b60c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/php-generator/zipball/eb2dbc9c3409e9db40568109ca4994d51373b60c", + "reference": "eb2dbc9c3409e9db40568109ca4994d51373b60c", + "shasum": "" + }, + "require": { + "nette/utils": "^2.4.2 || ~3.0.0", + "php": ">=7.0" + }, + "conflict": { + "nette/nette": "<2.2" + }, + "require-dev": { + "nette/tester": "^2.0", + "tracy/tracy": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "šŸ˜ Nette PHP Generator: generates neat PHP code for you. Supports new PHP 7.1 features.", + "homepage": "https://nette.org", + "keywords": [ + "code", + "nette", + "php", + "scaffolding" + ], + "time": "2017-07-11T19:07:13+00:00" + }, + { + "name": "nette/robot-loader", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/nette/robot-loader.git", + "reference": "b703b4f5955831b0bcaacbd2f6af76021b056826" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/robot-loader/zipball/b703b4f5955831b0bcaacbd2f6af76021b056826", + "reference": "b703b4f5955831b0bcaacbd2f6af76021b056826", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "nette/finder": "^2.3 || ^3.0", + "nette/utils": "^2.4 || ^3.0", + "php": ">=5.6.0" + }, + "conflict": { + "nette/nette": "<2.2" + }, + "require-dev": { + "nette/tester": "^2.0", + "tracy/tracy": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "šŸ€ Nette RobotLoader: high performance and comfortable autoloader that will search and autoload classes within your application.", + "homepage": "https://nette.org", + "keywords": [ + "autoload", + "class", + "interface", + "nette", + "trait" + ], + "time": "2017-07-18T00:09:56+00:00" + }, + { + "name": "nette/utils", + "version": "v3.0.0-beta2", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "eed59a3a5baffd431a60d4d5cd0776e246689f43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/eed59a3a5baffd431a60d4d5cd0776e246689f43", + "reference": "eed59a3a5baffd431a60d4d5cd0776e246689f43", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "conflict": { + "nette/nette": "<2.2" + }, + "require-dev": { + "nette/tester": "~2.0", + "tracy/tracy": "^2.3" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize() and toAscii()", + "ext-intl": "for script transliteration in Strings::webalize() and toAscii()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-xml": "to use Strings::length() etc. when mbstring is not available" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "šŸ›  Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "time": "2017-07-21T18:53:58+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v3.1.2", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "08131e7ff29de6bb9f12275c7d35df71f25f4d89" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/08131e7ff29de6bb9f12275c7d35df71f25f4d89", + "reference": "08131e7ff29de6bb9f12275c7d35df71f25f4d89", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "~4.0|~5.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2017-11-04T11:48:34+00:00" + }, + { + "name": "ocramius/package-versions", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/Ocramius/PackageVersions.git", + "reference": "72b226d2957e9e6a9ed09aeaa29cabd840d1a3b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/72b226d2957e9e6a9ed09aeaa29cabd840d1a3b7", + "reference": "72b226d2957e9e6a9ed09aeaa29cabd840d1a3b7", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0", + "php": "~7.0" + }, + "require-dev": { + "composer/composer": "^1.3", + "ext-zip": "*", + "humbug/humbug": "dev-master", + "phpunit/phpunit": "^5.7.5" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "time": "2017-09-06T15:24:43+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v2.0.11", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8", + "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ], + "time": "2017-09-27T21:40:39+00:00" + }, + { + "name": "php-cs-fixer/diff", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/diff.git", + "reference": "f0ef6133d674137e902fdf8a6f2e8e97e14a087b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/f0ef6133d674137e902fdf8a6f2e8e97e14a087b", + "reference": "f0ef6133d674137e902fdf8a6f2e8e97e14a087b", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.4.3", + "symfony/process": "^3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "SpacePossum" + } + ], + "description": "sebastian/diff v2 backport support for PHP5.6", + "homepage": "https://github.com/PHP-CS-Fixer", + "keywords": [ + "diff" + ], + "time": "2017-10-19T09:58:18+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "eff3f0c33d869cf85d22ffa6509037b6f57cc7f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/eff3f0c33d869cf85d22ffa6509037b6f57cc7f5", + "reference": "eff3f0c33d869cf85d22ffa6509037b6f57cc7f5", + "shasum": "" + }, + "require": { + "jean85/pretty-package-versions": "^1.0.2", + "nette/bootstrap": "^2.4 || ^3.0", + "nette/di": "^2.4.7 || ^3.0", + "nette/robot-loader": "^3.0.1", + "nette/utils": "^2.4.5 || ^3.0", + "nikic/php-parser": "^3.1", + "php": "~7.0", + "symfony/console": "~3.2 || ~4.0", + "symfony/finder": "~3.2 || ~4.0" + }, + "require-dev": { + "consistence/coding-standard": "^2.0.0", + "jakub-onderka/php-parallel-lint": "^0.9.2", + "phing/phing": "^2.16.0", + "phpstan/phpstan-php-parser": "^0.9", + "phpstan/phpstan-phpunit": "^0.9", + "phpstan/phpstan-strict-rules": "^0.9", + "phpunit/phpunit": "^6.3", + "slevomat/coding-standard": "^3.3.0" + }, + "bin": [ + "bin/phpstan" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.9-dev" + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": [ + "src/", + "build/PHPStan" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "time": "2017-11-20T06:58:15+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v4.0.0-BETA4", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "ef617a2867c7d889d4ecee3c29595698d87474a4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/ef617a2867c7d889d4ecee3c29595698d87474a4", + "reference": "ef617a2867c7d889d4ecee3c29595698d87474a4", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "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": "Symfony OptionsResolver Component", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "time": "2017-11-07T14:45:01+00:00" + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff", + "reference": "0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "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": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2017-10-11T12:05:26+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "6de4f4884b97abbbed9f0a84a95ff2ff77254254" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/6de4f4884b97abbbed9f0a84a95ff2ff77254254", + "reference": "6de4f4884b97abbbed9f0a84a95ff2ff77254254", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "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": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2017-10-11T12:05:26+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v4.0.0-BETA4", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "ac0e49150555c703fef6b696d8eaba1db7a3ca03" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/ac0e49150555c703fef6b696d8eaba1db7a3ca03", + "reference": "ac0e49150555c703fef6b696d8eaba1db7a3ca03", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "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": "Symfony Stopwatch Component", + "homepage": "https://symfony.com", + "time": "2017-11-09T12:45:29+00:00" + } + ], + "aliases": [], + "minimum-stability": "beta", + "stability-flags": { + "symfony/lts": 20, + "symfony/maker-bundle": 20, + "symfony/process": 10, + "phpstan/phpstan": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^7.1.3" + }, + "platform-dev": [] +} diff --git a/config/bundles.php b/config/bundles.php new file mode 100644 index 0000000..ffeb861 --- /dev/null +++ b/config/bundles.php @@ -0,0 +1,6 @@ + ['all' => true], + Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], +]; diff --git a/config/packages/dev/routing.yaml b/config/packages/dev/routing.yaml new file mode 100644 index 0000000..4116679 --- /dev/null +++ b/config/packages/dev/routing.yaml @@ -0,0 +1,3 @@ +framework: + router: + strict_requirements: true diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml new file mode 100644 index 0000000..297687e --- /dev/null +++ b/config/packages/framework.yaml @@ -0,0 +1,14 @@ +framework: + secret: '%env(APP_SECRET)%' + #default_locale: en + #csrf_protection: ~ + #http_method_override: true + #trusted_hosts: ~ + # https://symfony.com/doc/current/reference/configuration/framework.html#handler-id + #session: + # # The native PHP session handler will be used + # handler_id: ~ + #esi: ~ + #fragments: ~ + php_errors: + log: true diff --git a/config/packages/routing.yaml b/config/packages/routing.yaml new file mode 100644 index 0000000..368bc7f --- /dev/null +++ b/config/packages/routing.yaml @@ -0,0 +1,3 @@ +framework: + router: + strict_requirements: ~ diff --git a/config/packages/test/framework.yaml b/config/packages/test/framework.yaml new file mode 100644 index 0000000..7de07e1 --- /dev/null +++ b/config/packages/test/framework.yaml @@ -0,0 +1,2 @@ +framework: + test: ~ diff --git a/config/services.yaml b/config/services.yaml new file mode 100644 index 0000000..67e1cc1 --- /dev/null +++ b/config/services.yaml @@ -0,0 +1,10 @@ +parameters: + +services: + _defaults: { autowire: true, autoconfigure: true, public: false } + + App\: + resource: '../src' + + App\Domain\DockerClient: + alias: 'App\Infra\ShellDockerClient' \ No newline at end of file diff --git a/docker-stack.yml b/docker-stack.yml new file mode 100644 index 0000000..cba340f --- /dev/null +++ b/docker-stack.yml @@ -0,0 +1,23 @@ +version: '3.4' + +services: + www: + image: nginx:alpine + ports: + - "8000:80" + deploy: + replicas: 2 + success: + image: alpine:3.6 + command: "sh -c 'exit 0'" + deploy: + replicas: 3 + restart_policy: + condition: none + # failure: + # image: alpine:3.6 + # command: "sh -c 'exit 127'" + # deploy: + # replicas: 1 + # restart_policy: + # condition: none diff --git a/src/Command/StackConvergeCommand.php b/src/Command/StackConvergeCommand.php new file mode 100644 index 0000000..c9fa5a7 --- /dev/null +++ b/src/Command/StackConvergeCommand.php @@ -0,0 +1,79 @@ +stack = $stack; + parent::__construct('stack:converge'); + } + + protected function configure() + { + $this + ->setDescription('Wait for services of a freshly deployed stack to converge.') + ->addArgument('stack', InputArgument::REQUIRED, 'name of the stack') + ->addOption('limit', 'l', InputOption::VALUE_OPTIONAL, 'a duration limit in seconds to wait about the stack to converge', 300) + ; + } + + public function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $stackName = $input->getArgument('stack'); + $limit = $input->getOption('limit'); + + try { + $startTime = time(); + $progress = $this->stack->getProgress($stackName); + + $io->progressStart($progress->getDesired()); + $previous = $progress->getCurrent(); + do { + $progress = $this->stack->getProgress($stackName); + $increment = $progress->getCurrent() - $previous; + + if ($increment > 0) { + $io->progressAdvance($increment); + } + + $previous = $progress->getCurrent(); + + if (time() - $startTime >= $limit) { + $io->error('Time limit exceeded.'); + + return self::ETIME; + } + + usleep(self::SLEEP_IN_U_SECONDS); + } while (true !== $progress->hasConverged()); + + $io->progressFinish(); + $io->success('Stack has successfuly converged'); + } catch (ServiceFailure $e) { + $io->error('Emergency stop: at least a service in the stack has a failure.'); + + return self::ENOTRECOVERABLE; + } + } +} diff --git a/src/Domain/DockerClient.php b/src/Domain/DockerClient.php new file mode 100644 index 0000000..2a77990 --- /dev/null +++ b/src/Domain/DockerClient.php @@ -0,0 +1,10 @@ +currentState = $currentState; + $this->desiredState = $desiredState; + } + + public function hasConverged(): bool + { + return $this->currentState->isFinal() && $this->desiredState->isFinal(); + } + + public function hasFailed(): bool + { + return $this->currentState->hasFailed(); + } +} diff --git a/src/Domain/Service/CurrentState.php b/src/Domain/Service/CurrentState.php new file mode 100644 index 0000000..8743ad2 --- /dev/null +++ b/src/Domain/Service/CurrentState.php @@ -0,0 +1,37 @@ +state = $cleanState; + } + + public function isFinal(): bool + { + return in_array($this->state, self::FINAL_STATES, true); + } + + public function hasFailed(): bool + { + return 'failed' === $this->state; + } +} diff --git a/src/Domain/Service/DesiredState.php b/src/Domain/Service/DesiredState.php new file mode 100644 index 0000000..2cbceb5 --- /dev/null +++ b/src/Domain/Service/DesiredState.php @@ -0,0 +1,30 @@ +state = $cleanState; + } + + public function isFinal(): bool + { + return in_array($this->state, self::FINAL_STATES, true); + } +} diff --git a/src/Domain/ServiceFailure.php b/src/Domain/ServiceFailure.php new file mode 100644 index 0000000..6d5a684 --- /dev/null +++ b/src/Domain/ServiceFailure.php @@ -0,0 +1,9 @@ +dockerClient = $dockerClient; + } + + public function getProgress(string $stackName): StackProgress + { + $currentCount = 0; + $desiredCount = 0; + + foreach ($this->dockerClient->stackPs($stackName) as $process) { + $service = new Service( + new Service\CurrentState($process['CurrentState']), + new Service\DesiredState($process['DesiredState']) + ); + if ($service->hasFailed()) { + throw new ServiceFailure(); + } + + ++$desiredCount; + if ($service->hasConverged()) { + $currentCount += 1; + } + } + + return new StackProgress($currentCount, $desiredCount); + } +} diff --git a/src/Domain/StackProgress.php b/src/Domain/StackProgress.php new file mode 100644 index 0000000..c0609fa --- /dev/null +++ b/src/Domain/StackProgress.php @@ -0,0 +1,39 @@ +current = $current; + $this->desired = $desired; + } + + public function getCurrent(): int + { + return $this->current; + } + + public function getDesired(): int + { + return $this->desired; + } + + public function hasConverged(): bool + { + return $this->getCurrent() === $this->getDesired(); + } +} diff --git a/src/Infra/ShellDockerClient.php b/src/Infra/ShellDockerClient.php new file mode 100644 index 0000000..0a3f26b --- /dev/null +++ b/src/Infra/ShellDockerClient.php @@ -0,0 +1,35 @@ +run(); + + if (!$process->isSuccessful()) { + throw new ProcessFailedException($process); + } + + $jsonLines = $this->jsonLines($process->getOutput()); + foreach ($jsonLines as $jsonLine) { + yield json_decode($jsonLine, true); + } + } + + private function jsonLines(string $lines): array + { + return array_filter( + explode("\n", $lines), + function ($line) { return strlen(trim($line)) > 0; } + ); + } +} diff --git a/src/Kernel.php b/src/Kernel.php new file mode 100644 index 0000000..e368601 --- /dev/null +++ b/src/Kernel.php @@ -0,0 +1,57 @@ +environment}"; + } + + public function getLogDir() + { + return '/home/php/log'; + } + + public function registerBundles() + { + $contents = require $this->getProjectDir().'/config/bundles.php'; + foreach ($contents as $class => $envs) { + if (isset($envs['all']) || isset($envs[$this->environment])) { + yield new $class(); + } + } + } + + protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader) + { + $container->setParameter('container.autowiring.strict_mode', true); + $container->setParameter('container.dumper.inline_class_loader', true); + $confDir = $this->getProjectDir().'/config'; + $loader->load($confDir.'/packages/*'.self::CONFIG_EXTS, 'glob'); + if (is_dir($confDir.'/packages/'.$this->environment)) { + $loader->load($confDir.'/packages/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob'); + } + $loader->load($confDir.'/services'.self::CONFIG_EXTS, 'glob'); + $loader->load($confDir.'/services_'.$this->environment.self::CONFIG_EXTS, 'glob'); + } + + protected function configureRoutes(RouteCollectionBuilder $routes) + { + } +} diff --git a/symfony.lock b/symfony.lock new file mode 100644 index 0000000..87ca033 --- /dev/null +++ b/symfony.lock @@ -0,0 +1,176 @@ +{ + "symfony/flex": { + "version": "1.0", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "1.0", + "ref": "e921bdbfe20cdefa3b82f379d1cd36df1bc8d115" + } + }, + "symfony/lts": { + "version": "4-dev" + }, + "psr/log": { + "version": "1.0.2" + }, + "symfony/debug": { + "version": "v4.0.0-beta4" + }, + "symfony/polyfill-mbstring": { + "version": "v1.6.0" + }, + "symfony/console": { + "version": "3.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "3.3", + "ref": "e7bbddb8dd603b2c3770cb10f163e692b4b414aa" + } + }, + "symfony/routing": { + "version": "3.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "3.3", + "ref": "5b2f0ee78c90d671860ac6450e37dec10fbc0719" + } + }, + "symfony/http-foundation": { + "version": "v4.0.0-beta4" + }, + "symfony/event-dispatcher": { + "version": "v4.0.0-beta4" + }, + "symfony/http-kernel": { + "version": "v4.0.0-beta4" + }, + "symfony/finder": { + "version": "v4.0.0-beta4" + }, + "symfony/filesystem": { + "version": "v4.0.0-beta4" + }, + "psr/container": { + "version": "1.0.0" + }, + "symfony/dependency-injection": { + "version": "v4.0.0-beta4" + }, + "symfony/config": { + "version": "v4.0.0-beta4" + }, + "psr/simple-cache": { + "version": "1.0.0" + }, + "psr/cache": { + "version": "1.0.1" + }, + "symfony/cache": { + "version": "v4.0.0-beta4" + }, + "symfony/framework-bundle": { + "version": "3.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "3.3", + "ref": "4772f8752de95b89b4e1f966c6bd49b48366c068" + } + }, + "symfony/yaml": { + "version": "v4.0.0-beta4" + }, + "ocramius/package-versions": { + "version": "1.1.3" + }, + "atoum/atoum": { + "version": "3.2.0" + }, + "nette/utils": { + "version": "v3.0.0-beta2" + }, + "nette/php-generator": { + "version": "v3.0.1" + }, + "nette/neon": { + "version": "v3.0.0-beta1" + }, + "nette/di": { + "version": "v2.4.10" + }, + "nikic/php-parser": { + "version": "v3.1.2" + }, + "jean85/pretty-package-versions": { + "version": "1.0.2" + }, + "nette/finder": { + "version": "v3.0.0-rc" + }, + "nette/robot-loader": { + "version": "v3.0.2" + }, + "nette/bootstrap": { + "version": "v3.0.0-beta" + }, + "phpstan/phpstan": { + "version": "0.9-dev" + }, + "symfony/stopwatch": { + "version": "v4.0.0-beta4" + }, + "symfony/process": { + "version": "v4.0.0-beta4" + }, + "symfony/polyfill-php72": { + "version": "v1.6.0" + }, + "paragonie/random_compat": { + "version": "v2.0.11" + }, + "symfony/polyfill-php70": { + "version": "v1.6.0" + }, + "symfony/options-resolver": { + "version": "v4.0.0-beta4" + }, + "php-cs-fixer/diff": { + "version": "v1.2.0" + }, + "gecko-packages/gecko-php-unit": { + "version": "v3.0" + }, + "doctrine/lexer": { + "version": "v1.0.1" + }, + "doctrine/annotations": { + "version": "v1.5.0" + }, + "composer/semver": { + "version": "1.4.2" + }, + "friendsofphp/php-cs-fixer": { + "version": "2.2", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "2.2", + "ref": "9d60c231a92e69c68b89897813ec4931d0697b1f" + } + }, + "symfony/maker-bundle": { + "version": "1.0", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "1.0", + "ref": "ab19a6746d621be8d660890e565d3a86077fea00" + } + }, + "beberlei/assert": { + "version": "v2.7.11" + } +} diff --git a/tests/Units/Command/StackConvergeCommand.php b/tests/Units/Command/StackConvergeCommand.php new file mode 100644 index 0000000..0ba1d6f --- /dev/null +++ b/tests/Units/Command/StackConvergeCommand.php @@ -0,0 +1,94 @@ +mockGenerator->orphanize('__construct'); + $this->stack = new \mock\App\Domain\Stack(); + $stackName = 'someStack'; + $this->stackName = $stackName; + + $this->input = new \mock\Symfony\Component\Console\Input\InputInterface(); + $this->calling($this->input)->getArgument = function ($name) use ($stackName) { + return 'stack' === $name ? $stackName : null; + }; + $this->calling($this->input)->getOption = function ($name) { + return 'limit' === $name ? 2 : null; + }; + $this->output = new \mock\Symfony\Component\Console\Output\OutputInterface(); + $this->calling($this->output)->getFormatter = new \mock\Symfony\Component\Console\Formatter\OutputFormatterInterface(); + } + + public function testĀ successfulyĀ waitĀ forĀ stackĀ convergence() + { + $this + ->given( + $this->newTestedInstance($this->stack), + $this->calling($this->stack)->getProgress = new \App\Domain\StackProgress(1, 1) + ) + ->when( + $result = $this->testedInstance->execute($this->input, $this->output) + ) + ->then + ->variable($result) + ->isEqualTo(0) + ->mock($this->stack) + ->call('getProgress') + ->withIdenticalArguments($this->stackName) + ->twice() + ; + } + + public function testĀ failĀ onĀ serviceĀ failure() + { + $this + ->given( + $this->newTestedInstance($this->stack), + $this->calling($this->stack)->getProgress = function () { + throw new \App\Domain\ServiceFailure(); + } + ) + ->when( + $result = $this->testedInstance->execute($this->input, $this->output) + ) + ->then + ->variable($result) + ->isEqualTo(131) + ; + } + + public function testĀ failĀ onĀ timeout() + { + $this + ->given( + $this->newTestedInstance($this->stack), + $this->calling($this->stack)->getProgress = function () { + sleep(1); + + return new \App\Domain\StackProgress(1, 2); + } + ) + ->when( + $result = $this->testedInstance->execute($this->input, $this->output) + ) + ->then + ->variable($result) + ->isEqualTo(62) + ; + } +} diff --git a/tests/Units/Domain/Service.php b/tests/Units/Domain/Service.php new file mode 100644 index 0000000..534f712 --- /dev/null +++ b/tests/Units/Domain/Service.php @@ -0,0 +1,40 @@ +when( + $this->newTestedInstance($currentState, $desiredState) + ) + ->then + ->boolean($this->testedInstance->hasConverged()) + ->isIdenticalTo($hasConverged) + ; + } + + protected function convergence(): array + { + return [ + 'Convergence [complete,running]' => [new CurrentState('complete'), new DesiredState('running'), true], + 'Convergence [running,running]' => [new CurrentState('running'), new DesiredState('running'), true], + 'Convergence [failed,running]' => [new CurrentState('failed'), new DesiredState('running'), true], + 'Convergence [complete,shutdown]' => [new CurrentState('complete'), new DesiredState('shutdown'), true], + 'Convergence [running,shutdown]' => [new CurrentState('running'), new DesiredState('shutdown'), true], + 'Convergence [failed,shutdown]' => [new CurrentState('failed'), new DesiredState('shutdown'), true], + '!= Convergence [accepted,shutdown]' => [new CurrentState('accepted'), new DesiredState('shutdown'), false], + ]; + } +} diff --git a/tests/Units/Domain/Service/CurrentState.php b/tests/Units/Domain/Service/CurrentState.php new file mode 100644 index 0000000..75f5b2c --- /dev/null +++ b/tests/Units/Domain/Service/CurrentState.php @@ -0,0 +1,67 @@ +when( + $this->newTestedInstance($state) + ) + ->then + ->boolean($this->testedInstance->isFinal()) + ->isIdenticalTo($isFinal) + ; + } + + public function testĀ stateĀ isĀ failure() + { + $this + ->given( + $state = 'failed' + ) + ->when( + $this->newTestedInstance($state) + ) + ->then + ->boolean($this->testedInstance->hasFailed()) + ->isTrue() + ; + } + + public function testĀ invalidĀ stateĀ isĀ notĀ accepted() + { + $this + ->given( + $state = '' + ) + ->exception(function () use ($state) { + $this->newTestedInstance($state); + }) + ->isInstanceOf('\InvalidArgumentException') + ; + } + + protected function finalState(): array + { + return [ + 'Not final state accepted' => ['accepted', false], + 'Not final state preparing' => ['Preparing 1 second ago', false], + 'Not final state pending' => ['Pending 1 second ago', false], + 'Not final state assigned' => ['assigned 1 second ago', false], + 'Not final state starting' => ['Starting', false], + 'Final state complete' => ['Complete since 2 hours', true], + 'Final state running' => ['Running', true], + 'Final state failed' => ['Failed a few seconds ago', true], + ]; + } +} diff --git a/tests/Units/Domain/Service/DesiredState.php b/tests/Units/Domain/Service/DesiredState.php new file mode 100644 index 0000000..5cb6ff2 --- /dev/null +++ b/tests/Units/Domain/Service/DesiredState.php @@ -0,0 +1,47 @@ +when( + $this->newTestedInstance($state) + ) + ->then + ->boolean($this->testedInstance->isFinal()) + ->isIdenticalTo($isFinal) + ; + } + + public function testĀ invalidĀ stateĀ isĀ notĀ accepted() + { + $this + ->given( + $state = '' + ) + ->exception(function () use ($state) { + $this->newTestedInstance($state); + }) + ->isInstanceOf('\InvalidArgumentException') + ; + } + + protected function finalState(): array + { + return [ + 'Not final state running' => ['Running', true], + 'Not final state shutdown' => ['shutdown', true], + 'Final state accepted' => [' Accepted', false], + ]; + } +} diff --git a/tests/Units/Domain/Stack.php b/tests/Units/Domain/Stack.php new file mode 100644 index 0000000..c630eb1 --- /dev/null +++ b/tests/Units/Domain/Stack.php @@ -0,0 +1,81 @@ +given( + $dockerClientMock = new \mock\App\Domain\DockerClient(), + $this->calling($dockerClientMock)->stackPs = function () { + yield from + [ + [ + 'CurrentState' => 'running', + 'DesiredState' => 'running', + ], + [ + 'CurrentState' => 'running', + 'DesiredState' => 'shutdown', + ], + ]; + } + ) + ->and( + $stackName = 'someStack' + ) + ->and( + $this->newTestedInstance($dockerClientMock) + ) + ->when( + $progress = $this->testedInstance->getProgress($stackName) + ) + ->then + ->mock($dockerClientMock) + ->call('stackPs') + ->withIdenticalArguments($stackName) + ->once() + ->boolean($progress->hasConverged()) + ->isTrue() + ; + } + + public function testĀ itĀ failĀ toĀ trackĀ progressĀ onĀ anyĀ serviceĀ failure() + { + $this + ->given( + $dockerClientMock = new \mock\App\Domain\DockerClient(), + $this->calling($dockerClientMock)->stackPs = function () { + yield from + [ + [ + 'CurrentState' => 'running', + 'DesiredState' => 'running', + ], + [ + 'CurrentState' => 'failed', + 'DesiredState' => 'running', + ], + ]; + } + ) + ->and( + $stackName = 'someStack' + ) + ->and( + $this->newTestedInstance($dockerClientMock) + ) + ->exception(function () use ($dockerClientMock, $stackName) { + $this->testedInstance->getProgress($stackName); + }) + ->isInstanceOf('\App\Domain\ServiceFailure') + ; + } +} diff --git a/tests/Units/Domain/StackProgress.php b/tests/Units/Domain/StackProgress.php new file mode 100644 index 0000000..94fd24a --- /dev/null +++ b/tests/Units/Domain/StackProgress.php @@ -0,0 +1,63 @@ +when( + $this->newTestedInstance($current, $desired) + ) + ->then + ->boolean($this->testedInstance->hasConverged()) + ->isIdenticalTo($hasConverged) + ->integer($this->testedInstance->getCurrent()) + ->isIdenticalTo($current) + ->integer($this->testedInstance->getDesired()) + ->isIdenticalTo($desired) + ; + } + + /** + * @dataProvider invalidBounds + */ + public function testĀ itĀ failsĀ withĀ invalidĀ bounds(int $current, int $desired) + { + $this + ->exception(function () use ($current, $desired) { + $this->newTestedInstance($current, $desired); + }) + ->isInstanceOf('\InvalidArgumentException') + ; + } + + public function convergence(): array + { + return [ + 'Convergence #1' => [1, 1, true], + 'Convergence #2' => [PHP_INT_MAX, PHP_INT_MAX, true], + '!= Convergence #1' => [1, 2, false], + '!= Convergence #2' => [1, PHP_INT_MAX, false], + ]; + } + + protected function invalidBounds(): array + { + return [ + 'current < 0' => [-1, 1], + 'current = 0' => [0, 1], + 'current > desired' => [2, 1], + 'desired = 0' => [2, 0], + 'desired < 0' => [2, -1], + ]; + } +}