commit 0c7b2f252ecd21a415eb9952652ea6857b4d40b6 Author: Graham Christensen Date: Sun Oct 29 17:10:26 2017 -0400 Initial port from my network diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0139c4a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +config.php +vendor +*.log +test.php diff --git a/README.md b/README.md new file mode 100644 index 0000000..7ef40d0 --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +# grahamcofborg + +1. All github events go in to web/index.php, which sends the event to + an exchange named for the full name of the repo (ex: nixos/nixpkgs) + in lower case. The exchange is set to "fanout" +2. build-filter.php creates a queue called build-inputs and binds it + to the nixos/nixpkgs exchange. It also creates an exchange, + build-jobs, set to fan out. It listens for messages on the + build-inputs queue. Issue comments from authorized users on + PRs get tokenized and turned in to build instructions. These jobs + are then written to the build-jobs exchange. +3. builder.php creates a queue called `build-inputs-x86_64-linux`, and + binds it to the build-jobs exchange. It then listens for build + instructions on the `build-inputs-x86_64-linux` queue. For each + job, it uses nix-build to run the build instructions. The status + result (pass/fail) and the last ten lines of output are then placed + in to the `build-results` queue. +4. poster.php declares the build-results queue, and listens for + messages on it. It posts the build status and text output on the PR + the build is from. + + +The conspicuously missing config.php looks like: + +```php + true, + 'verify_peer_name' => true, + 'peer_name' => 'events.nix.gsc.io', + 'verify_depth' => 10, + 'ca_file' => '/etc/ssl/certs/ca-certificates.crt' + ) + ); + + return $connection; +} + +function gh_client() { + $client = new \Github\Client(); + $client->authenticate('githubusername', + 'githubpassword', + Github\Client::AUTH_HTTP_PASSWORD); + + return $client; + + +} +``` diff --git a/build-filter.php b/build-filter.php new file mode 100644 index 0000000..b4316ad --- /dev/null +++ b/build-filter.php @@ -0,0 +1,118 @@ +channel(); +$channel->basic_qos(null, 1, true); + +$channel->exchange_declare('build-jobs', 'fanout', false, true, false); + + +list($queueName, , ) = $channel->queue_declare('build-inputs', + false, true, false, false); +$channel->queue_bind($queueName, 'nixos/nixpkgs'); +$channel->queue_bind($queueName, 'grahamc/elm-stuff'); + +function runner($msg) { + $in = json_decode($msg->body); + + try { + $etype = \GHE\EventClassifier::classifyEvent($in); + + if ($etype != "issue_comment") { + echo "Skipping event type: $etype\n"; + return true; + } + } catch (\GHE\EventClassifierUnknownException $e) { + echo "Skipping unknown event type\n"; + print_r($in); + return true; + } + + if (!\GHE\ACL::isUserAuthorized($in->comment->user->login)) { + echo "Commenter not authorized (" . $in->comment->user->login . ")\n"; + return true; + } + + if (!\GHE\ACL::isRepoEligible($in->repository->full_name)) { + echo "Repo not authorized (" . $in->repository->full_name . ")\n"; + return true; + } + + if (!isset($in->issue->pull_request)) { + echo "not a PR\n"; + return true; + } + + # // We don't get a useful pull_request here, we'd have to fetch it + # to know if it is open + #if ($in->issue->pull_request->state != "open") { + # var_dump($in->issue->pull_request); + # echo "PR isn't open\n"; + # return true; + # } + + $cmt = explode(' ', strtolower($in->comment->body)); + if (!in_array('@grahamcofborg', $cmt)) { + echo "not a borgpr\n"; + return true; + } + + $cmt = explode(' ', $in->comment->body); + + $tokens = array_map(function($term) { return trim($term); }, + array_filter($cmt, + function($term) { + return !in_array(strtolower($term), [ + "@grahamcofborg", + "", + ]); + } + ) + ); + + if (count($tokens) == 1 && implode("", $tokens) == "default") { + echo "default support is blocked\n"; + return true; + $forward = [ + 'payload' => $in, + 'build_default' => true, + 'attrs' => [], + ]; + } else { + $forward = [ + 'payload' => $in, + 'build_default' => false, + 'attrs' => $tokens, + ]; + } + + echo "forwarding to build-jobs :)\n"; + + $message = new AMQPMessage(json_encode($forward), + array('content_type' => 'application/json')); + $msg->delivery_info['channel']->basic_publish($message, 'build-jobs'); + return true; +} + + +function outrunner($msg) { + try { + if (runner($msg) === true) { + $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']); + } + } catch (\GHE\ExecException $e) { + var_dump($e->getMessage()); + var_dump($e->getCode()); + var_dump($e->getOutput()); + } +} + +$consumerTag = 'consumer' . getmypid(); +$channel->basic_consume($queueName, $consumerTag, false, false, false, false, 'outrunner'); +while(count($channel->callbacks)) { + $channel->wait(); +} diff --git a/builder.php b/builder.php new file mode 100644 index 0000000..81b8020 --- /dev/null +++ b/builder.php @@ -0,0 +1,107 @@ +channel(); +$channel->basic_qos(null, 1, true); + +list($queueName, , ) = $channel->queue_declare('build-results', + false, true, false, false); + +list($queueName, , ) = $channel->queue_declare('build-inputs-x86_64-linux', + false, true, false, false); +$channel->queue_bind($queueName, 'build-jobs'); + + +function runner($msg) { + echo "got a job!\n"; + $body = json_decode($msg->body); + $in = $body->payload; + + $co = new GHE\Checkout("/home/grahamc/.nix-test", "builder"); + $pname = $co->checkOutRef($in->repository->full_name, + $in->repository->clone_url, + $in->issue->number, + "origin/master" + ); + + $patch_url = $in->issue->pull_request->patch_url; + echo "Building $patch_url\n"; + $co->applyPatches($pname, $patch_url); + + if ($body->build_default) { + echo "building via nix-build .\n"; + + $cmd = 'NIX_PATH=nixpkgs=%s nix-build --option restrict-eval true --keep-going .'; + $args = [$pname]; + } else { + echo "building via nix-build . -A\n"; + $attrs = array_intersperse(array_values((array)$body->attrs), '-A'); + var_dump($attrs); + + $fillers = implode(" ", array_fill(0, count($attrs), '%s')); + + $cmd = 'NIX_PATH=nixpkgs=%s nix-build --option restrict-eval true --keep-going . ' . $fillers; + $args = $attrs; + array_unshift($args, $pname); + } + + try { + $output = GHE\Exec::exec($cmd, $args); + $pass = true; + } catch (GHE\ExecException $e) { + $output = $e->getOutput(); + $pass = false; + } + + $lastlines = array_reverse( + array_slice( + array_reverse($output), + 0, 10 + ) + ); + + $forward = [ + 'payload' => $in, + 'output' => $lastlines, + 'success' => $pass, + ]; + + $message = new AMQPMessage(json_encode($forward), + array('content_type' => 'application/json')); + $msg->delivery_info['channel']->basic_publish($message, '', 'build-results'); + $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']); + + echo "finished\n"; +} + +function array_intersperse($array, $val) { + return array_reduce($array, + function($c, $elem) use ($val) { + $c[] = $val; + $c[] = $elem; + return $c; + }, + array()); +} + + +function outrunner($msg) { + try { + return runner($msg); + } catch (ExecException $e) { + var_dump($e->getMessage()); + var_dump($e->getCode()); + var_dump($e->getOutput()); + } +} + + +$consumerTag = 'consumer' . getmypid(); +$channel->basic_consume($queueName, $consumerTag, false, false, false, false, 'outrunner'); +while(count($channel->callbacks)) { + $channel->wait(); +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..2c7424a --- /dev/null +++ b/composer.json @@ -0,0 +1,14 @@ +{ + "require": { + "php-amqplib/php-amqplib": ">=2.6.1", + "knplabs/github-api": "^2.6@dev", + "php-http/guzzle6-adapter": "^1.2@dev" + }, + "minimum-stability": "dev", + "autoload": { + "psr-4": {"GHE\\": "src/"} + }, + "require-dev": { + "phpunit/phpunit": "^6.4" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..df5b6ee --- /dev/null +++ b/composer.lock @@ -0,0 +1,2461 @@ +{ + "_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": "5ac078c9602fc65993891726f1216093", + "packages": [ + { + "name": "clue/stream-filter", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/clue/php-stream-filter.git", + "reference": "d80fdee9b3a7e0d16fc330a22f41f3ad0eeb09d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/php-stream-filter/zipball/d80fdee9b3a7e0d16fc330a22f41f3ad0eeb09d0", + "reference": "d80fdee9b3a7e0d16fc330a22f41f3ad0eeb09d0", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "phpunit/phpunit": "^5.0 || ^4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Clue\\StreamFilter\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@lueck.tv" + } + ], + "description": "A simple and modern approach to stream filtering in PHP", + "homepage": "https://github.com/clue/php-stream-filter", + "keywords": [ + "bucket brigade", + "callback", + "filter", + "php_user_filter", + "stream", + "stream_filter_append", + "stream_filter_register" + ], + "time": "2017-08-18T09:54:01+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "6.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699", + "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699", + "shasum": "" + }, + "require": { + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.4", + "php": ">=5.5" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.0 || ^5.0", + "psr/log": "^1.0" + }, + "suggest": { + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.2-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2017-06-22T18:50:49+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "e9cdab6ff93ff789b5b599326c727f51d10893a6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/e9cdab6ff93ff789b5b599326c727f51d10893a6", + "reference": "e9cdab6ff93ff789b5b599326c727f51d10893a6", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "time": "2017-10-06T12:25:00+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "d2537c86fa8b004c29e9b9f5e10028f0a29df101" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/d2537c86fa8b004c29e9b9f5e10028f0a29df101", + "reference": "d2537c86fa8b004c29e9b9f5e10028f0a29df101", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "request", + "response", + "stream", + "uri", + "url" + ], + "time": "2017-10-07T03:19:56+00:00" + }, + { + "name": "knplabs/github-api", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/KnpLabs/php-github-api.git", + "reference": "13003af4d8f5abf8e55a8ad38a4eea4dfed5a513" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/KnpLabs/php-github-api/zipball/13003af4d8f5abf8e55a8ad38a4eea4dfed5a513", + "reference": "13003af4d8f5abf8e55a8ad38a4eea4dfed5a513", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "php-http/cache-plugin": "^1.4", + "php-http/client-common": "^1.3", + "php-http/client-implementation": "^1.0", + "php-http/discovery": "^1.0", + "php-http/httplug": "^1.1", + "psr/cache": "^1.0", + "psr/http-message": "^1.0" + }, + "require-dev": { + "cache/array-adapter": "^0.4", + "guzzlehttp/psr7": "^1.2", + "php-http/guzzle6-adapter": "^1.0", + "php-http/mock-client": "^1.0", + "phpunit/phpunit": "^4.0 || ^5.5", + "sllh/php-cs-fixer-styleci-bridge": "^1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Github\\": "lib/Github/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Thibault Duplessis", + "email": "thibault.duplessis@gmail.com", + "homepage": "http://ornicar.github.com" + }, + { + "name": "KnpLabs Team", + "homepage": "http://knplabs.com" + } + ], + "description": "GitHub API v3 client", + "homepage": "https://github.com/KnpLabs/php-github-api", + "keywords": [ + "api", + "gh", + "gist", + "github" + ], + "time": "2017-10-13T06:47:44+00:00" + }, + { + "name": "php-amqplib/php-amqplib", + "version": "v2.7.0", + "source": { + "type": "git", + "url": "https://github.com/php-amqplib/php-amqplib.git", + "reference": "f48748546e398d846134c594dfca9070c4c3b356" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/f48748546e398d846134c594dfca9070c4c3b356", + "reference": "f48748546e398d846134c594dfca9070c4c3b356", + "shasum": "" + }, + "require": { + "ext-bcmath": "*", + "ext-mbstring": "*", + "php": ">=5.3.0" + }, + "replace": { + "videlalvaro/php-amqplib": "self.version" + }, + "require-dev": { + "phpunit/phpunit": "^4.8", + "scrutinizer/ocular": "^1.1", + "squizlabs/php_codesniffer": "^2.5" + }, + "suggest": { + "ext-sockets": "Use AMQPSocketConnection" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "PhpAmqpLib\\": "PhpAmqpLib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "Alvaro Videla", + "role": "Original Maintainer" + }, + { + "name": "John Kelly", + "email": "johnmkelly86@gmail.com", + "role": "Maintainer" + }, + { + "name": "Raúl Araya", + "email": "nubeiro@gmail.com", + "role": "Maintainer" + } + ], + "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.", + "homepage": "https://github.com/php-amqplib/php-amqplib/", + "keywords": [ + "message", + "queue", + "rabbitmq" + ], + "time": "2017-08-03T22:06:21+00:00" + }, + { + "name": "php-http/cache-plugin", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-http/cache-plugin.git", + "reference": "eef8c86e91af39d0d9323a921f55187ad8282194" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/cache-plugin/zipball/eef8c86e91af39d0d9323a921f55187ad8282194", + "reference": "eef8c86e91af39d0d9323a921f55187ad8282194", + "shasum": "" + }, + "require": { + "php": "^5.4 || ^7.0", + "php-http/client-common": "^1.1", + "php-http/message-factory": "^1.0", + "psr/cache": "^1.0", + "symfony/options-resolver": "^2.6 || ^3.0" + }, + "require-dev": { + "henrikbjorn/phpspec-code-coverage": "^1.0", + "phpspec/phpspec": "^2.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Client\\Common\\Plugin\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "PSR-6 Cache plugin for HTTPlug", + "homepage": "http://httplug.io", + "keywords": [ + "cache", + "http", + "httplug", + "plugin" + ], + "time": "2017-07-31T06:46:14+00:00" + }, + { + "name": "php-http/client-common", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-http/client-common.git", + "reference": "1901ad36347227c14751a218d8f4ea1467d1f1ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/client-common/zipball/1901ad36347227c14751a218d8f4ea1467d1f1ed", + "reference": "1901ad36347227c14751a218d8f4ea1467d1f1ed", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "php-http/httplug": "^1.1", + "php-http/message": "^1.6", + "php-http/message-factory": "^1.0", + "symfony/options-resolver": "^2.6 || ^3.0" + }, + "require-dev": { + "guzzlehttp/psr7": "^1.4", + "henrikbjorn/phpspec-code-coverage": "^1.0", + "phpspec/phpspec": "^2.4" + }, + "suggest": { + "php-http/cache-plugin": "PSR-6 Cache plugin", + "php-http/logger-plugin": "PSR-3 Logger plugin", + "php-http/stopwatch-plugin": "Symfony Stopwatch plugin" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Client\\Common\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Common HTTP Client implementations and tools for HTTPlug", + "homepage": "http://httplug.io", + "keywords": [ + "client", + "common", + "http", + "httplug" + ], + "time": "2017-10-16T16:16:36+00:00" + }, + { + "name": "php-http/discovery", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-http/discovery.git", + "reference": "d39a8798c473b6b896059a8de576598e4d17d992" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/discovery/zipball/d39a8798c473b6b896059a8de576598e4d17d992", + "reference": "d39a8798c473b6b896059a8de576598e4d17d992", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0" + }, + "require-dev": { + "henrikbjorn/phpspec-code-coverage": "^2.0.2", + "php-http/httplug": "^1.0", + "php-http/message-factory": "^1.0", + "phpspec/phpspec": "^2.4", + "puli/composer-plugin": "1.0.0-beta10" + }, + "suggest": { + "php-http/message": "Allow to use Guzzle, Diactoros or Slim Framework factories", + "puli/composer-plugin": "Sets up Puli which is recommended for Discovery to work. Check http://docs.php-http.org/en/latest/discovery.html for more details." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Discovery\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Finds installed HTTPlug implementations and PSR-7 message factories", + "homepage": "http://php-http.org", + "keywords": [ + "adapter", + "client", + "discovery", + "factory", + "http", + "message", + "psr7" + ], + "time": "2017-09-13T14:06:45+00:00" + }, + { + "name": "php-http/guzzle6-adapter", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-http/guzzle6-adapter.git", + "reference": "54181ff8455a4c2e1706a53e0d98060b93030321" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/guzzle6-adapter/zipball/54181ff8455a4c2e1706a53e0d98060b93030321", + "reference": "54181ff8455a4c2e1706a53e0d98060b93030321", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0", + "php": "^5.5 || ^7.0", + "php-http/httplug": "^1.0" + }, + "provide": { + "php-http/async-client-implementation": "1.0", + "php-http/client-implementation": "1.0" + }, + "require-dev": { + "ext-curl": "*", + "php-http/client-integration-tests": "^0.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Adapter\\Guzzle6\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + }, + { + "name": "David de Boer", + "email": "david@ddeboer.nl" + } + ], + "description": "Guzzle 6 HTTP Adapter", + "homepage": "http://httplug.io", + "keywords": [ + "Guzzle", + "http" + ], + "time": "2017-05-29T15:06:15+00:00" + }, + { + "name": "php-http/httplug", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-http/httplug.git", + "reference": "036f86f0cb1f37c13dd9b5e75b71866e7ab3f60b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/httplug/zipball/036f86f0cb1f37c13dd9b5e75b71866e7ab3f60b", + "reference": "036f86f0cb1f37c13dd9b5e75b71866e7ab3f60b", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "php-http/promise": "^1.0", + "psr/http-message": "^1.0" + }, + "require-dev": { + "henrikbjorn/phpspec-code-coverage": "^1.0", + "phpspec/phpspec": "^2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eric GELOEN", + "email": "geloen.eric@gmail.com" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "HTTPlug, the HTTP client abstraction for PHP", + "homepage": "http://httplug.io", + "keywords": [ + "client", + "http" + ], + "time": "2017-08-18T18:51:51+00:00" + }, + { + "name": "php-http/message", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-http/message.git", + "reference": "2edd63bae5f52f79363c5f18904b05ce3a4b7253" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/message/zipball/2edd63bae5f52f79363c5f18904b05ce3a4b7253", + "reference": "2edd63bae5f52f79363c5f18904b05ce3a4b7253", + "shasum": "" + }, + "require": { + "clue/stream-filter": "^1.3", + "php": ">=5.4", + "php-http/message-factory": "^1.0.2", + "psr/http-message": "^1.0" + }, + "provide": { + "php-http/message-factory-implementation": "1.0" + }, + "require-dev": { + "akeneo/phpspec-skip-example-extension": "^1.0", + "coduo/phpspec-data-provider-extension": "^1.0", + "ext-zlib": "*", + "guzzlehttp/psr7": "^1.0", + "henrikbjorn/phpspec-code-coverage": "^1.0", + "phpspec/phpspec": "^2.4", + "slim/slim": "^3.0", + "zendframework/zend-diactoros": "^1.0" + }, + "suggest": { + "ext-zlib": "Used with compressor/decompressor streams", + "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories", + "slim/slim": "Used with Slim Framework PSR-7 implementation", + "zendframework/zend-diactoros": "Used with Diactoros Factories" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Message\\": "src/" + }, + "files": [ + "src/filters.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "HTTP Message related tools", + "homepage": "http://php-http.org", + "keywords": [ + "http", + "message", + "psr-7" + ], + "time": "2017-07-05T06:40:53+00:00" + }, + { + "name": "php-http/message-factory", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-http/message-factory.git", + "reference": "a2809d4fe294ebe8879aec8d4d5bf21faa029344" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/message-factory/zipball/a2809d4fe294ebe8879aec8d4d5bf21faa029344", + "reference": "a2809d4fe294ebe8879aec8d4d5bf21faa029344", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Factory interfaces for PSR-7 HTTP Message", + "homepage": "http://php-http.org", + "keywords": [ + "factory", + "http", + "message", + "stream", + "uri" + ], + "time": "2016-02-03T08:16:31+00:00" + }, + { + "name": "php-http/promise", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-http/promise.git", + "reference": "810b30da8bcf69e4b82c4b9bc6b31518234293ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/promise/zipball/810b30da8bcf69e4b82c4b9bc6b31518234293ab", + "reference": "810b30da8bcf69e4b82c4b9bc6b31518234293ab", + "shasum": "" + }, + "require-dev": { + "henrikbjorn/phpspec-code-coverage": "^1.0", + "phpspec/phpspec": "^2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + }, + { + "name": "Joel Wurtz", + "email": "joel.wurtz@gmail.com" + } + ], + "description": "Promise used for asynchronous HTTP requests", + "homepage": "http://httplug.io", + "keywords": [ + "promise" + ], + "time": "2016-01-28T07:54:12+00:00" + }, + { + "name": "psr/cache", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "78c5a01ddbf11cf731f1338a4f5aba23b14d5b47" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/78c5a01ddbf11cf731f1338a4f5aba23b14d5b47", + "reference": "78c5a01ddbf11cf731f1338a4f5aba23b14d5b47", + "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-10-13T14:48:10+00:00" + }, + { + "name": "psr/http-message", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "3.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "d0412a6676e80d5aea70f42ec964de77f61a3516" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/d0412a6676e80d5aea70f42ec964de77f61a3516", + "reference": "d0412a6676e80d5aea70f42ec964de77f61a3516", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-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-10-12T16:27:27+00:00" + } + ], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "7af8066e48b8a4cbd90849bbe9234ab444057968" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/7af8066e48b8a4cbd90849bbe9234ab444057968", + "reference": "7af8066e48b8a4cbd90849bbe9234ab444057968", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "^6.2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2017-09-19T12:41:22+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.7.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^4.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "time": "2017-10-19T19:58:43+00:00" + }, + { + "name": "phar-io/manifest", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "014feadb268809af7c8e2f7ccd396b8494901f58" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/014feadb268809af7c8e2f7ccd396b8494901f58", + "reference": "014feadb268809af7c8e2f7ccd396b8494901f58", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "phar-io/version": "^1.0.1", + "php": "^5.6 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "time": "2017-04-07T07:07:10+00:00" + }, + { + "name": "phar-io/version", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", + "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "time": "2017-03-05T17:38:23+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2017-09-11T18:02:19+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "4.1.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/2d3d238c433cf69caeb4842e97a3223a116f94b2", + "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2", + "shasum": "" + }, + "require": { + "php": "^7.0", + "phpdocumentor/reflection-common": "^1.0@dev", + "phpdocumentor/type-resolver": "^0.4.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2017-08-30T18:51:59+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "time": "2017-07-14T14:27:02+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6", + "reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", + "sebastian/comparator": "^1.1|^2.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5|^3.2", + "phpunit/phpunit": "^4.8 || ^5.6.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2017-09-04T11:05:03+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "77a1ba8076365f943e2a3d75573b6c9822840ac6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/77a1ba8076365f943e2a3d75573b6c9822840ac6", + "reference": "77a1ba8076365f943e2a3d75573b6c9822840ac6", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^7.0", + "phpunit/php-file-iterator": "^1.4.2", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-token-stream": "^2.0", + "sebastian/code-unit-reverse-lookup": "^1.0.1", + "sebastian/environment": "^3.0", + "sebastian/version": "^2.0.1", + "theseer/tokenizer": "^1.1" + }, + "require-dev": { + "ext-xdebug": "^2.5", + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-xdebug": "^2.5.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2017-08-25T06:32:04+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2016-10-03T07:40:28+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21T13:50:34+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "d107f347d368dd8a384601398280c7c608390ab7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/d107f347d368dd8a384601398280c7c608390ab7", + "reference": "d107f347d368dd8a384601398280c7c608390ab7", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2017-03-07T15:42:04+00:00" + }, + { + "name": "phpunit/php-token-stream", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "9a02332089ac48e704c70f6cefed30c224e3c0b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/9a02332089ac48e704c70f6cefed30c224e3c0b0", + "reference": "9a02332089ac48e704c70f6cefed30c224e3c0b0", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2017-08-20T05:47:52+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "1ce64f90a27a563c9eeef8cf384aabfeddaf572a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1ce64f90a27a563c9eeef8cf384aabfeddaf572a", + "reference": "1ce64f90a27a563c9eeef8cf384aabfeddaf572a", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "myclabs/deep-copy": "^1.6.1", + "phar-io/manifest": "^1.0.1", + "phar-io/version": "^1.0", + "php": "^7.0", + "phpspec/prophecy": "^1.7", + "phpunit/php-code-coverage": "^5.2.2", + "phpunit/php-file-iterator": "^1.4.2", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^1.0.9", + "phpunit/phpunit-mock-objects": "^4.0.3", + "sebastian/comparator": "^2.0.2", + "sebastian/diff": "^2.0", + "sebastian/environment": "^3.1", + "sebastian/exporter": "^3.1", + "sebastian/global-state": "^2.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^1.0", + "sebastian/version": "^2.0.1" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "3.0.2", + "phpunit/dbunit": "<3.0" + }, + "require-dev": { + "ext-pdo": "*" + }, + "suggest": { + "ext-xdebug": "*", + "phpunit/php-invoker": "^1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.5.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2017-10-23T05:05:42+00:00" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "2f789b59ab89669015ad984afa350c4ec577ade0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/2f789b59ab89669015ad984afa350c4ec577ade0", + "reference": "2f789b59ab89669015ad984afa350c4ec577ade0", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.5", + "php": "^7.0", + "phpunit/php-text-template": "^1.2.1", + "sebastian/exporter": "^3.0" + }, + "conflict": { + "phpunit/phpunit": "<6.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2017-08-03T14:08:16+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "3488be0a7b346cd6e5361510ed07e88f9bea2e88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/3488be0a7b346cd6e5361510ed07e88f9bea2e88", + "reference": "3488be0a7b346cd6e5361510ed07e88f9bea2e88", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "time": "2017-03-04T10:23:55+00:00" + }, + { + "name": "sebastian/comparator", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "7672f0c31b6ec068e3e941dc5025f822fe724b73" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/7672f0c31b6ec068e3e941dc5025f822fe724b73", + "reference": "7672f0c31b6ec068e3e941dc5025f822fe724b73", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/diff": "^2.0", + "sebastian/exporter": "^3.1" + }, + "require-dev": { + "phpunit/phpunit": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2017-10-16T04:35:48+00:00" + }, + { + "name": "sebastian/diff", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "376cb25efae48c66fb660c9ce7bc07e1682b5e84" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/376cb25efae48c66fb660c9ce7bc07e1682b5e84", + "reference": "376cb25efae48c66fb660c9ce7bc07e1682b5e84", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2017-10-05T13:24:46+00:00" + }, + { + "name": "sebastian/environment", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", + "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2017-07-01T08:51:00+00:00" + }, + { + "name": "sebastian/exporter", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2017-04-03T13:19:02+00:00" + }, + { + "name": "sebastian/global-state", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2017-04-27T15:39:26+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2017-08-03T12:35:26+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "773f97c67f28de00d397be301821b06708fca0be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", + "reference": "773f97c67f28de00d397be301821b06708fca0be", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "time": "2017-03-29T09:07:27+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "a0e54bc9bf04e2c5b302236984cebc277631f0f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/a0e54bc9bf04e2c5b302236984cebc277631f0f1", + "reference": "a0e54bc9bf04e2c5b302236984cebc277631f0f1", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2017-03-07T15:09:59+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "fadc83f7c41fb2924e542635fea47ae546816ece" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/fadc83f7c41fb2924e542635fea47ae546816ece", + "reference": "fadc83f7c41fb2924e542635fea47ae546816ece", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2016-10-03T07:43:09+00:00" + }, + { + "name": "sebastian/version", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2016-10-03T07:35:21+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "time": "2017-04-07T12:08:54+00:00" + }, + { + "name": "webmozart/assert", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "4a8bf11547e139e77b651365113fc12850c43d9a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/4a8bf11547e139e77b651365113fc12850c43d9a", + "reference": "4a8bf11547e139e77b651365113fc12850c43d9a", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2016-11-23T20:04:41+00:00" + } + ], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": { + "knplabs/github-api": 20, + "php-http/guzzle6-adapter": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/mass-rebuilder.php b/mass-rebuilder.php new file mode 100644 index 0000000..9be08ff --- /dev/null +++ b/mass-rebuilder.php @@ -0,0 +1,191 @@ +channel(); +$channel->basic_qos(null, 1, true); + + +list($queueName, , ) = $channel->queue_declare('mass-rebuild-checks', + false, true, false, false); +$channel->queue_bind($queueName, 'nixos/nixpkgs'); + +echo "hi\n"; + +function outrunner($msg) { + try { + $ret = runner($msg); + var_dump($ret); + if ($ret === true) { + echo "cool\n"; + echo "acking\n"; + $r = $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']); + var_dump($r); + echo "acked\n"; + } else { + echo "Not acking?\n"; + } + } catch (\GHE\ExecException $e) { + var_dump($msg); + var_dump($e->getMessage()); + var_dump($e->getCode()); + var_dump($e->getOutput()); + } +} + +function runner($msg) { + $in = json_decode($msg->body); + + try { + $etype = \GHE\EventClassifier::classifyEvent($in); + + if ($etype != "pull_request") { + echo "Skipping event type: $etype\n"; + return true; + } + } catch (\GHE\EventClassifierUnknownException $e) { + echo "Skipping unknown event type\n"; + print_r($in); + return true; + } + + if (!\GHE\ACL::isRepoEligible($in->repository->full_name)) { + echo "Repo not authorized (" . $in->repository->full_name . ")\n"; + return true; + } + + if ($in->pull_request->state != "open") { + echo "PR isn't open\n"; + return true; + } + + $ok_events = [ + 'created', + 'edited', + 'synchronize', + ]; + + if (!in_array($in->action, $ok_events)) { + echo "Uninteresting event " . $in->action . "\n"; + return true; + } + + $r = $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']); + var_dump($r); + echo "acked\n"; + + + $against_name = "origin/" . $in->pull_request->base->ref; + echo "Building against $against_name\n"; + $co = new GHE\Checkout("/home/grahamc/.nix-test", "mr-est"); + $pname = $co->checkOutRef($in->repository->full_name, + $in->repository->clone_url, + $in->number, + $against_name + ); + + $against = GHE\Exec::exec('git rev-parse %s', [$against_name]); + echo " $against_name is $against[0]\n"; + + try { + $co->applyPatches($pname, $in->pull_request->patch_url); + } catch (GHE\ExecException $e) { + echo "Received ExecException applying patches, likely due to conflicts:\n"; + var_dump($e->getCode()); + var_dump($e->getMessage()); + var_dump($e->getArgs()); + var_dump($e->getOutput()); + return false; + } + + $current = GHE\Exec::exec('git rev-parse HEAD'); + echo " currently at ${current[0]}\n"; + + + reply_to_issue($in, $against[0], $current[0]); + $msg->delivery_info['channel']->basic_cancel($msg->delivery_info['consumer_tag']); + return false; +} + +function reply_to_issue($issue, $prev, $current) { + $client = gh_client(); + + echo "current labels:\n"; + $already_there = $client->api('issue')->labels()->all( + $issue->repository->owner->login, + $issue->repository->name, + $issue->number); + $already_there = array_map(function($val) { return $val['name']; }, $already_there); + var_dump($already_there); + + $output = GHE\Exec::exec('$(nix-instantiate --eval -E %s) %s %s', + [ + '', + $prev, + $current + ] + ); + + $labels = []; + foreach ($output as $line) { + if (preg_match('/^\s*(\d+) (.*)$/', $line, $matches)) { + var_dump($matches); + # TODO: separate out the rebuild ranges from the rebuild platform and + # splice the string together, rather than this ugliness + if ($matches[1] > 500) { + if ($matches[2] == "x86_64-darwin") { + $labels[] = "10.rebuild-darwin: 501+"; + } else { + $labels[] = "10.rebuild-linux: 501+"; + } + } else if ($matches[1] > 100 && $matches[1] <= 500) { + if ($matches[2] == "x86_64-darwin") { + $labels[] = "10.rebuild-darwin: 101-500"; + } else { + $labels[] = "10.rebuild-linux: 101-500"; + } + } else if ($matches[1] > 10 && $matches[1] <= 100) { + if ($matches[2] == "x86_64-darwin") { + $labels[] = "10.rebuild-darwin: 11-100"; + } else { + $labels[] = "10.rebuild-linux: 11-100"; + } + } else if ($matches[1] <= 10) { + if ($matches[2] == "x86_64-darwin") { + $labels[] = "10.rebuild-darwin: 1-10"; + } else { + $labels[] = "10.rebuild-linux: 1-10"; + } + } + } + } + + + foreach ($labels as $label) { + if (in_array($label, $already_there)) { + echo "already labeled $label\n"; + + continue; + } else { + echo "will label +$label\n"; + } + + $client->api('issue')->labels()->add( + $issue->repository->owner->login, + $issue->repository->name, + $issue->number, + $label); + } +} + +$consumerTag = 'consumer' . getmypid(); +$channel->basic_consume($queueName, $consumerTag, false, true, false, false, 'outrunner'); +while(count($channel->callbacks)) { + $channel->wait(); +} + +echo "Bye\n"; \ No newline at end of file diff --git a/poster.php b/poster.php new file mode 100644 index 0000000..d2a5ad0 --- /dev/null +++ b/poster.php @@ -0,0 +1,68 @@ +channel(); +$channel->basic_qos(null, 1, true); + +list($queueName, , ) = $channel->queue_declare('build-results', + false, true, false, false); + +function runner($msg) { + $body = json_decode($msg->body); + $in = $body->payload; + + $num = $in->issue->number; + if ($body->success) { + echo "yay! $num passed!\n"; + } else { + echo "Yikes, $num failede\n"; + } + + reply_to_issue($in, implode("\n", $body->output), $body->success); + + var_dump($body->success); + + $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']); +} + +function reply_to_issue($issue, $output, $success) { + $client = gh_client(); + $pr = $client->api('pull_request')->show( + $issue->repository->owner->login, + $issue->repository->name, + $issue->issue->number + ); + $sha = $pr['head']['sha']; + + $client->api('pull_request')->reviews()->create( + $issue->repository->owner->login, + $issue->repository->name, + $issue->issue->number, + array( + 'body' => "```\n$output\n```", + 'event' => $success ? 'APPROVE' : 'COMMENT', + 'commit_id' => $sha, + )); +} + + +function outrunner($msg) { + try { + return runner($msg); + } catch (ExecException $e) { + var_dump($e->getMessage()); + var_dump($e->getCode()); + var_dump($e->getOutput()); + } +} + + +$consumerTag = 'consumer' . getmypid(); +$channel->basic_consume($queueName, $consumerTag, false, false, false, false, 'outrunner'); +while(count($channel->callbacks)) { + $channel->wait(); +} diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..e62a00b --- /dev/null +++ b/shell.nix @@ -0,0 +1,15 @@ +let + pkgs = import {}; + + inherit (pkgs) stdenv; + +in stdenv.mkDerivation rec { + name = "gh-event-forwarder"; + src = ./.; + buildInputs = with pkgs; [ + php + phpPackages.composer + ]; + + HISTFILE = "${src}/.bash_hist"; +} diff --git a/src/ACL.php b/src/ACL.php new file mode 100644 index 0000000..1f3cc49 --- /dev/null +++ b/src/ACL.php @@ -0,0 +1,37 @@ +root = $root; + $this->type = $type; + } + + function checkOutRef($repo_name, $clone_url, $id, $ref) { + $this->prefetchRepoCache($repo_name, $clone_url); + + $pname = $this->pathToRepoCache($repo_name); + $bname = $this->pathToBuildDir($repo_name, $id); + + $guard = $this->guard($bname); + if (!is_dir($bname)) { + echo "Cloning " . $id . " to $bname\n"; + Exec::exec('git clone --reference-if-able %s %s %s', + [ + $pname, + $clone_url, + $bname + ]); + } + + if (!chdir($bname)) { + throw new CoFailedException("Failed to chdir to $bname\n"); + } + + echo "fetching " . $id . " in $bname\n"; + Exec::exec('git fetch origin'); + try { + Exec::exec('git am --abort'); + } catch (ExecException $e) { + // non-zero exit if no am is in progress + } + Exec::exec('git reset --hard %s', [$ref]); + + + $this->release($guard); + + return $bname; + } + + function applyPatches($bname, $patch_url) { + if (!chdir($bname)) { + throw new CoFailedException("Failed to chdir to $bname\n"); + } + + $guard = $this->guard($pname); + Exec::exec('curl -L %s | git am --no-gpg-sign -', [$patch_url]); + $this->release($guard); + } + + function prefetchRepoCache($name, $clone_url) { + if (!chdir($this->root)) { + throw new CoFailedException("Failed to chdir to " . $this->root); + } + + $pname = $this->pathToRepoCache($name); + + $guard = $this->guard($pname); + + if (!is_dir($pname)) { + echo "Cloning " . $name . " to $pname\n"; + Exec::exec('git clone --bare %s %s', + [ + $clone_url, + $pname + ]); + } + + $this->release($guard); + + if (!chdir($pname)) { + throw new CoFailedException("Failed to chdir to $pname"); + } + + echo "Fetching $name to $pname\n"; + Exec::exec('git fetch origin'); + } + + function pathToRepoCache($name) { + return $this->root . "/repo-" . md5($name); + } + + function pathToBuildDir($repo, $id_number) { + $id = (int) $id_number; + $repo_hash = md5($repo); + $type = $this->type; + + return $this->root . "/$type-$repo_hash-$id"; + } + + function guard($path) { + $res = fopen("$path.lock", 'c'); + while (!flock($res, LOCK_EX)) { + echo "waiting for lock on $path...\n"; + sleep(1); + } + + return $res; + } + + function release($res) { + fclose($res); + } + +} + +class CoFailedException extends \Exception {} \ No newline at end of file diff --git a/src/EventClassifier.php b/src/EventClassifier.php new file mode 100644 index 0000000..9f3a722 --- /dev/null +++ b/src/EventClassifier.php @@ -0,0 +1,132 @@ +issue) + && !isset($payload->comment) + && isset($payload->action) + && in_array($payload->action, + [ "assigned", "unassigned", "labeled", + "unlabeled", "opened", "edited", + "milestoned", "demilestoned", "closed", + "reopened" ]); + } + + public static function isIssueComment($payload) { + return isset($payload->issue) + && isset($payload->comment) + && isset($payload->action) + && in_array($payload->action, + ['created', 'edited', 'deleted']); + } + + public static function isCommitComment($payload) { + return !isset($payload->issue) + && !isset($payload->pull_request) + && isset($payload->comment) + && isset($payload->action); + } + + public static function isPullRequestReviewComment($payload) { + return !isset($payload->issue) + && isset($payload->pull_request) + && isset($payload->comment) + && isset($payload->action) + && in_array($payload->action, + ['created', 'edited', 'deleted']); + } + + public static function isPullRequestReviewEvent($payload) { + return isset($payload->review) + && isset($payload->pull_request) + && isset($payload->action) + && in_array($payload->action, + ['submitted', 'edited', 'dismissed']); + } + + public static function isPullRequestEvent($payload) { + return isset($payload->number) + && isset($payload->pull_request) + && isset($payload->action) + && in_array($payload->action, + [ "assigned", "unassigned", + "review_requested", + "review_request_removed", "labeled", + "unlabeled", "opened", "edited", "closed", + "reopened", "synchronize" ]); + } + + public static function isStatusEvent($payload) { + return isset($payload->sha) + && isset($payload->commit) + && isset($payload->state) + && in_array($payload->state, + ['pending', 'success', 'failure', 'error']); + + } + + public static function isPushEvent($payload) { + return isset($payload->head_commit) + && isset($payload->commits) + && isset($payload->compare) + && isset($payload->forced); + } + + public static function isWatchEvent($payload) { + return isset($payload->action) + && $payload->action == "started"; + } + + public static function isForkEvent($payload) { + return isset($payload->forkee); + } + +} + +class EventClassifierUnknownException extends \Exception{}; \ No newline at end of file diff --git a/src/Exec.php b/src/Exec.php new file mode 100644 index 0000000..aa05bfa --- /dev/null +++ b/src/Exec.php @@ -0,0 +1,42 @@ +&1', + escapeshellarg($interiorCmd)); + + exec($exteriorCmd, $output, $return); + + if ($return > 0) { + throw new ExecException($cmd, $args, $output, $return); + } + + return $output; + } +} + +class ExecException extends \Exception { + protected $args; + protected $output; + + public function __construct($cmd, $args, $output, $return) { + $this->args = $args; + $this->output = $output; + + parent::__construct("Error calling $cmd", $return); + } + + public function getArgs() { + return $this->args; + } + + public function getOutput() { + return $this->output; + } + +} \ No newline at end of file diff --git a/src/NixBuild.php b/src/NixBuild.php new file mode 100644 index 0000000..ab2eb15 --- /dev/null +++ b/src/NixBuild.php @@ -0,0 +1,3 @@ +assertEquals( + ['oof'], + Exec::exec('echo foo | rev') + ); + } + + function testExecArgs() { + $this->assertEquals( + ['rab'], + Exec::exec('echo %s | rev', ['bar']) + ); + } + + function testExecArgsDangerous() { + $this->assertEquals( + ['$(whoami)'], + Exec::exec('echo %s', ['$(whoami)']) + ); + } + + function testExecFailureExceptions() { + $this->expectException(ExecException::class); + $this->expectExceptionCode(123); + $this->expectExceptionMessage("Error calling exit 123"); + Exec::exec('exit 123'); + } + + function testExecFailureExceptionsOutput() { + try { + Exec::exec('echo %s; exit %s', ["heya", 10]); + $this->assertFalse(true, "Should have excepted!"); + } catch (ExecException $e) { + $this->assertEquals(10, $e->getCode()); + $this->assertEquals(["heya", 10], $e->getArgs()); + $this->assertEquals(["heya"], $e->getOutput()); + } + } + + + function testExecFailureExceptionPipefailEnd() { + try { + var_dump(Exec::exec('echo "foo" | (exit 2);')); + $this->assertFalse(true, "Should have excepted!"); + } catch (ExecException $e) { + $this->assertEquals(2, $e->getCode()); + } + } + + function testExecFailureExceptionPipefailStart() { + try { + var_dump(Exec::exec('(echo "foo"; exit 3) | rev;')); + $this->assertFalse(true, "Should have excepted!"); + } catch (ExecException $e) { + $this->assertEquals(3, $e->getCode()); + } + } + +} \ No newline at end of file diff --git a/web/index.php b/web/index.php new file mode 100644 index 0000000..5435e52 --- /dev/null +++ b/web/index.php @@ -0,0 +1,27 @@ +channel(); + +$input = json_decode(file_get_contents('php://input'), true); +if (!isset($input['repository']['full_name'])) { + echo "no full_name set?"; + exit(0); +} + + +$name = strtolower($input['repository']['full_name']); +if (!GHE\ACL::isRepoEligible($name)) { + echo "repo not in ok name list"; + exit(1); +} + +$channel->exchange_declare($name, 'fanout', false, true, false); + +$message = new AMQPMessage(json_encode($input), + array('content_type' => 'application/json')); + +$channel->basic_publish($message, $name);