├── .circleci └── config.yml ├── .gitignore ├── Dockerfile ├── README.md ├── bin ├── container-start.sh └── wait.sh ├── composer.json ├── composer.lock ├── install └── composer.sh ├── phpunit.xml ├── rebuild.sh ├── test └── AppTest.php └── web ├── health.php └── index.php /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Build file for CD demo 2 | # 3 | # No real need for layer caching, this will be really quick to build 4 | 5 | # Some useful repeated stanzas 6 | 7 | docker-sign-in: &docker-sign-in 8 | name: Sign into Docker registry 9 | command: | 10 | docker login \ 11 | -u ${PROJECT_DOCKER_REGISTRY_USER} \ 12 | -p ${GITLAB_CDDEMO_REGISTRY_TOKEN} \ 13 | ${PROJECT_DOCKER_REGISTRY} 14 | 15 | only-deploy-tags: &only-deploy-tags 16 | filters: 17 | tags: 18 | only: /^deploy-.*/ 19 | branches: 20 | ignore: /.*/ 21 | 22 | common-env-vars: &common-env-vars 23 | PROJECT_DOCKER_REGISTRY: registry.gitlab.com 24 | PROJECT_DOCKER_REGISTRY_USER: halfercode 25 | PROJECT_DOCKER_IMAGE_URL: registry.gitlab.com/halfercode 26 | PROJECT_DOCKER_SERVICE_NAME: cd-demo 27 | PROJECT_DEPLOY_HOST: agnes.jondh.me.uk 28 | PROJECT_DEPLOY_USER: root 29 | 30 | # Here are the jobs 31 | 32 | version: 2 33 | jobs: 34 | build: 35 | working_directory: /app 36 | environment: *common-env-vars 37 | docker: 38 | - image: docker:17.05.0-ce-git 39 | steps: 40 | - checkout 41 | # This gets the local Docker service started 42 | - setup_remote_docker 43 | - run: *docker-sign-in 44 | - run: 45 | name: Build application Docker image 46 | command: | 47 | docker build \ 48 | --tag ${CIRCLE_PROJECT_REPONAME} \ 49 | . 50 | - run: 51 | name: Install test software 52 | command: | 53 | apk update 54 | apk --update add php7 openssl 55 | # Composer requirements 56 | apk --update add php7-openssl php7-json php7-mbstring php7-zlib php7-phar 57 | # PHPUnit requirements 58 | apk --update add php7-dom php7-mbstring 59 | # Give the PHP binary a nice name 60 | ln -s /usr/bin/php7 /usr/bin/php 61 | cd /app && sh /app/install/composer.sh 62 | php /app/composer.phar install 63 | - run: 64 | name: Start Docker container 65 | command: | 66 | docker run \ 67 | --detach \ 68 | --name ${CIRCLE_PROJECT_REPONAME} \ 69 | ${CIRCLE_PROJECT_REPONAME} 70 | docker ps 71 | - run: 72 | # Thanks to https://unix.stackexchange.com/a/5279. CircleCI's Docker 73 | # system does not allow external connections in a DinD set-up, so I am 74 | # reaching into the container to do that. 75 | name: Wait for container to be ready 76 | command: | 77 | docker exec -it ${CIRCLE_PROJECT_REPONAME} /app/bin/wait.sh 78 | - run: 79 | name: Run tests 80 | command: | 81 | /app/vendor/bin/phpunit 82 | - run: 83 | name: Stop Docker container 84 | command: | 85 | docker stop ${CIRCLE_PROJECT_REPONAME} 86 | - run: 87 | name: Push Docker image if a deploy tag is detected 88 | command: | 89 | case $CIRCLE_TAG in 90 | deploy*) 91 | docker tag ${CIRCLE_PROJECT_REPONAME} ${PROJECT_DOCKER_IMAGE_URL}/${CIRCLE_PROJECT_REPONAME}:${CIRCLE_TAG} 92 | docker push ${PROJECT_DOCKER_IMAGE_URL}/${CIRCLE_PROJECT_REPONAME}:${CIRCLE_TAG} 93 | ;; 94 | *) 95 | echo "No deploy tag, so no need to push" 96 | ;; 97 | esac 98 | 99 | deploy: 100 | working_directory: /app 101 | environment: *common-env-vars 102 | docker: 103 | - image: docker:17.05.0-ce-git 104 | steps: 105 | # Adds the keys from the UI so we can access a remote service 106 | - add_ssh_keys 107 | # This gets the local Docker service started 108 | - setup_remote_docker 109 | - run: 110 | name: Add the keys to the known hosts 111 | command: | 112 | ssh-keyscan -H ${PROJECT_DEPLOY_HOST} >> /root/.ssh/known_hosts 113 | - run: 114 | name: Pull Docker image onto deploy box 115 | command: | 116 | ssh ${PROJECT_DEPLOY_USER}@${PROJECT_DEPLOY_HOST} "docker pull ${PROJECT_DOCKER_IMAGE_URL}/${CIRCLE_PROJECT_REPONAME}:${CIRCLE_TAG}" 117 | - run: 118 | name: Do a rolling service update 119 | command: | 120 | # This should already be scaled to two instances 121 | # The 'uniq' suppresses a long stream of ncurses updates 122 | # (which the CI console doesn't cope with very well) 123 | ssh ${PROJECT_DEPLOY_USER}@${PROJECT_DEPLOY_HOST} \ 124 | "docker service update \ 125 | --env-add CD_DEMO_VERSION=${CIRCLE_TAG} \ 126 | --image=${PROJECT_DOCKER_IMAGE_URL}/${CIRCLE_PROJECT_REPONAME}:${CIRCLE_TAG} \ 127 | --with-registry-auth \ 128 | ${PROJECT_DOCKER_SERVICE_NAME} \ 129 | | uniq \ 130 | " 131 | workflows: 132 | version: 2 133 | # This runs on non-tag pushes 134 | untagged-build: 135 | jobs: 136 | - build 137 | # This only runs on deploy tags and not branches 138 | tagged-build: 139 | jobs: 140 | - build: *only-deploy-tags 141 | - deploy: 142 | <<: *only-deploy-tags 143 | requires: 144 | - build 145 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore IDE 2 | nbproject/ 3 | 4 | # Ignore dependencies 5 | vendor/ 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Docker build script for continuous deployment demo container 2 | 3 | FROM alpine:3.8 4 | 5 | # Install software 6 | RUN apk update 7 | RUN apk add php-cli php-apache2 8 | 9 | # Prep Apache 10 | RUN mkdir -p /run/apache2 11 | RUN echo "ServerName localhost" > /etc/apache2/conf.d/server-name.conf 12 | 13 | # Add dumb init to improve sig handling (stop time in CircleCI of 10sec is too slow) 14 | RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_amd64 15 | RUN chmod +x /usr/local/bin/dumb-init 16 | 17 | # Copy shell scripts 18 | COPY bin /app/bin 19 | RUN chmod -R +x /app/bin 20 | 21 | # Copy contents of a web dir 22 | RUN rm /var/www/localhost/htdocs/* 23 | COPY web/* /var/www/localhost/htdocs/ 24 | 25 | EXPOSE 80 26 | 27 | # The healthcheck is used by the Routing Mesh, during a rolling update, to understand 28 | # when to avoid a container that is not ready to receive HTTP traffic. 29 | HEALTHCHECK --interval=5s --timeout=5s --start-period=2s --retries=5 \ 30 | CMD wget -qO- http://localhost/health.php > /dev/null || exit 1 31 | 32 | # Start the web server 33 | ENTRYPOINT ["/usr/local/bin/dumb-init", "--"] 34 | CMD ["sh", "/app/bin/container-start.sh"] 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Continuous integration/deployment demo 2 | === 3 | 4 | [![CircleCI](https://circleci.com/gh/halfer/cd-demo-container.svg?style=svg)](https://circleci.com/gh/halfer/cd-demo-container) 5 | 6 | Introduction 7 | --- 8 | 9 | This is a simple demonstration repository to show how to use continuous integration and 10 | continuous deployment to deploy a web service with zero downtime. To do that, I am using 11 | some features in Docker Swarm to run multiple containers, such that during the short outage 12 | during a container upgrade, traffic is automatically routed to a healthy running instance. 13 | 14 | I am using these things: 15 | 16 | * PHP for scripting 17 | * The Apache web-server 18 | * GitHub for version control (obviously `;-)`) 19 | * Docker to containerise the app 20 | * Alpine Linux as a lightweight container OS 21 | * Docker Swarm to run multiple containers in a service 22 | * HTTP health-checks to help Docker maintain availability 23 | * CircleCI for integration 24 | * GitLab as a Docker image registry 25 | * Vultr as a cloud Linux host 26 | * Git tags as an auto-deployment trigger 27 | 28 | If you're wanting to get this project working, but want to change individual things, go 29 | right ahead. For example, you could use a different Git host or Docker registry 30 | without much bother. The only thing that would need much more work to swap is CircleCI, 31 | since the configuration file is specific to their system. 32 | 33 | How it works 34 | --- 35 | 36 | The heart of this project is a CircleCI configuration containing two jobs: one (`build`) 37 | for building and testing a Docker image, and the other (`deploy`) for deploying it 38 | automatically. When pushing code, the first job will run, and it will build the 39 | Dockerfile. If this works, it will try to start it and run a test on it. 40 | 41 | If the developer pushes a deploy tag (i.e. with a prefix of `deploy-`) then the first 42 | job will additionally tag the Docker image with the Git tag and will push it to 43 | the Docker image registry. If this is all successful, the second job will SSH to the 44 | remote machine, pull the image from the registry, and start a rolling Swarm update. 45 | 46 | In this example, the Swarm service is _always_ run using two replicas, so there 47 | is always one operational container to service incoming web requests. Docker Swarm 48 | uses a networking system called [Routing Mesh](https://docs.docker.com/engine/swarm/ingress/#using-the-routing-mesh) 49 | together with the custom health-checks to determine how to update internal networking 50 | rules during a rolling update. 51 | 52 | Target audience 53 | --- 54 | 55 | This material is not suitable for beginners. A basic understanding of the Linux 56 | command line, containers and general hosting concepts are pretty much essential. 57 | 58 | Preparation 59 | --- 60 | 61 | To follow this tutorial, sign up for the following: 62 | 63 | * GitHub 64 | * Pretty much any VPS host 65 | * GitLab 66 | * CircleCI 67 | 68 | The VPS host will generally cost some money. If you can go for a cloud provider they 69 | will charge very accurately, and as long as you destroy the machine when you are 70 | finished, you'll incur very low costs (less than one US dollar). I use Vultr, but 71 | there's also Digital Ocean, Linode and many others available. 72 | 73 | General steps 74 | --- 75 | 76 | Experienced users may wish to just follow these guideline steps. They are expanded 77 | upon later if extra guidance is required. 78 | 79 | * Fork this repo 80 | * Create a small server in the VPS host 81 | * Create an empty project in GitLab (using the same name as the GitHub project) 82 | * Connect CircleCI to GitHub and start building the project 83 | * Customise the env vars in `config.yml` and push 84 | * Install Docker in the remote server 85 | * Create a registry token for CircleCI to do the image push 86 | * Login to the Docker registry in the remote server 87 | * Generate deployment keys 88 | * Initialise a Docker swarm (`docker swarm init`) 89 | * Start a Docker service in the remote 90 | * Add a Git deployment tag and push that tag to kick off an automatic deployment 91 | 92 | Optional steps: 93 | 94 | * Obtain a domain/sub-domain and point it to the server IP 95 | * Add other Docker machines into the Swarm (`docker swarm join`) 96 | 97 | Expanded steps 98 | --- 99 | 100 | ### Fork the repo 101 | 102 | The project name can be anything, but it may be easier to stick with 103 | `cd-demo-container`. 104 | 105 | To use CircleCI, you _have_ to use either GitHub or BitBucket, at the time of writing. 106 | Although you can checkout from any Git repo, I don't think CircleCI will monitor code 107 | pushes on anything other than these two. 108 | 109 | ### Create a small server 110 | 111 | I use a machine with 1G of RAM but you might get away with less than that. I use Ubuntu 112 | as it is popular and widely supported, but if you prefer something else (Fedora, Centos, 113 | Debian, etc) then go for it. 114 | 115 | If you're using Ubuntu, use the most recent stable Long Term Support version. 116 | 117 | ### Create an empty project in GitLab 118 | 119 | Your project URL will be `https://gitlab.com/username/cd-demo-container`, with the 120 | appropriate swap for the username. 121 | 122 | ### Customise the env vars 123 | 124 | These vars will need to be changed in `config.yml`: 125 | 126 | PROJECT_DOCKER_REGISTRY_USER: halfercode 127 | PROJECT_DOCKER_IMAGE_URL: registry.gitlab.com/halfercode 128 | PROJECT_DEPLOY_HOST: agnes.jondh.me.uk 129 | 130 | You can probably leave these be (but change them if you know what you are doing): 131 | 132 | PROJECT_DOCKER_REGISTRY: registry.gitlab.com 133 | PROJECT_DOCKER_SERVICE_NAME: cd-demo 134 | PROJECT_DEPLOY_USER: root 135 | 136 | ### Install Docker in the remote server 137 | 138 | For Ubuntu, I like to use the [Docker repository](https://download.docker.com/linux/ubuntu), 139 | since it is more up to date than the standard OS version, but use what works for you. Here 140 | are [the installation notes](https://docs.docker.com/install/linux/docker-ce/ubuntu/). 141 | 142 | ### Create a registry token for CircleCI to do the image push 143 | 144 | Generate an [access token in GitLab](https://gitlab.com/profile/personal_access_tokens) 145 | with the "api" permission. I believe that's the only permission with write access, 146 | and unfortunately is not per-project. You can choose the name for the token, I suggest 147 | "cd-demo-write". If you have high-value projects in GitLab, consider using a shorter 148 | expiry period, or perhaps even sign up for a separate account. 149 | 150 | Then store the generated token somewhere safe, such as a password keeper. 151 | 152 | Go to your project in CircleCI, click the cog icon, go to the "Environment Variables" 153 | section, and create a new key called `GITLAB_CDDEMO_REGISTRY_TOKEN`. Paste in the new 154 | token as the value of this variable. 155 | 156 | This will be used by the `docker login` command in the CircleCI config to obtain 157 | write access to your registry. 158 | 159 | ### Login to the Docker registry in the remote server 160 | 161 | Generate an [access token in GitLab](https://gitlab.com/profile/personal_access_tokens) 162 | with the "read_registry" permission. This is a read-only permission and will be used 163 | to pull images on your remote server. You can choose the name for the token, I suggest 164 | "cd-demo-read-only". It's worth noting that while you can reuse your read/write 165 | token here, it is better from a security perspective to only give the permissions 166 | that are actually needed. 167 | 168 | As before, it is a good idea to store the generated token somewhere safe, such as a 169 | password keeper. 170 | 171 | Finally, get a remote shell on the deployment target and enter the token in response to 172 | a login operation: 173 | 174 | docker login --username halfercode registry.gitlab.com 175 | 176 | A hash of your credentials will be stored in `~/.docker` and thus will survive 177 | a system reboot. 178 | 179 | ### Generate deployment keys 180 | 181 | * Generate a SSH key pair 182 | * Add the private key to CircleCI 183 | * Add the public key to the deploy target using `ssh-copy-id` 184 | 185 | I have more details about this [in this blog post](https://blog.jondh.me.uk/2018/08/creating-a-vps-deploy-key-for-a-circleci-build/). 186 | 187 | ### Start a Docker service in the remote 188 | 189 | After a Swarm has been initialised, run this on the manager node (of course, with 190 | suitable replacements for the repo URL and container name etc): 191 | 192 | # Get latest tag from the Git repo 193 | # (this lists available tags, removes the non-numeric prefix, sorts the 194 | # numbers mathematically, gets the largest one, then adds the prefix back) 195 | DEPLOY_VERSION=`git ls-remote --tags --refs \ 196 | git@github.com:halfer/cd-demo-container.git \ 197 | | grep deploy- \ 198 | | cut -f 2 \ 199 | | sed "s/refs\/tags\/deploy-v//" \ 200 | | sort --general-numeric-sort --reverse \ 201 | | head -n 1` 202 | DEPLOY_TAG=deploy-v${DEPLOY_VERSION} 203 | 204 | # Start service (if the image is not already pulled, this will do it for you) 205 | docker service create \ 206 | --name cd-demo \ 207 | --env CD_DEMO_VERSION=${DEPLOY_TAG} \ 208 | --replicas 2 \ 209 | --with-registry-auth \ 210 | --publish 80:80 \ 211 | registry.gitlab.com/halfercode/cd-demo-container:${DEPLOY_TAG} 212 | 213 | Docker Swarm will restart your service(s) automatically if you reboot the host. Try that 214 | now, to make sure it works for you. 215 | 216 | ### Add a Git deployment tag 217 | 218 | The configuration is designed to run the `build` job on an ordinary code push, and 219 | to run both the `build` and `deploy` jobs on a tag push. 220 | 221 | To add a tag, you can use: 222 | 223 | git tag -a deploy-v1 224 | 225 | To push both commits and tags, you can use: 226 | 227 | git push --follow-tags 228 | 229 | Running instance 230 | --- 231 | 232 | I have a [running instance](https://cd-demo.jondh.me.uk/) of this service. This one 233 | is a bit different because, obviously, it runs via a secure site. This is achieved 234 | by adding a [Traefik](https://traefik.io/) front-end proxy in front of the demo 235 | container. 236 | 237 | Other resources 238 | --- 239 | 240 | To achieve zero-downtime continuous deployment, I used the technique shown in 241 | [this video](https://www.youtube.com/watch?v=dLBGoaMz7dQ); I recommend watching it, 242 | as it is very well presented. 243 | 244 | License 245 | --- 246 | 247 | This material is licensed under the [MIT License](https://opensource.org/licenses/MIT), 248 | which means you can pretty much do anything you like with it. 249 | -------------------------------------------------------------------------------- /bin/container-start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # This script is the default CMD in the Dockerfile 4 | 5 | # Generate a GUID for this server: 6 | php -r "echo hash('sha256', uniqid());" > /var/www/localhost/htdocs/guid.txt 7 | chown apache /var/www/localhost/htdocs/guid.txt 8 | 9 | # Start web server 10 | /usr/sbin/httpd -DFOREGROUND 11 | -------------------------------------------------------------------------------- /bin/wait.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | timeout -t 5 sh -c 'while ! echo exit | nc localhost 80; do sleep 0.5; done' 4 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require-dev": { 3 | "phpunit/phpunit": "^6" 4 | } 5 | } 6 | 7 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "2bf7d4a2da3e1a4acc9973c9fe028094", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "doctrine/instantiator", 12 | "version": "1.0.5", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/doctrine/instantiator.git", 16 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", 21 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": ">=5.3,<8.0-DEV" 26 | }, 27 | "require-dev": { 28 | "athletic/athletic": "~0.1.8", 29 | "ext-pdo": "*", 30 | "ext-phar": "*", 31 | "phpunit/phpunit": "~4.0", 32 | "squizlabs/php_codesniffer": "~2.0" 33 | }, 34 | "type": "library", 35 | "extra": { 36 | "branch-alias": { 37 | "dev-master": "1.0.x-dev" 38 | } 39 | }, 40 | "autoload": { 41 | "psr-4": { 42 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 43 | } 44 | }, 45 | "notification-url": "https://packagist.org/downloads/", 46 | "license": [ 47 | "MIT" 48 | ], 49 | "authors": [ 50 | { 51 | "name": "Marco Pivetta", 52 | "email": "ocramius@gmail.com", 53 | "homepage": "http://ocramius.github.com/" 54 | } 55 | ], 56 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 57 | "homepage": "https://github.com/doctrine/instantiator", 58 | "keywords": [ 59 | "constructor", 60 | "instantiate" 61 | ], 62 | "time": "2015-06-14T21:17:01+00:00" 63 | }, 64 | { 65 | "name": "myclabs/deep-copy", 66 | "version": "1.7.0", 67 | "source": { 68 | "type": "git", 69 | "url": "https://github.com/myclabs/DeepCopy.git", 70 | "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" 71 | }, 72 | "dist": { 73 | "type": "zip", 74 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", 75 | "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", 76 | "shasum": "" 77 | }, 78 | "require": { 79 | "php": "^5.6 || ^7.0" 80 | }, 81 | "require-dev": { 82 | "doctrine/collections": "^1.0", 83 | "doctrine/common": "^2.6", 84 | "phpunit/phpunit": "^4.1" 85 | }, 86 | "type": "library", 87 | "autoload": { 88 | "psr-4": { 89 | "DeepCopy\\": "src/DeepCopy/" 90 | }, 91 | "files": [ 92 | "src/DeepCopy/deep_copy.php" 93 | ] 94 | }, 95 | "notification-url": "https://packagist.org/downloads/", 96 | "license": [ 97 | "MIT" 98 | ], 99 | "description": "Create deep copies (clones) of your objects", 100 | "keywords": [ 101 | "clone", 102 | "copy", 103 | "duplicate", 104 | "object", 105 | "object graph" 106 | ], 107 | "time": "2017-10-19T19:58:43+00:00" 108 | }, 109 | { 110 | "name": "phar-io/manifest", 111 | "version": "1.0.1", 112 | "source": { 113 | "type": "git", 114 | "url": "https://github.com/phar-io/manifest.git", 115 | "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" 116 | }, 117 | "dist": { 118 | "type": "zip", 119 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", 120 | "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", 121 | "shasum": "" 122 | }, 123 | "require": { 124 | "ext-dom": "*", 125 | "ext-phar": "*", 126 | "phar-io/version": "^1.0.1", 127 | "php": "^5.6 || ^7.0" 128 | }, 129 | "type": "library", 130 | "extra": { 131 | "branch-alias": { 132 | "dev-master": "1.0.x-dev" 133 | } 134 | }, 135 | "autoload": { 136 | "classmap": [ 137 | "src/" 138 | ] 139 | }, 140 | "notification-url": "https://packagist.org/downloads/", 141 | "license": [ 142 | "BSD-3-Clause" 143 | ], 144 | "authors": [ 145 | { 146 | "name": "Arne Blankerts", 147 | "email": "arne@blankerts.de", 148 | "role": "Developer" 149 | }, 150 | { 151 | "name": "Sebastian Heuer", 152 | "email": "sebastian@phpeople.de", 153 | "role": "Developer" 154 | }, 155 | { 156 | "name": "Sebastian Bergmann", 157 | "email": "sebastian@phpunit.de", 158 | "role": "Developer" 159 | } 160 | ], 161 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 162 | "time": "2017-03-05T18:14:27+00:00" 163 | }, 164 | { 165 | "name": "phar-io/version", 166 | "version": "1.0.1", 167 | "source": { 168 | "type": "git", 169 | "url": "https://github.com/phar-io/version.git", 170 | "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" 171 | }, 172 | "dist": { 173 | "type": "zip", 174 | "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", 175 | "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", 176 | "shasum": "" 177 | }, 178 | "require": { 179 | "php": "^5.6 || ^7.0" 180 | }, 181 | "type": "library", 182 | "autoload": { 183 | "classmap": [ 184 | "src/" 185 | ] 186 | }, 187 | "notification-url": "https://packagist.org/downloads/", 188 | "license": [ 189 | "BSD-3-Clause" 190 | ], 191 | "authors": [ 192 | { 193 | "name": "Arne Blankerts", 194 | "email": "arne@blankerts.de", 195 | "role": "Developer" 196 | }, 197 | { 198 | "name": "Sebastian Heuer", 199 | "email": "sebastian@phpeople.de", 200 | "role": "Developer" 201 | }, 202 | { 203 | "name": "Sebastian Bergmann", 204 | "email": "sebastian@phpunit.de", 205 | "role": "Developer" 206 | } 207 | ], 208 | "description": "Library for handling version information and constraints", 209 | "time": "2017-03-05T17:38:23+00:00" 210 | }, 211 | { 212 | "name": "phpdocumentor/reflection-common", 213 | "version": "1.0.1", 214 | "source": { 215 | "type": "git", 216 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 217 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" 218 | }, 219 | "dist": { 220 | "type": "zip", 221 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", 222 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", 223 | "shasum": "" 224 | }, 225 | "require": { 226 | "php": ">=5.5" 227 | }, 228 | "require-dev": { 229 | "phpunit/phpunit": "^4.6" 230 | }, 231 | "type": "library", 232 | "extra": { 233 | "branch-alias": { 234 | "dev-master": "1.0.x-dev" 235 | } 236 | }, 237 | "autoload": { 238 | "psr-4": { 239 | "phpDocumentor\\Reflection\\": [ 240 | "src" 241 | ] 242 | } 243 | }, 244 | "notification-url": "https://packagist.org/downloads/", 245 | "license": [ 246 | "MIT" 247 | ], 248 | "authors": [ 249 | { 250 | "name": "Jaap van Otterdijk", 251 | "email": "opensource@ijaap.nl" 252 | } 253 | ], 254 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure", 255 | "homepage": "http://www.phpdoc.org", 256 | "keywords": [ 257 | "FQSEN", 258 | "phpDocumentor", 259 | "phpdoc", 260 | "reflection", 261 | "static analysis" 262 | ], 263 | "time": "2017-09-11T18:02:19+00:00" 264 | }, 265 | { 266 | "name": "phpdocumentor/reflection-docblock", 267 | "version": "4.3.0", 268 | "source": { 269 | "type": "git", 270 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 271 | "reference": "94fd0001232e47129dd3504189fa1c7225010d08" 272 | }, 273 | "dist": { 274 | "type": "zip", 275 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", 276 | "reference": "94fd0001232e47129dd3504189fa1c7225010d08", 277 | "shasum": "" 278 | }, 279 | "require": { 280 | "php": "^7.0", 281 | "phpdocumentor/reflection-common": "^1.0.0", 282 | "phpdocumentor/type-resolver": "^0.4.0", 283 | "webmozart/assert": "^1.0" 284 | }, 285 | "require-dev": { 286 | "doctrine/instantiator": "~1.0.5", 287 | "mockery/mockery": "^1.0", 288 | "phpunit/phpunit": "^6.4" 289 | }, 290 | "type": "library", 291 | "extra": { 292 | "branch-alias": { 293 | "dev-master": "4.x-dev" 294 | } 295 | }, 296 | "autoload": { 297 | "psr-4": { 298 | "phpDocumentor\\Reflection\\": [ 299 | "src/" 300 | ] 301 | } 302 | }, 303 | "notification-url": "https://packagist.org/downloads/", 304 | "license": [ 305 | "MIT" 306 | ], 307 | "authors": [ 308 | { 309 | "name": "Mike van Riel", 310 | "email": "me@mikevanriel.com" 311 | } 312 | ], 313 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 314 | "time": "2017-11-30T07:14:17+00:00" 315 | }, 316 | { 317 | "name": "phpdocumentor/type-resolver", 318 | "version": "0.4.0", 319 | "source": { 320 | "type": "git", 321 | "url": "https://github.com/phpDocumentor/TypeResolver.git", 322 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" 323 | }, 324 | "dist": { 325 | "type": "zip", 326 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", 327 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", 328 | "shasum": "" 329 | }, 330 | "require": { 331 | "php": "^5.5 || ^7.0", 332 | "phpdocumentor/reflection-common": "^1.0" 333 | }, 334 | "require-dev": { 335 | "mockery/mockery": "^0.9.4", 336 | "phpunit/phpunit": "^5.2||^4.8.24" 337 | }, 338 | "type": "library", 339 | "extra": { 340 | "branch-alias": { 341 | "dev-master": "1.0.x-dev" 342 | } 343 | }, 344 | "autoload": { 345 | "psr-4": { 346 | "phpDocumentor\\Reflection\\": [ 347 | "src/" 348 | ] 349 | } 350 | }, 351 | "notification-url": "https://packagist.org/downloads/", 352 | "license": [ 353 | "MIT" 354 | ], 355 | "authors": [ 356 | { 357 | "name": "Mike van Riel", 358 | "email": "me@mikevanriel.com" 359 | } 360 | ], 361 | "time": "2017-07-14T14:27:02+00:00" 362 | }, 363 | { 364 | "name": "phpspec/prophecy", 365 | "version": "1.8.0", 366 | "source": { 367 | "type": "git", 368 | "url": "https://github.com/phpspec/prophecy.git", 369 | "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" 370 | }, 371 | "dist": { 372 | "type": "zip", 373 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", 374 | "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", 375 | "shasum": "" 376 | }, 377 | "require": { 378 | "doctrine/instantiator": "^1.0.2", 379 | "php": "^5.3|^7.0", 380 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", 381 | "sebastian/comparator": "^1.1|^2.0|^3.0", 382 | "sebastian/recursion-context": "^1.0|^2.0|^3.0" 383 | }, 384 | "require-dev": { 385 | "phpspec/phpspec": "^2.5|^3.2", 386 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" 387 | }, 388 | "type": "library", 389 | "extra": { 390 | "branch-alias": { 391 | "dev-master": "1.8.x-dev" 392 | } 393 | }, 394 | "autoload": { 395 | "psr-0": { 396 | "Prophecy\\": "src/" 397 | } 398 | }, 399 | "notification-url": "https://packagist.org/downloads/", 400 | "license": [ 401 | "MIT" 402 | ], 403 | "authors": [ 404 | { 405 | "name": "Konstantin Kudryashov", 406 | "email": "ever.zet@gmail.com", 407 | "homepage": "http://everzet.com" 408 | }, 409 | { 410 | "name": "Marcello Duarte", 411 | "email": "marcello.duarte@gmail.com" 412 | } 413 | ], 414 | "description": "Highly opinionated mocking framework for PHP 5.3+", 415 | "homepage": "https://github.com/phpspec/prophecy", 416 | "keywords": [ 417 | "Double", 418 | "Dummy", 419 | "fake", 420 | "mock", 421 | "spy", 422 | "stub" 423 | ], 424 | "time": "2018-08-05T17:53:17+00:00" 425 | }, 426 | { 427 | "name": "phpunit/php-code-coverage", 428 | "version": "5.3.2", 429 | "source": { 430 | "type": "git", 431 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 432 | "reference": "c89677919c5dd6d3b3852f230a663118762218ac" 433 | }, 434 | "dist": { 435 | "type": "zip", 436 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac", 437 | "reference": "c89677919c5dd6d3b3852f230a663118762218ac", 438 | "shasum": "" 439 | }, 440 | "require": { 441 | "ext-dom": "*", 442 | "ext-xmlwriter": "*", 443 | "php": "^7.0", 444 | "phpunit/php-file-iterator": "^1.4.2", 445 | "phpunit/php-text-template": "^1.2.1", 446 | "phpunit/php-token-stream": "^2.0.1", 447 | "sebastian/code-unit-reverse-lookup": "^1.0.1", 448 | "sebastian/environment": "^3.0", 449 | "sebastian/version": "^2.0.1", 450 | "theseer/tokenizer": "^1.1" 451 | }, 452 | "require-dev": { 453 | "phpunit/phpunit": "^6.0" 454 | }, 455 | "suggest": { 456 | "ext-xdebug": "^2.5.5" 457 | }, 458 | "type": "library", 459 | "extra": { 460 | "branch-alias": { 461 | "dev-master": "5.3.x-dev" 462 | } 463 | }, 464 | "autoload": { 465 | "classmap": [ 466 | "src/" 467 | ] 468 | }, 469 | "notification-url": "https://packagist.org/downloads/", 470 | "license": [ 471 | "BSD-3-Clause" 472 | ], 473 | "authors": [ 474 | { 475 | "name": "Sebastian Bergmann", 476 | "email": "sebastian@phpunit.de", 477 | "role": "lead" 478 | } 479 | ], 480 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 481 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 482 | "keywords": [ 483 | "coverage", 484 | "testing", 485 | "xunit" 486 | ], 487 | "time": "2018-04-06T15:36:58+00:00" 488 | }, 489 | { 490 | "name": "phpunit/php-file-iterator", 491 | "version": "1.4.5", 492 | "source": { 493 | "type": "git", 494 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 495 | "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" 496 | }, 497 | "dist": { 498 | "type": "zip", 499 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", 500 | "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", 501 | "shasum": "" 502 | }, 503 | "require": { 504 | "php": ">=5.3.3" 505 | }, 506 | "type": "library", 507 | "extra": { 508 | "branch-alias": { 509 | "dev-master": "1.4.x-dev" 510 | } 511 | }, 512 | "autoload": { 513 | "classmap": [ 514 | "src/" 515 | ] 516 | }, 517 | "notification-url": "https://packagist.org/downloads/", 518 | "license": [ 519 | "BSD-3-Clause" 520 | ], 521 | "authors": [ 522 | { 523 | "name": "Sebastian Bergmann", 524 | "email": "sb@sebastian-bergmann.de", 525 | "role": "lead" 526 | } 527 | ], 528 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 529 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 530 | "keywords": [ 531 | "filesystem", 532 | "iterator" 533 | ], 534 | "time": "2017-11-27T13:52:08+00:00" 535 | }, 536 | { 537 | "name": "phpunit/php-text-template", 538 | "version": "1.2.1", 539 | "source": { 540 | "type": "git", 541 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 542 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 543 | }, 544 | "dist": { 545 | "type": "zip", 546 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 547 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 548 | "shasum": "" 549 | }, 550 | "require": { 551 | "php": ">=5.3.3" 552 | }, 553 | "type": "library", 554 | "autoload": { 555 | "classmap": [ 556 | "src/" 557 | ] 558 | }, 559 | "notification-url": "https://packagist.org/downloads/", 560 | "license": [ 561 | "BSD-3-Clause" 562 | ], 563 | "authors": [ 564 | { 565 | "name": "Sebastian Bergmann", 566 | "email": "sebastian@phpunit.de", 567 | "role": "lead" 568 | } 569 | ], 570 | "description": "Simple template engine.", 571 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 572 | "keywords": [ 573 | "template" 574 | ], 575 | "time": "2015-06-21T13:50:34+00:00" 576 | }, 577 | { 578 | "name": "phpunit/php-timer", 579 | "version": "1.0.9", 580 | "source": { 581 | "type": "git", 582 | "url": "https://github.com/sebastianbergmann/php-timer.git", 583 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" 584 | }, 585 | "dist": { 586 | "type": "zip", 587 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", 588 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", 589 | "shasum": "" 590 | }, 591 | "require": { 592 | "php": "^5.3.3 || ^7.0" 593 | }, 594 | "require-dev": { 595 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" 596 | }, 597 | "type": "library", 598 | "extra": { 599 | "branch-alias": { 600 | "dev-master": "1.0-dev" 601 | } 602 | }, 603 | "autoload": { 604 | "classmap": [ 605 | "src/" 606 | ] 607 | }, 608 | "notification-url": "https://packagist.org/downloads/", 609 | "license": [ 610 | "BSD-3-Clause" 611 | ], 612 | "authors": [ 613 | { 614 | "name": "Sebastian Bergmann", 615 | "email": "sb@sebastian-bergmann.de", 616 | "role": "lead" 617 | } 618 | ], 619 | "description": "Utility class for timing", 620 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 621 | "keywords": [ 622 | "timer" 623 | ], 624 | "time": "2017-02-26T11:10:40+00:00" 625 | }, 626 | { 627 | "name": "phpunit/php-token-stream", 628 | "version": "2.0.2", 629 | "source": { 630 | "type": "git", 631 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 632 | "reference": "791198a2c6254db10131eecfe8c06670700904db" 633 | }, 634 | "dist": { 635 | "type": "zip", 636 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", 637 | "reference": "791198a2c6254db10131eecfe8c06670700904db", 638 | "shasum": "" 639 | }, 640 | "require": { 641 | "ext-tokenizer": "*", 642 | "php": "^7.0" 643 | }, 644 | "require-dev": { 645 | "phpunit/phpunit": "^6.2.4" 646 | }, 647 | "type": "library", 648 | "extra": { 649 | "branch-alias": { 650 | "dev-master": "2.0-dev" 651 | } 652 | }, 653 | "autoload": { 654 | "classmap": [ 655 | "src/" 656 | ] 657 | }, 658 | "notification-url": "https://packagist.org/downloads/", 659 | "license": [ 660 | "BSD-3-Clause" 661 | ], 662 | "authors": [ 663 | { 664 | "name": "Sebastian Bergmann", 665 | "email": "sebastian@phpunit.de" 666 | } 667 | ], 668 | "description": "Wrapper around PHP's tokenizer extension.", 669 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 670 | "keywords": [ 671 | "tokenizer" 672 | ], 673 | "time": "2017-11-27T05:48:46+00:00" 674 | }, 675 | { 676 | "name": "phpunit/phpunit", 677 | "version": "6.5.12", 678 | "source": { 679 | "type": "git", 680 | "url": "https://github.com/sebastianbergmann/phpunit.git", 681 | "reference": "24da433d7384824d65ea93fbb462e2f31bbb494e" 682 | }, 683 | "dist": { 684 | "type": "zip", 685 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/24da433d7384824d65ea93fbb462e2f31bbb494e", 686 | "reference": "24da433d7384824d65ea93fbb462e2f31bbb494e", 687 | "shasum": "" 688 | }, 689 | "require": { 690 | "ext-dom": "*", 691 | "ext-json": "*", 692 | "ext-libxml": "*", 693 | "ext-mbstring": "*", 694 | "ext-xml": "*", 695 | "myclabs/deep-copy": "^1.6.1", 696 | "phar-io/manifest": "^1.0.1", 697 | "phar-io/version": "^1.0", 698 | "php": "^7.0", 699 | "phpspec/prophecy": "^1.7", 700 | "phpunit/php-code-coverage": "^5.3", 701 | "phpunit/php-file-iterator": "^1.4.3", 702 | "phpunit/php-text-template": "^1.2.1", 703 | "phpunit/php-timer": "^1.0.9", 704 | "phpunit/phpunit-mock-objects": "^5.0.9", 705 | "sebastian/comparator": "^2.1", 706 | "sebastian/diff": "^2.0", 707 | "sebastian/environment": "^3.1", 708 | "sebastian/exporter": "^3.1", 709 | "sebastian/global-state": "^2.0", 710 | "sebastian/object-enumerator": "^3.0.3", 711 | "sebastian/resource-operations": "^1.0", 712 | "sebastian/version": "^2.0.1" 713 | }, 714 | "conflict": { 715 | "phpdocumentor/reflection-docblock": "3.0.2", 716 | "phpunit/dbunit": "<3.0" 717 | }, 718 | "require-dev": { 719 | "ext-pdo": "*" 720 | }, 721 | "suggest": { 722 | "ext-xdebug": "*", 723 | "phpunit/php-invoker": "^1.1" 724 | }, 725 | "bin": [ 726 | "phpunit" 727 | ], 728 | "type": "library", 729 | "extra": { 730 | "branch-alias": { 731 | "dev-master": "6.5.x-dev" 732 | } 733 | }, 734 | "autoload": { 735 | "classmap": [ 736 | "src/" 737 | ] 738 | }, 739 | "notification-url": "https://packagist.org/downloads/", 740 | "license": [ 741 | "BSD-3-Clause" 742 | ], 743 | "authors": [ 744 | { 745 | "name": "Sebastian Bergmann", 746 | "email": "sebastian@phpunit.de", 747 | "role": "lead" 748 | } 749 | ], 750 | "description": "The PHP Unit Testing framework.", 751 | "homepage": "https://phpunit.de/", 752 | "keywords": [ 753 | "phpunit", 754 | "testing", 755 | "xunit" 756 | ], 757 | "time": "2018-08-22T06:32:48+00:00" 758 | }, 759 | { 760 | "name": "phpunit/phpunit-mock-objects", 761 | "version": "5.0.10", 762 | "source": { 763 | "type": "git", 764 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 765 | "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f" 766 | }, 767 | "dist": { 768 | "type": "zip", 769 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f", 770 | "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f", 771 | "shasum": "" 772 | }, 773 | "require": { 774 | "doctrine/instantiator": "^1.0.5", 775 | "php": "^7.0", 776 | "phpunit/php-text-template": "^1.2.1", 777 | "sebastian/exporter": "^3.1" 778 | }, 779 | "conflict": { 780 | "phpunit/phpunit": "<6.0" 781 | }, 782 | "require-dev": { 783 | "phpunit/phpunit": "^6.5.11" 784 | }, 785 | "suggest": { 786 | "ext-soap": "*" 787 | }, 788 | "type": "library", 789 | "extra": { 790 | "branch-alias": { 791 | "dev-master": "5.0.x-dev" 792 | } 793 | }, 794 | "autoload": { 795 | "classmap": [ 796 | "src/" 797 | ] 798 | }, 799 | "notification-url": "https://packagist.org/downloads/", 800 | "license": [ 801 | "BSD-3-Clause" 802 | ], 803 | "authors": [ 804 | { 805 | "name": "Sebastian Bergmann", 806 | "email": "sebastian@phpunit.de", 807 | "role": "lead" 808 | } 809 | ], 810 | "description": "Mock Object library for PHPUnit", 811 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 812 | "keywords": [ 813 | "mock", 814 | "xunit" 815 | ], 816 | "time": "2018-08-09T05:50:03+00:00" 817 | }, 818 | { 819 | "name": "sebastian/code-unit-reverse-lookup", 820 | "version": "1.0.1", 821 | "source": { 822 | "type": "git", 823 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 824 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" 825 | }, 826 | "dist": { 827 | "type": "zip", 828 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 829 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 830 | "shasum": "" 831 | }, 832 | "require": { 833 | "php": "^5.6 || ^7.0" 834 | }, 835 | "require-dev": { 836 | "phpunit/phpunit": "^5.7 || ^6.0" 837 | }, 838 | "type": "library", 839 | "extra": { 840 | "branch-alias": { 841 | "dev-master": "1.0.x-dev" 842 | } 843 | }, 844 | "autoload": { 845 | "classmap": [ 846 | "src/" 847 | ] 848 | }, 849 | "notification-url": "https://packagist.org/downloads/", 850 | "license": [ 851 | "BSD-3-Clause" 852 | ], 853 | "authors": [ 854 | { 855 | "name": "Sebastian Bergmann", 856 | "email": "sebastian@phpunit.de" 857 | } 858 | ], 859 | "description": "Looks up which function or method a line of code belongs to", 860 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 861 | "time": "2017-03-04T06:30:41+00:00" 862 | }, 863 | { 864 | "name": "sebastian/comparator", 865 | "version": "2.1.3", 866 | "source": { 867 | "type": "git", 868 | "url": "https://github.com/sebastianbergmann/comparator.git", 869 | "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" 870 | }, 871 | "dist": { 872 | "type": "zip", 873 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", 874 | "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", 875 | "shasum": "" 876 | }, 877 | "require": { 878 | "php": "^7.0", 879 | "sebastian/diff": "^2.0 || ^3.0", 880 | "sebastian/exporter": "^3.1" 881 | }, 882 | "require-dev": { 883 | "phpunit/phpunit": "^6.4" 884 | }, 885 | "type": "library", 886 | "extra": { 887 | "branch-alias": { 888 | "dev-master": "2.1.x-dev" 889 | } 890 | }, 891 | "autoload": { 892 | "classmap": [ 893 | "src/" 894 | ] 895 | }, 896 | "notification-url": "https://packagist.org/downloads/", 897 | "license": [ 898 | "BSD-3-Clause" 899 | ], 900 | "authors": [ 901 | { 902 | "name": "Jeff Welch", 903 | "email": "whatthejeff@gmail.com" 904 | }, 905 | { 906 | "name": "Volker Dusch", 907 | "email": "github@wallbash.com" 908 | }, 909 | { 910 | "name": "Bernhard Schussek", 911 | "email": "bschussek@2bepublished.at" 912 | }, 913 | { 914 | "name": "Sebastian Bergmann", 915 | "email": "sebastian@phpunit.de" 916 | } 917 | ], 918 | "description": "Provides the functionality to compare PHP values for equality", 919 | "homepage": "https://github.com/sebastianbergmann/comparator", 920 | "keywords": [ 921 | "comparator", 922 | "compare", 923 | "equality" 924 | ], 925 | "time": "2018-02-01T13:46:46+00:00" 926 | }, 927 | { 928 | "name": "sebastian/diff", 929 | "version": "2.0.1", 930 | "source": { 931 | "type": "git", 932 | "url": "https://github.com/sebastianbergmann/diff.git", 933 | "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" 934 | }, 935 | "dist": { 936 | "type": "zip", 937 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", 938 | "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", 939 | "shasum": "" 940 | }, 941 | "require": { 942 | "php": "^7.0" 943 | }, 944 | "require-dev": { 945 | "phpunit/phpunit": "^6.2" 946 | }, 947 | "type": "library", 948 | "extra": { 949 | "branch-alias": { 950 | "dev-master": "2.0-dev" 951 | } 952 | }, 953 | "autoload": { 954 | "classmap": [ 955 | "src/" 956 | ] 957 | }, 958 | "notification-url": "https://packagist.org/downloads/", 959 | "license": [ 960 | "BSD-3-Clause" 961 | ], 962 | "authors": [ 963 | { 964 | "name": "Kore Nordmann", 965 | "email": "mail@kore-nordmann.de" 966 | }, 967 | { 968 | "name": "Sebastian Bergmann", 969 | "email": "sebastian@phpunit.de" 970 | } 971 | ], 972 | "description": "Diff implementation", 973 | "homepage": "https://github.com/sebastianbergmann/diff", 974 | "keywords": [ 975 | "diff" 976 | ], 977 | "time": "2017-08-03T08:09:46+00:00" 978 | }, 979 | { 980 | "name": "sebastian/environment", 981 | "version": "3.1.0", 982 | "source": { 983 | "type": "git", 984 | "url": "https://github.com/sebastianbergmann/environment.git", 985 | "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" 986 | }, 987 | "dist": { 988 | "type": "zip", 989 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", 990 | "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", 991 | "shasum": "" 992 | }, 993 | "require": { 994 | "php": "^7.0" 995 | }, 996 | "require-dev": { 997 | "phpunit/phpunit": "^6.1" 998 | }, 999 | "type": "library", 1000 | "extra": { 1001 | "branch-alias": { 1002 | "dev-master": "3.1.x-dev" 1003 | } 1004 | }, 1005 | "autoload": { 1006 | "classmap": [ 1007 | "src/" 1008 | ] 1009 | }, 1010 | "notification-url": "https://packagist.org/downloads/", 1011 | "license": [ 1012 | "BSD-3-Clause" 1013 | ], 1014 | "authors": [ 1015 | { 1016 | "name": "Sebastian Bergmann", 1017 | "email": "sebastian@phpunit.de" 1018 | } 1019 | ], 1020 | "description": "Provides functionality to handle HHVM/PHP environments", 1021 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1022 | "keywords": [ 1023 | "Xdebug", 1024 | "environment", 1025 | "hhvm" 1026 | ], 1027 | "time": "2017-07-01T08:51:00+00:00" 1028 | }, 1029 | { 1030 | "name": "sebastian/exporter", 1031 | "version": "3.1.0", 1032 | "source": { 1033 | "type": "git", 1034 | "url": "https://github.com/sebastianbergmann/exporter.git", 1035 | "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" 1036 | }, 1037 | "dist": { 1038 | "type": "zip", 1039 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", 1040 | "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", 1041 | "shasum": "" 1042 | }, 1043 | "require": { 1044 | "php": "^7.0", 1045 | "sebastian/recursion-context": "^3.0" 1046 | }, 1047 | "require-dev": { 1048 | "ext-mbstring": "*", 1049 | "phpunit/phpunit": "^6.0" 1050 | }, 1051 | "type": "library", 1052 | "extra": { 1053 | "branch-alias": { 1054 | "dev-master": "3.1.x-dev" 1055 | } 1056 | }, 1057 | "autoload": { 1058 | "classmap": [ 1059 | "src/" 1060 | ] 1061 | }, 1062 | "notification-url": "https://packagist.org/downloads/", 1063 | "license": [ 1064 | "BSD-3-Clause" 1065 | ], 1066 | "authors": [ 1067 | { 1068 | "name": "Jeff Welch", 1069 | "email": "whatthejeff@gmail.com" 1070 | }, 1071 | { 1072 | "name": "Volker Dusch", 1073 | "email": "github@wallbash.com" 1074 | }, 1075 | { 1076 | "name": "Bernhard Schussek", 1077 | "email": "bschussek@2bepublished.at" 1078 | }, 1079 | { 1080 | "name": "Sebastian Bergmann", 1081 | "email": "sebastian@phpunit.de" 1082 | }, 1083 | { 1084 | "name": "Adam Harvey", 1085 | "email": "aharvey@php.net" 1086 | } 1087 | ], 1088 | "description": "Provides the functionality to export PHP variables for visualization", 1089 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1090 | "keywords": [ 1091 | "export", 1092 | "exporter" 1093 | ], 1094 | "time": "2017-04-03T13:19:02+00:00" 1095 | }, 1096 | { 1097 | "name": "sebastian/global-state", 1098 | "version": "2.0.0", 1099 | "source": { 1100 | "type": "git", 1101 | "url": "https://github.com/sebastianbergmann/global-state.git", 1102 | "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" 1103 | }, 1104 | "dist": { 1105 | "type": "zip", 1106 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", 1107 | "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", 1108 | "shasum": "" 1109 | }, 1110 | "require": { 1111 | "php": "^7.0" 1112 | }, 1113 | "require-dev": { 1114 | "phpunit/phpunit": "^6.0" 1115 | }, 1116 | "suggest": { 1117 | "ext-uopz": "*" 1118 | }, 1119 | "type": "library", 1120 | "extra": { 1121 | "branch-alias": { 1122 | "dev-master": "2.0-dev" 1123 | } 1124 | }, 1125 | "autoload": { 1126 | "classmap": [ 1127 | "src/" 1128 | ] 1129 | }, 1130 | "notification-url": "https://packagist.org/downloads/", 1131 | "license": [ 1132 | "BSD-3-Clause" 1133 | ], 1134 | "authors": [ 1135 | { 1136 | "name": "Sebastian Bergmann", 1137 | "email": "sebastian@phpunit.de" 1138 | } 1139 | ], 1140 | "description": "Snapshotting of global state", 1141 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1142 | "keywords": [ 1143 | "global state" 1144 | ], 1145 | "time": "2017-04-27T15:39:26+00:00" 1146 | }, 1147 | { 1148 | "name": "sebastian/object-enumerator", 1149 | "version": "3.0.3", 1150 | "source": { 1151 | "type": "git", 1152 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1153 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" 1154 | }, 1155 | "dist": { 1156 | "type": "zip", 1157 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", 1158 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", 1159 | "shasum": "" 1160 | }, 1161 | "require": { 1162 | "php": "^7.0", 1163 | "sebastian/object-reflector": "^1.1.1", 1164 | "sebastian/recursion-context": "^3.0" 1165 | }, 1166 | "require-dev": { 1167 | "phpunit/phpunit": "^6.0" 1168 | }, 1169 | "type": "library", 1170 | "extra": { 1171 | "branch-alias": { 1172 | "dev-master": "3.0.x-dev" 1173 | } 1174 | }, 1175 | "autoload": { 1176 | "classmap": [ 1177 | "src/" 1178 | ] 1179 | }, 1180 | "notification-url": "https://packagist.org/downloads/", 1181 | "license": [ 1182 | "BSD-3-Clause" 1183 | ], 1184 | "authors": [ 1185 | { 1186 | "name": "Sebastian Bergmann", 1187 | "email": "sebastian@phpunit.de" 1188 | } 1189 | ], 1190 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1191 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1192 | "time": "2017-08-03T12:35:26+00:00" 1193 | }, 1194 | { 1195 | "name": "sebastian/object-reflector", 1196 | "version": "1.1.1", 1197 | "source": { 1198 | "type": "git", 1199 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1200 | "reference": "773f97c67f28de00d397be301821b06708fca0be" 1201 | }, 1202 | "dist": { 1203 | "type": "zip", 1204 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", 1205 | "reference": "773f97c67f28de00d397be301821b06708fca0be", 1206 | "shasum": "" 1207 | }, 1208 | "require": { 1209 | "php": "^7.0" 1210 | }, 1211 | "require-dev": { 1212 | "phpunit/phpunit": "^6.0" 1213 | }, 1214 | "type": "library", 1215 | "extra": { 1216 | "branch-alias": { 1217 | "dev-master": "1.1-dev" 1218 | } 1219 | }, 1220 | "autoload": { 1221 | "classmap": [ 1222 | "src/" 1223 | ] 1224 | }, 1225 | "notification-url": "https://packagist.org/downloads/", 1226 | "license": [ 1227 | "BSD-3-Clause" 1228 | ], 1229 | "authors": [ 1230 | { 1231 | "name": "Sebastian Bergmann", 1232 | "email": "sebastian@phpunit.de" 1233 | } 1234 | ], 1235 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1236 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1237 | "time": "2017-03-29T09:07:27+00:00" 1238 | }, 1239 | { 1240 | "name": "sebastian/recursion-context", 1241 | "version": "3.0.0", 1242 | "source": { 1243 | "type": "git", 1244 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1245 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" 1246 | }, 1247 | "dist": { 1248 | "type": "zip", 1249 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", 1250 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", 1251 | "shasum": "" 1252 | }, 1253 | "require": { 1254 | "php": "^7.0" 1255 | }, 1256 | "require-dev": { 1257 | "phpunit/phpunit": "^6.0" 1258 | }, 1259 | "type": "library", 1260 | "extra": { 1261 | "branch-alias": { 1262 | "dev-master": "3.0.x-dev" 1263 | } 1264 | }, 1265 | "autoload": { 1266 | "classmap": [ 1267 | "src/" 1268 | ] 1269 | }, 1270 | "notification-url": "https://packagist.org/downloads/", 1271 | "license": [ 1272 | "BSD-3-Clause" 1273 | ], 1274 | "authors": [ 1275 | { 1276 | "name": "Jeff Welch", 1277 | "email": "whatthejeff@gmail.com" 1278 | }, 1279 | { 1280 | "name": "Sebastian Bergmann", 1281 | "email": "sebastian@phpunit.de" 1282 | }, 1283 | { 1284 | "name": "Adam Harvey", 1285 | "email": "aharvey@php.net" 1286 | } 1287 | ], 1288 | "description": "Provides functionality to recursively process PHP variables", 1289 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1290 | "time": "2017-03-03T06:23:57+00:00" 1291 | }, 1292 | { 1293 | "name": "sebastian/resource-operations", 1294 | "version": "1.0.0", 1295 | "source": { 1296 | "type": "git", 1297 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1298 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" 1299 | }, 1300 | "dist": { 1301 | "type": "zip", 1302 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", 1303 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", 1304 | "shasum": "" 1305 | }, 1306 | "require": { 1307 | "php": ">=5.6.0" 1308 | }, 1309 | "type": "library", 1310 | "extra": { 1311 | "branch-alias": { 1312 | "dev-master": "1.0.x-dev" 1313 | } 1314 | }, 1315 | "autoload": { 1316 | "classmap": [ 1317 | "src/" 1318 | ] 1319 | }, 1320 | "notification-url": "https://packagist.org/downloads/", 1321 | "license": [ 1322 | "BSD-3-Clause" 1323 | ], 1324 | "authors": [ 1325 | { 1326 | "name": "Sebastian Bergmann", 1327 | "email": "sebastian@phpunit.de" 1328 | } 1329 | ], 1330 | "description": "Provides a list of PHP built-in functions that operate on resources", 1331 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1332 | "time": "2015-07-28T20:34:47+00:00" 1333 | }, 1334 | { 1335 | "name": "sebastian/version", 1336 | "version": "2.0.1", 1337 | "source": { 1338 | "type": "git", 1339 | "url": "https://github.com/sebastianbergmann/version.git", 1340 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" 1341 | }, 1342 | "dist": { 1343 | "type": "zip", 1344 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", 1345 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", 1346 | "shasum": "" 1347 | }, 1348 | "require": { 1349 | "php": ">=5.6" 1350 | }, 1351 | "type": "library", 1352 | "extra": { 1353 | "branch-alias": { 1354 | "dev-master": "2.0.x-dev" 1355 | } 1356 | }, 1357 | "autoload": { 1358 | "classmap": [ 1359 | "src/" 1360 | ] 1361 | }, 1362 | "notification-url": "https://packagist.org/downloads/", 1363 | "license": [ 1364 | "BSD-3-Clause" 1365 | ], 1366 | "authors": [ 1367 | { 1368 | "name": "Sebastian Bergmann", 1369 | "email": "sebastian@phpunit.de", 1370 | "role": "lead" 1371 | } 1372 | ], 1373 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1374 | "homepage": "https://github.com/sebastianbergmann/version", 1375 | "time": "2016-10-03T07:35:21+00:00" 1376 | }, 1377 | { 1378 | "name": "theseer/tokenizer", 1379 | "version": "1.1.0", 1380 | "source": { 1381 | "type": "git", 1382 | "url": "https://github.com/theseer/tokenizer.git", 1383 | "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" 1384 | }, 1385 | "dist": { 1386 | "type": "zip", 1387 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", 1388 | "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", 1389 | "shasum": "" 1390 | }, 1391 | "require": { 1392 | "ext-dom": "*", 1393 | "ext-tokenizer": "*", 1394 | "ext-xmlwriter": "*", 1395 | "php": "^7.0" 1396 | }, 1397 | "type": "library", 1398 | "autoload": { 1399 | "classmap": [ 1400 | "src/" 1401 | ] 1402 | }, 1403 | "notification-url": "https://packagist.org/downloads/", 1404 | "license": [ 1405 | "BSD-3-Clause" 1406 | ], 1407 | "authors": [ 1408 | { 1409 | "name": "Arne Blankerts", 1410 | "email": "arne@blankerts.de", 1411 | "role": "Developer" 1412 | } 1413 | ], 1414 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 1415 | "time": "2017-04-07T12:08:54+00:00" 1416 | }, 1417 | { 1418 | "name": "webmozart/assert", 1419 | "version": "1.3.0", 1420 | "source": { 1421 | "type": "git", 1422 | "url": "https://github.com/webmozart/assert.git", 1423 | "reference": "0df1908962e7a3071564e857d86874dad1ef204a" 1424 | }, 1425 | "dist": { 1426 | "type": "zip", 1427 | "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", 1428 | "reference": "0df1908962e7a3071564e857d86874dad1ef204a", 1429 | "shasum": "" 1430 | }, 1431 | "require": { 1432 | "php": "^5.3.3 || ^7.0" 1433 | }, 1434 | "require-dev": { 1435 | "phpunit/phpunit": "^4.6", 1436 | "sebastian/version": "^1.0.1" 1437 | }, 1438 | "type": "library", 1439 | "extra": { 1440 | "branch-alias": { 1441 | "dev-master": "1.3-dev" 1442 | } 1443 | }, 1444 | "autoload": { 1445 | "psr-4": { 1446 | "Webmozart\\Assert\\": "src/" 1447 | } 1448 | }, 1449 | "notification-url": "https://packagist.org/downloads/", 1450 | "license": [ 1451 | "MIT" 1452 | ], 1453 | "authors": [ 1454 | { 1455 | "name": "Bernhard Schussek", 1456 | "email": "bschussek@gmail.com" 1457 | } 1458 | ], 1459 | "description": "Assertions to validate method input/output with nice error messages.", 1460 | "keywords": [ 1461 | "assert", 1462 | "check", 1463 | "validate" 1464 | ], 1465 | "time": "2018-01-29T19:49:41+00:00" 1466 | } 1467 | ], 1468 | "aliases": [], 1469 | "minimum-stability": "stable", 1470 | "stability-flags": [], 1471 | "prefer-stable": false, 1472 | "prefer-lowest": false, 1473 | "platform": [], 1474 | "platform-dev": [] 1475 | } 1476 | -------------------------------------------------------------------------------- /install/composer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | EXPECTED_SIGNATURE=$(wget -q -O - https://composer.github.io/installer.sig) 4 | php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" 5 | ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA384', 'composer-setup.php');") 6 | 7 | if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ] 8 | then 9 | >&2 echo 'ERROR: Invalid installer signature' 10 | rm composer-setup.php 11 | exit 1 12 | fi 13 | 14 | php composer-setup.php --quiet 15 | RESULT=$? 16 | rm composer-setup.php 17 | exit $RESULT 18 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | test 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /rebuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is a handy script for local use 4 | 5 | docker build --tag cd-demo-container . 6 | 7 | -------------------------------------------------------------------------------- /test/AppTest.php: -------------------------------------------------------------------------------- 1 | getWebPage(); 15 | 16 | // CI is giving me some parsing issues, so let's see it 17 | echo str_replace("\n", "⏎", $html); 18 | 19 | $doc = simplexml_load_string($html); 20 | $message = trim((string) $doc->body->div); 21 | 22 | $this->assertEquals( 23 | "This is a 'hello world' for my CD container.", 24 | $message 25 | ); 26 | } 27 | 28 | protected function getWebPage() 29 | { 30 | // Do the HTTP fetch first 31 | $this->exec( 32 | sprintf( 33 | 'docker exec -ti %s wget -O /tmp/home-page http://localhost', 34 | escapeshellarg($this->getRepoName()) 35 | ) 36 | ); 37 | 38 | // Now try copying it out 39 | $this->exec( 40 | sprintf( 41 | 'docker cp %s:/tmp/home-page /tmp/home-page', 42 | escapeshellarg($this->getRepoName()) 43 | ) 44 | ); 45 | $html = file_get_contents('/tmp/home-page'); 46 | 47 | return $html; 48 | } 49 | 50 | protected function exec($command) 51 | { 52 | $output = $return = null; 53 | 54 | exec($command, $output, $return); 55 | 56 | // Check return value first 57 | if ($return) 58 | { 59 | throw new RuntimeException( 60 | sprintf( 61 | 'Returned a failure exit code %d on Docker operation', 62 | $return 63 | ) 64 | ); 65 | } 66 | 67 | return [$output, $return]; 68 | } 69 | 70 | protected function getRepoName() 71 | { 72 | $name = isset($_SERVER['CIRCLE_PROJECT_REPONAME']) ? 73 | $_SERVER['CIRCLE_PROJECT_REPONAME'] : 74 | null; 75 | if (!$name) 76 | { 77 | throw new RuntimeException('Repository name not detected'); 78 | } 79 | 80 | return $name; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /web/health.php: -------------------------------------------------------------------------------- 1 | 4 | OK 5 | -------------------------------------------------------------------------------- /web/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test page ♥ 5 | 6 | 7 |
8 | This is a 'hello world' for my CD container. 9 |
10 |

11 | Version: 12 |

13 |

14 | GUID: 15 |

16 | 17 | 18 | 19 | --------------------------------------------------------------------------------