Full Code of jolicode/JoliCi for AI

master c042812c6ccf cached
62 files
91.6 KB
25.6k tokens
147 symbols
1 requests
Download .txt
Repository: jolicode/JoliCi
Branch: master
Commit: c042812c6ccf
Files: 62
Total size: 91.6 KB

Directory structure:
gitextract_ckyocog0/

├── .gitignore
├── .gush.yml
├── .pharcc.yml
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── bin/
│   └── jolici
├── composer.json
├── docs/
│   ├── command/
│   │   ├── clean.md
│   │   ├── run.md
│   │   └── updates-image.md
│   ├── installation.md
│   └── strategies/
│       ├── JoliCiStrategy.md
│       └── TravisCiStrategy.md
├── phpunit.xml
├── resources/
│   └── templates/
│       ├── Dockerfile-travis.twig
│       ├── Dockerfile.twig
│       ├── node_js/
│       │   ├── Dockerfile-0.10.twig
│       │   ├── Dockerfile-0.11.twig
│       │   ├── Dockerfile-0.6.twig
│       │   └── Dockerfile-0.8.twig
│       ├── php/
│       │   ├── Dockerfile-5.3.twig
│       │   ├── Dockerfile-5.4.twig
│       │   ├── Dockerfile-5.5.twig
│       │   ├── Dockerfile-5.6.twig
│       │   ├── Dockerfile-7.twig
│       │   ├── Dockerfile-hhvm.twig
│       │   └── Dockerfile.twig
│       └── ruby/
│           ├── Dockerfile-1.9.3.twig
│           ├── Dockerfile-2.0.0.twig
│           └── Dockerfile-2.1.0.twig
├── src/
│   └── Joli/
│       └── JoliCi/
│           ├── BuildStrategy/
│           │   ├── BuildStrategyInterface.php
│           │   ├── ChainBuildStrategy.php
│           │   ├── JoliCiBuildStrategy.php
│           │   └── TravisCiBuildStrategy.php
│           ├── Builder/
│           │   └── DockerfileBuilder.php
│           ├── Command/
│           │   ├── CleanCommand.php
│           │   ├── RunCommand.php
│           │   └── UpdateImageCommand.php
│           ├── Container.php
│           ├── Executor.php
│           ├── Filesystem/
│           │   └── Filesystem.php
│           ├── Job.php
│           ├── Log/
│           │   └── SimpleFormatter.php
│           ├── LoggerCallback.php
│           ├── Matrix.php
│           ├── Naming.php
│           ├── Service.php
│           ├── ServiceManager.php
│           └── Vacuum.php
└── tests/
    └── Joli/
        └── JoliCi/
            ├── BuildStrategy/
            │   ├── ChainBuildStrategyTest.php
            │   ├── JoliCiBuildStrategyTest.php
            │   ├── TravisCIBuildStrategyTest.php
            │   └── fixtures/
            │       ├── jolici/
            │       │   ├── project1/
            │       │   │   ├── .jolici/
            │       │   │   │   └── test/
            │       │   │   │       └── Dockerfile
            │       │   │   └── foo
            │       │   └── project2/
            │       │       └── .gitkeep
            │       └── travisci/
            │           └── project1/
            │               └── .travis.yml
            ├── JobTest.php
            ├── MatrixTest.php
            └── ServiceTest.php

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# IDE
/.build_dir
/.externalTollBuilders
/.settings
/.project
/.buildpath

# Dependencies
/vendor

# Bin
/pharcc.phar
/jolici.phar

================================================
FILE: .gush.yml
================================================
adapter: github
issue_tracker: github


================================================
FILE: .pharcc.yml
================================================
name: jolici.phar
main: bin/jolici
include:
    - src/
    - vendor/
    - resources/
exclude:
    - "/[Tt]ests?/"
    - "/docs?/"
    - "^vendor/phpunit"
    - "^vendor/bin"

================================================
FILE: .travis.yml
================================================
language: php

php:
    - 5.5
    - 5.6
    - hhvm
    - 7.0

matrix:
    allow_failures:
        - php: hhvm

before_script:
    - COMPOSER_ROOT_VERSION=dev-master composer --prefer-source install

script: ./vendor/bin/phpunit


================================================
FILE: CHANGELOG.md
================================================
## Change Log

### v0.3.1 (2015/04/02)

- Add [images-update command](docs/command/updates-image.md)
- Add php7 support
- Fix quoted environnement variable #52
- Assumed default versions for each language #51
- Add desktop notifications
- Fix output of versbose mode with new lines

### v0.3.0 (2015/02/25)

- Better stability with updated docker-php library and error management
- Services ! (check the dock)
- Cleaning old images and tests

### v0.2.4 (2014/08/01)
- [1690410](https://github.com/jolicode/JoliCi/commit/16904105468a718a705376baf9d9ac66bd280c64) Add php 5.6 support (@joelwurtz)
- [99a9dea](https://github.com/jolicode/JoliCi/commit/99a9dea3d196845545c21e1da0449865a900d4f1) Remove hack for phpenv, as new images use phpenv (@joelwurtz)
- [093a309](https://github.com/jolicode/JoliCi/commit/093a309cb1d804748cde099df618c21967809c48) Add jolici binary to composer. (@nubs)
- [d494e90](https://github.com/jolicode/JoliCi/commit/d494e90324f0d87cd6a705b41f825474763a094a) Remove non-available builds from filesystem. (@nubs)
- [0a715f8](https://github.com/jolicode/JoliCi/commit/0a715f86cb52fe815f379ff28c4bc03a9b746cf9) Add support for gush (@cordoval)
- [9714ab0](https://github.com/jolicode/JoliCi/commit/9714ab02dc5da5b1af78b4bb7ec4881de7c4620a) Use stable version for dependencies (@nubs)

### v0.2.2 (2014/06/26)
- [239a2de](https://github.com/jolicode/JoliCi/commit/239a2de6cf5d0db31404dbb39ab6f4b9210aa921) Update dependencies versions (@joelwurtz)

### v0.2.1 (2014/05/15)
- [ad1ba9a](https://github.com/jolicode/JoliCi/commit/ad1ba9a3eca7fb4a0e4f3f3e5aba59904d525127) Add ruby versions (@joelwurtz)
- [d697ee8](https://github.com/jolicode/JoliCi/commit/d697ee8eedcd0b33b42c2b5e89c918fb222eefbc) Encapsulate command in bash logged with profile support (@joelwurtz)
- [c9b86ce](https://github.com/jolicode/JoliCi/commit/c9b86ce4c63ceb430a10930e5a7345e70706a321) Add node js support (@joelwurtz)
- [9b1c684](https://github.com/jolicode/JoliCi/commit/9b1c684ebe11991253d76825847d79c91ad92ac8) Add support for env variables (@joelwurtz)
- [d1cf3b7](https://github.com/jolicode/JoliCi/commit/d1cf3b7966b385759acc351c299b067b653ac06d) Add timeout support, and set to 5 min by default (@joelwurtz)
- [9a551c8](https://github.com/jolicode/JoliCi/commit/9a551c8938f61ef74beaff3737b8dfa7c2091b9e) Add static progress bar when getting image from docker (@joelwurtz)
- [f6662c2](https://github.com/jolicode/JoliCi/commit/f6662c25c8ad55ead5b654a75a29668ffa35f236) Make php dockerfile work when using phpenv specific action (like symfony) (@joelwurtz)

### v0.2.0 (2014/02/21)
- [7d85d78](https://github.com/jolicode/JoliCi/commit/7d85d78774eaf6a5d41c1cd98a4be4ce7497c054) Adding ruby to travis strategy (@joelwurtz)
- [71364c3](https://github.com/jolicode/JoliCi/commit/71364c3814e071cf63983f98933e11ec75fb5ea9) Add a default timeout to 10min for long running build (@joelwurtz)
- [7100908](https://github.com/jolicode/JoliCi/commit/7100908c6f78966bd750310b2abfa0af5173f1fb) Refactoring creation of Dockerfile with twig generation (@joelwurtz)

### v0.1.1 (2014/01/18)
- [69276ca](https://github.com/jolicode/JoliCi/commit/69276ca8b982b1343519f4119d188a299258c40b) Add travis ci support (@joelwurtz)
- [8d469ec](https://github.com/jolicode/JoliCi/commit/8d469ecad0b696f72032e21f0d654ee8ad304c47) Add error management (@joelwurtz)
- [945a31a](https://github.com/jolicode/JoliCi/commit/945a31a1750c366eae15f1baeebf1aaff73001ff) Allow to override command when running test (@joelwurtz)


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

First, thank you!

Here are a few rules to follow in order to ease code reviews, and discussions before maintainers accept and merge your work.

* You **MUST** follow the [PSR-1](http://www.php-fig.org/psr/1/) and [PSR-2](http://www.php-fig.org/psr/2/).
* You **MUST** run the test suite.
* You **MUST** write (or update) unit tests.
* You **SHOULD** write documentation.

Please, write [commit messages that make sense](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), and [rebase your branch](http://git-scm.com/book/en/Git-Branching-Rebasing) before submitting your Pull Request.

One may ask you to [squash your commits](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) too. This is used to "clean" your Pull Request before merging it (we don't want commits such as `fix tests`, `fix 2`, `fix 3`, etc.).

Also, when creating your Pull Request on GitHub, you **MUST** write a description which gives the context and/or explains why you are creating it.


================================================
FILE: LICENSE
================================================
Copyright (C) 2014 Joel Wurtz

Permission is hereby granted, free of charge, to any person obtaining a copy 
of this software and associated documentation files (the "Software"), to deal 
in the Software without restriction, including without limitation the rights 
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
copies of the Software, and to permit persons to whom the Software is furnished 
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all 
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
THE SOFTWARE.


================================================
FILE: README.md
================================================
# JoliCi

JoliCi is a free and open source Continuous Integration _Client_ written in PHP (5.4 minimum) and powered by Docker (please use a recent version). It has been written to be compliant 
with existent Ci services like Travis-Ci and not create a new standard. ([Remove that smile, I KNOW what you're thinking.](http://xkcd.com/927/))

**This project is still in beta, there may be bugs and missing features**

[![Build Status](https://travis-ci.org/jolicode/JoliCi.png?branch=master)](https://travis-ci.org/jolicode/JoliCi) [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/jolicode/JoliCi/badges/quality-score.png?s=1ba180546468c07ca8fc0996dcdc4a740dcf23fc)](https://scrutinizer-ci.com/g/jolicode/JoliCi/)

## Usage

[Have a .travis.yml in your project](http://docs.travis-ci.com/user/getting-started/#Step-three%3A-Add-.travis.yml-file-to-your-repository)

[Download the last version of jolici](https://github.com/jolicode/JoliCi/releases) and run it, i.e. for v0.3.1:

```bash
wget https://github.com/jolicode/JoliCi/releases/download/v0.3.1/jolici.phar
php jolici.phar run
```

First run can be quite long since it has to build everything from the beginning. Subsequent build should be faster thanks to docker caching.

![JoliCi Demo](https://github.com/jolicode/JoliCi/raw/master/docs/jolici-terminal.gif "JoliCi Demo")

If you want to see what happens behind this black box:

```bash
php jolici.phar run -v
```

## Ci supported

* Travis-Ci
* [...](CONTRIBUTING.md)

## I want to read more

* [Installation](docs/installation.md)
* Usage
    * [The run command](docs/command/run.md)
    * [The clean command](docs/command/clean.md)
    * [The images-update command](docs/command/updates-image.md)
* Strategies (a.k.a. how to create a build from a configuration file)
    * [Travis-Ci](docs/strategies/TravisCiStrategy.md)
    * [JoliCi](docs/strategies/JoliCiStrategy.md)

## Credits

* [All contributors](https://github.com/jolicode/JoliCi/graphs/contributors)
* Some parts of this project are inspired by :
	* [Docker Client](https://github.com/dotcloud/docker/blob/master/commands.go)
* This README is heavily inspired by a @willdurand [blog post](http://williamdurand.fr/2013/07/04/on-open-sourcing-libraries/).
* [@ubermuda](https://github.com/ubermuda) for accepting many pull requests on [docker-php](https://github.com/stage1/docker-php) library

## License

View the [LICENSE](LICENSE) file attach to this project.


================================================
FILE: bin/jolici
================================================
#!/usr/bin/env php
<?php

use Joli\JoliCi\Command\RunCommand;
use Joli\JoliCi\Command\CleanCommand;
use Symfony\Component\Console\Application;
use cbednarski\Pharcc\Git;

if (file_exists(__DIR__ . '/../vendor/autoload.php')) {
    require_once(__DIR__ . '/../vendor/autoload.php');
} elseif (file_exists(__DIR__ . '/../../../../vendor/autoload.php')) {
    require_once(__DIR__ . '/../../../../vendor/autoload.php');
} else {
    throw new \Exception('Unable to load autoloader');
}

$version = "dev";

if (class_exists("\cbednarski\Pharcc\Git")) {
    $version = Git::getVersion(__DIR__);
}

$application = new Application('jolici', $version);

$application->add(new RunCommand());
$application->add(new CleanCommand());
$application->add(new \Joli\JoliCi\Command\UpdateImageCommand());

$application->run();


================================================
FILE: composer.json
================================================
{
    "name": "jolicode/jolici",
    "description": "Run your TravisCi builds locally",
    "type": "library",
    "license": "MIT",
    "keywords": ["test", "ci", "travisci", "docker", "continuous", "integration"],
    "authors": [
        {
            "name": "Joel Wurtz",
            "email": "jwurtz@jolicode.com",
            "homepage": "https://twitter.com/JoelWurtz"
        }
    ],
    "require" : {
        "php" : ">=5.3",
        "symfony/console" : "^2.4",
        "symfony/filesystem" : "^2.4",
        "symfony/finder" : "^2.4",
        "symfony/yaml" : "^2.4",
        "monolog/monolog": "^1.6",
        "docker-php/docker-php": "^1.22",
        "cedriclombardot/twig-generator": "^1.0",
        "guzzlehttp/streams": "^1.3",
        "behat/transliterator": "^1.0",
        "jolicode/jolinotif": "^1.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^4.8",
        "mikey179/vfsStream": "^1.2"
    },
    "bin": [
        "bin/jolici"
    ],
    "autoload" : {
        "psr-0" : {
            "Joli" : "src"
        }
    }
}


================================================
FILE: docs/command/clean.md
================================================
# The clean command

JoliCi try to keep the number of docker images, containers and build directories as low as possible when running test.
 
But sometimes thing can break hard and cleaning process is not run, so you will need to run this command in order 
to not overflow your hard drive.

## Default command:

```
php jolici.phar clean
```

The default command will clean all images, containers and build directories except for the last one, in the current project directory.

## Options

Here is a list of options you can pass to the clean command:

* `--project-path DIRECTORY` / `-p DIRECTORY`: Set the root path (DIRECTORY) of your project, use current directory as a default
* `--keep NUMBER` / `-k NUMBER`: How many versions (NUMBER) of images, containers and / or build directories should the clean process keeps
* `--only-containers`: Remove only containers
* `--only-directories`: Remove only build directories
* `--only-images`: Remove only images
* `--force`: Force removal of images



================================================
FILE: docs/command/run.md
================================================
# The run command

The run command is the main command of JoliCi. It create and prepare the differents environments (jobs) and execute the command on every one.

## Workflow

### Creating environments

An environment is created when a build strategy is detected on your project, 
this can be as simple as [parsing a `.travis.yml`](../strategies/TravisCiStrategy.md) file or more complex
by [reading a `.jolici directory](../strategies/JoliCiStrategy.md)

Each environment will be prepared in a temporary directory so this will never modify your project (no need to add a line in 
your .gitignore file)

### Building environment

An environment is simply a docker image build with a Dockerfile, which can be generated for you when using
TravisCi strategy or using your own with JoliCi strategy

### Starting services

Before running test, JoliCi will try to launch services determined by your configuration file (only on TravisCi for the moment), 
in order to have mysql, memcached, elasticsearch... services available for your tests.

Each service will start from a clean state, data is not keeped.

For the moment, services are not host in the test container but run in a separate container.
To use them you need to set the correct host name for the service on your different configuration files.

The hostname for each service is the same as the service name, so i.e. connecting to mysql can be done like that in 
your test container : 

```
mysql -u travis -h mysql
```

Also be aware that services are only available during the script execution, any action using a service before the script 
will fail (like creating the schema in the install part).

Elimination of this two downsides are currently the focus for the next releases.

### Running test

Once the environment is ready, JoliCi will run your test command on it and display the output directly on your console.

### Exit code

If all test on each environment is successful (return 0 for exit code), jolici will return as well 0 for exit code.
Otherwise it will return the number of failing tests.

### Cleaning

JoliCi try to do his best to keep disk space as low as possible without decreasing speed. In order to do this it will delete all
files related to an environment at the exception of the last run so we can use his cache.

## Default command:

```bash
php jolici.phar run
```

The default command will run test for each environments created, this will only output the result of the test command. If you 
want to see the output of the build process (like TravisCi) you can run this command with a verbose option:

```bash
php jolici.phar run -v
```

## Options

Here is a list of options you can pass to the clean command:

* `--project-path DIRECTORY` / `-p DIRECTORY`: Set the root path (DIRECTORY) of your project, use current directory as a default
* `--keep NUMBER` / `-k NUMBER`: How many versions (NUMBER) of images, containers and / or build directories should be clean after running test (default to 1)
* `--no-cache`: Use this option if you don't want to use the cache from the last build
* `--timeout TIMEOUT` / `-t TIMEOUT`: This is the timeout, in seconds, for the run command, it allows to aborting test if a command hangs up forever (default to 5 minutes)
* `--notify`: Use this option to display desktop notifications for each job

## Overriding test command

If one more argument is present there will be considered as the command line for running test, 
for example to see the php version of an environment created by JoliCi you can do the following command:

```bash
php jolici.phar run php -v
```


================================================
FILE: docs/command/updates-image.md
================================================
# The images-update command

This command is used to update all images jolici used to create test environments.

```
$ jolici images-update
Update jolicode/php56 image
Update jolicode/php55 image
Update jolicode/hhvm image
Update jolicode/php54 image
```


================================================
FILE: docs/installation.md
================================================
# Installation

## Install globally

### Download

[Download the last version of jolici](https://github.com/jolicode/JoliCi/releases), i.e. for v0.3.1:

```bash
curl https://github.com/jolicode/JoliCi/releases/download/v0.3.1/jolici.phar -o /usr/local/bin/jolici
chmod +x /usr/local/bin/jolici
```

```bash
wget https://github.com/jolicode/JoliCi/releases/download/v0.3.1/jolici.phar -O /usr/local/bin/jolici
chmod +x /usr/local/bin/jolici
```

### Composer

```
composer global require "jolicode/jolici:*"
```

## Install per project

### Download

```
curl http://jolici.jolicode.com/jolici.phar
```

```
wget http://jolici.jolicode.com/jolici.phar
```

### Composer

```
composer require --dev "jolicode/jolici:*"
```

This tool is mainly used for development purpose so here we set the --dev option, if this tool is not only for developers remove that option.




================================================
FILE: docs/strategies/JoliCiStrategy.md
================================================
# JoliCiBuildStrategy

This strategy is based on a directory structure and Dockerfile, this is the most flexible strategy as you can whatever you want for environment.

## Usage

To create jobs the project **MUST** have a `.jolici` directory.

Each subdirectory is then considered as a different job, they **MUST** have a `Dockerfile` which will explain how to create the environment.

The test command is determined by the `CMD` or `ENTRYPOINT` keywords in the `Dockerfile`.

## Overriding files

You may want to have a different configuration file for each environment. In order to have this behavior all files under the job directory (`.jolici/my_job_environment`) will be 
copied, before creation, to the root directory of the project.

## Add source to the build

Due to the precedent behavior, the Dockerfile is now at the root of your project. To add the source code of your project in your job environment you 
can add this command inside your Dockerfile:

```
ADD . /project
```

Your project will then be available at the `/project` path in the environment.


================================================
FILE: docs/strategies/TravisCiStrategy.md
================================================
# TravisCiBuildStrategy

This strategy parses the .travis.yml file at the root of your project to create a Dockerfile for each job and run tests.

## Language and version support

For the moment only the following language / version list is supported, the goal is to support what travis supports:

* php
	* 5.3 (5.3.28)
	* 5.4 (5.4.31)
	* 5.5 (5.5.15)
	* 5.6 (5.6.3)
	* hhvm (3.3.0)
* ruby
    * 1.9.3
    * 2.0.0
    * 2.1.0
* node
    * 0.6
    * 0.8
    * 0.10
    * 0.11

## Service support

Some services are supported for you to use

## Docker images

This strategy use pre-built docker images available on this github repository: [https://github.com/jolicode/docker-images](https://github.com/jolicode/docker-images)



================================================
FILE: phpunit.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>

<!-- http://www.phpunit.de/manual/current/en/appendixes.configuration.html -->
<phpunit
    backupGlobals               = "false"
    backupStaticAttributes      = "false"
    colors                      = "true"
    convertErrorsToExceptions   = "true"
    convertNoticesToExceptions  = "true"
    convertWarningsToExceptions = "true"
    processIsolation            = "false"
    stopOnFailure               = "false"
    syntaxCheck                 = "false"
    bootstrap                   = "vendor/autoload.php" >

    <testsuites>
        <testsuite name="JoliCi Unit Test Suite">
            <directory>tests</directory>
        </testsuite>
    </testsuites>

    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory>src</directory>
        </whitelist>
    </filter>
</phpunit>


================================================
FILE: resources/templates/Dockerfile-travis.twig
================================================
{% extends "Dockerfile.twig" %}

{% block env %}
{% for key,value in global_env %}
ENV {{ key }}={{ value }}
{% endfor %}

{% for key,value in env %}
ENV {{ key }} {{ value }}
{% endfor %}
{% endblock %}

{% block before_install %}
    {% for line in before_install %}
RUN /bin/bash -c -l "cd $WORKDIR && {{ line|replace({"$": "\\\$", '"': '\\\"'}) }}"
    {% endfor %}
{% endblock %}

{% block install %}
    {% for line in install %}
RUN /bin/bash -c -l "cd $WORKDIR && {{ line|replace({"$": "\\\$", '"': '\\\"'}) }}"
    {% endfor %}
{% endblock %}

{% block before_script %}
    {% for line in before_script %}
RUN /bin/bash -c -l "cd $WORKDIR && {{ line|replace({"$": "\\\$", '"': '\\\"'}) }}"
    {% endfor %}
{% endblock %}

{% block script %}
CMD /bin/bash -c -l "cd $WORKDIR{% for line in script %} && {{ line|replace({"$": "\\\$", '"': '\\\"'}) }}{% endfor %}"
{% endblock %}


================================================
FILE: resources/templates/Dockerfile.twig
================================================
{% block from %}
FROM jolicode/base:latest
{% endblock %}

{% block content %}
{% endblock %}

LABEL com.jolici.image="true"
ENV WORKDIR $HOME/project
ADD . $WORKDIR
WORKDIR $HOME/project
RUN sudo chown travis:travis -R $HOME/project

{% block env %}{% endblock %}

{% block before_install %}{% endblock %}

{% block install %}{% endblock %}

{% block before_script %}{% endblock %}

{% block script %}{% endblock %}


================================================
FILE: resources/templates/node_js/Dockerfile-0.10.twig
================================================
{% extends "Dockerfile-travis.twig" %}

{% block from %}
FROM jolicode/node-0.10:latest
{% endblock %}

================================================
FILE: resources/templates/node_js/Dockerfile-0.11.twig
================================================
{% extends "Dockerfile-travis.twig" %}

{% block from %}
FROM jolicode/node-0.11:latest
{% endblock %}

================================================
FILE: resources/templates/node_js/Dockerfile-0.6.twig
================================================
{% extends "Dockerfile-travis.twig" %}

{% block from %}
FROM jolicode/node-0.6:latest
{% endblock %}

================================================
FILE: resources/templates/node_js/Dockerfile-0.8.twig
================================================
{% extends "Dockerfile-travis.twig" %}

{% block from %}
FROM jolicode/node-0.8:latest
{% endblock %}

================================================
FILE: resources/templates/php/Dockerfile-5.3.twig
================================================
{% extends "php/Dockerfile.twig" %}

{% block env %}
{{ parent() }}
ENV TRAVIS_PHP_VERSION php5.3
{% endblock %}

{% block from %}
FROM jolicode/php53:latest
{% endblock %}


================================================
FILE: resources/templates/php/Dockerfile-5.4.twig
================================================
{% extends "php/Dockerfile.twig" %}

{% block env %}
{{ parent() }}
ENV TRAVIS_PHP_VERSION php5.4
{% endblock %}

{% block from %}
FROM jolicode/php54:latest
{% endblock %}


================================================
FILE: resources/templates/php/Dockerfile-5.5.twig
================================================
{% extends "php/Dockerfile.twig" %}

{% block env %}
{{ parent() }}
ENV TRAVIS_PHP_VERSION php5.5
{% endblock %}

{% block from %}
FROM jolicode/php55:latest
{% endblock %}


================================================
FILE: resources/templates/php/Dockerfile-5.6.twig
================================================
{% extends "php/Dockerfile.twig" %}

{% block env %}
{{ parent() }}
ENV TRAVIS_PHP_VERSION php5.6
{% endblock %}

{% block from %}
FROM jolicode/php56:latest
{% endblock %}


================================================
FILE: resources/templates/php/Dockerfile-7.twig
================================================
{% extends "php/Dockerfile.twig" %}

{% block env %}
{{ parent() }}
ENV TRAVIS_PHP_VERSION php7.0-dev
{% endblock %}

{% block from %}
FROM jolicode/php-master:latest
{% endblock %}


================================================
FILE: resources/templates/php/Dockerfile-hhvm.twig
================================================
{% extends "Dockerfile-travis.twig" %}

{% block from %}
FROM jolicode/hhvm:latest
{% endblock %}

{% block env %}
{{ parent() }}
ENV TRAVIS_PHP_VERSION hhvm
{% endblock %}

{% block before_install %}
RUN echo 'date.timezone={{ timezone }}' | sudo tee -a /etc/hhvm/php.ini
{% endblock %}


================================================
FILE: resources/templates/php/Dockerfile.twig
================================================
{% extends "Dockerfile-travis.twig" %}

{% block env %}
{{ parent() }}
{% endblock %}

{% block before_install %}
RUN echo 'date.timezone={{ timezone }}' >> /home/.phpenv/versions/`cat /home/.phpenv/version`/etc/php.ini
{% endblock %}


================================================
FILE: resources/templates/ruby/Dockerfile-1.9.3.twig
================================================
{% extends "Dockerfile-travis.twig" %}

{% block from %}
FROM jolicode/ruby-1.9.3:latest
{% endblock %}

================================================
FILE: resources/templates/ruby/Dockerfile-2.0.0.twig
================================================
{% extends "Dockerfile-travis.twig" %}

{% block from %}
FROM jolicode/ruby-2.0.0:latest
{% endblock %}

================================================
FILE: resources/templates/ruby/Dockerfile-2.1.0.twig
================================================
{% extends "Dockerfile-travis.twig" %}

{% block from %}
FROM jolicode/ruby-2.1.0:latest
{% endblock %}

================================================
FILE: src/Joli/JoliCi/BuildStrategy/BuildStrategyInterface.php
================================================
<?php
/*
 * This file is part of JoliCi.
*
* (c) Joel Wurtz <jwurtz@jolicode.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Joli\JoliCi\BuildStrategy;

use Joli\JoliCi\Job;

/**
 * Interface that all Build strategy must implement
 *
 * @author Joel Wurtz <jwurtz@jolicode.com>
 */
interface BuildStrategyInterface
{
    const WORKDIR = "/home/project";

    /**
     * Create / Get jobs for a project
     *
     * @param string $directory Location of project
     *
     * @return \Joli\JoliCi\Job[] Return a list of jobs to create
     */
    public function getJobs($directory);

    /**
     * Prepare a job (generally copy its files to a new directory
     *
     * @param \Joli\JoliCi\Job $job
     *
     * @return void
     */
    public function prepareJob(Job $job);

    /**
     * Return name of the build strategy
     *
     * @return string
     */
    public function getName();

    /**
     * Tell if the build strategy is supported for a project
     *
     * @param string $directory Location of project
     *
     * @return boolean
     */
    public function supportProject($directory);
}


================================================
FILE: src/Joli/JoliCi/BuildStrategy/ChainBuildStrategy.php
================================================
<?php

namespace Joli\JoliCi\BuildStrategy;

use Joli\JoliCi\Job;

class ChainBuildStrategy implements BuildStrategyInterface
{
    /**
     * @var BuildStrategyInterface[] A list of strategy to use for this builder
     */
    private $strategies = array();

    /**
     * Add a build strategy to builder
     *
     * @param BuildStrategyInterface $strategy Strategy to add
     */
    public function pushStrategy(BuildStrategyInterface $strategy)
    {
        $this->strategies[$strategy->getName()] = $strategy;
    }

    /**
     * {@inheritdoc}
     */
    public function getJobs($directory)
    {
        $builds = array();

        foreach ($this->strategies as $strategy) {
            if ($strategy->supportProject($directory)) {
                $builds += $strategy->getJobs($directory);
            }
        }

        return $builds;
    }

    /**
     * {@inheritdoc}
     */
    public function prepareJob(Job $job)
    {
        $this->strategies[$job->getStrategy()]->prepareJob($job);
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'Chain';
    }

    /**
     * {@inheritdoc}
     */
    public function supportProject($directory)
    {
        foreach ($this->strategies as $strategy) {
            if ($strategy->supportProject($directory)) {
                return true;
            }
        }

        return false;
    }
}


================================================
FILE: src/Joli/JoliCi/BuildStrategy/JoliCiBuildStrategy.php
================================================
<?php
/*
 * This file is part of JoliCi.
*
* (c) Joel Wurtz <jwurtz@jolicode.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Joli\JoliCi\BuildStrategy;

use Joli\JoliCi\Job;
use Joli\JoliCi\Filesystem\Filesystem;
use Joli\JoliCi\Naming;
use Symfony\Component\Finder\Finder;

/**
 * JoliCi implementation for build
 *
 * A project must have a .jolici directory, each directory inside this one will be a type of build and must contain a Dockerfile to be executable
 *
 * @author Joel Wurtz <jwurtz@jolicode.com>
 */
class JoliCiBuildStrategy implements BuildStrategyInterface
{
    /**
     * @var string Base path for build
     */
    private $buildPath;

    /**
     * @var Filesystem Filesystem service
     */
    private $filesystem;

    /**
     * @var Naming Use to name the image created
     */
    private $naming;

    /**
     * @param string     $buildPath  Directory where build must be created
     * @param Naming     $naming     Naming service
     * @param Filesystem $filesystem Filesystem service
     */
    public function __construct($buildPath, Naming $naming, Filesystem $filesystem)
    {
        $this->buildPath  = $buildPath;
        $this->naming     = $naming;
        $this->filesystem = $filesystem;
    }

    /**
     * {@inheritdoc}
     */
    public function getJobs($directory)
    {
        $builds = array();
        $finder = new Finder();
        $finder->directories();

        foreach ($finder->in($this->getJoliCiStrategyDirectory($directory)) as $dir) {
            $builds[] = new Job(
                $this->naming->getProjectName($directory),
                $this->getName(),
                $this->naming->getUniqueKey(array('build' => $dir->getFilename())),
                array(
                    'origin' => $directory,
                    'build'  => $dir->getRealPath(),
                ),
                "JoliCi Build: ".$dir->getFilename()
            );
        }

        return $builds;
    }

    /**
     * {@inheritdoc}
     */
    public function prepareJob(Job $job)
    {
        $origin = $job->getParameters()['origin'];
        $target = $this->buildPath.DIRECTORY_SEPARATOR. $job->getDirectory();
        $build  = $job->getParameters()['build'];

        // First mirroring target
        $this->filesystem->mirror($origin, $target, null, array(
            'delete'   => true,
            'override' => true,
        ));

        // Second override target with build dir
        $this->filesystem->mirror($build, $target, null, array(
            'delete'   => false,
            'override' => true,
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return "JoliCi";
    }

    /**
     * {@inheritdoc}
     */
    public function supportProject($directory)
    {
        return file_exists($this->getJoliCiStrategyDirectory($directory)) && is_dir($this->getJoliCiStrategyDirectory($directory));
    }

    /**
     * Return the jolici strategy directory where there must be builds
     *
     * @param  string $projectPath
     * @return string
     */
    protected function getJoliCiStrategyDirectory($projectPath)
    {
        return $projectPath.DIRECTORY_SEPARATOR.'.jolici';
    }
}


================================================
FILE: src/Joli/JoliCi/BuildStrategy/TravisCiBuildStrategy.php
================================================
<?php
/*
 * This file is part of JoliCi.
 *
 * (c) Joel Wurtz <jwurtz@jolicode.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Joli\JoliCi\BuildStrategy;

use Joli\JoliCi\Job;
use Joli\JoliCi\Builder\DockerfileBuilder;
use Joli\JoliCi\Filesystem\Filesystem;
use Joli\JoliCi\Matrix;
use Joli\JoliCi\Naming;
use Joli\JoliCi\Service;
use Symfony\Component\Yaml\Yaml;

/**
 * TravisCi implementation for build strategy
 *
 * A project must have a .travis.yml file
 *
 * @author Joel Wurtz <jwurtz@jolicode.com>
 */
class TravisCiBuildStrategy implements BuildStrategyInterface
{
    private $languageVersionKeyMapping = array(
        'ruby' => 'rvm',
    );

    private $defaults = array(
        'php' => array(
            'before_install' => array(),
            'install'        => array(),
            'before_script'  => array(),
            'script'         => array('phpunit'),
            'env'            => array(),
            'default_versions' => array('5.6')
        ),
        'ruby' => array(
            'before_install' => array(),
            'install'        => array(),
            'before_script'  => array(),
            'script'         => array('bundle exec rake'),
            'env'            => array(),
            'default_versions' => array('2.1.0')
        ),
        'node_js' => array(
            'before_install' => array(),
            'install'        => array(),
            'before_script'  => array(),
            'script'         => array('npm test'),
            'env'            => array(),
            'default_versions' => array('0.10')
        ),
    );

    private $servicesMapping = array(
        'mongodb' => array(
            'repository' => 'mongo',
            'tag' => '2.6',
            'config' => array()
        ),
        'mysql'   => array(
            'repository' => 'mysql',
            'tag' => '5.5',
            'config' => array(
                'Env' => array(
                    'MYSQL_ROOT_PASSWORD=""',
                    'MYSQL_USER=travis',
                    'MYSQL_PASSWORD=""'
                )
            )
        ),
        'postgresql' => array(
            'repository' => 'postgres',
            'tag'        => '9.1',
            'config'     => array()
        ),
        'couchdb' => array(
            'repository' => 'fedora/couchdb',
            'tag' => 'latest',
            'config' => array()
        ),
        'rabbitmq' => array(
            'repository' => 'dockerfile/rabbitmq',
            'tag' => 'latest',
            'config' => array()
        ),
        'memcached' => array(
            'repository' => 'sylvainlasnier/memcached',
            'tag' => 'latest',
            'config' => array()
        ),
        'redis-server' => array(
            'repository' => 'redis',
            'tag' => '2.8',
            'config' => array()
        ),
        'cassandra' => array(
            'repository' => 'spotify/cassandra',
            'tag' => 'latest',
            'config' => array()
        ),
        'neo4j' => array(
            'repository' => 'tpires/neo4j',
            'tag' => 'latest',
            'config' => array()
        ),
        'elasticsearch' => array(
            'repository' => 'dockerfile/elasticsearch',
            'tag' => 'latest',
            'config' => array()
        ),
    );

    /**
     * @var DockerfileBuilder Builder for dockerfile
     */
    private $builder;

    /**
     * @var string Build path for project
     */
    private $buildPath;

    /**
     * @var Filesystem Filesystem service
     */
    private $filesystem;

    /**
     * @var \Joli\JoliCi\Naming Naming service to create docker name for images
     */
    private $naming;

    /**
     * @param DockerfileBuilder $builder    Twig Builder for Dockerfile
     * @param string            $buildPath  Directory where builds are created
     * @param Naming            $naming     Naming service
     * @param Filesystem        $filesystem Filesystem service
     */
    public function __construct(DockerfileBuilder $builder, $buildPath, Naming $naming, Filesystem $filesystem)
    {
        $this->builder    = $builder;
        $this->buildPath  = $buildPath;
        $this->naming     = $naming;
        $this->filesystem = $filesystem;
    }

    /**
     * {@inheritdoc}
     */
    public function getJobs($directory)
    {
        $jobs       = array();
        $config     = Yaml::parse(file_get_contents($directory.DIRECTORY_SEPARATOR.".travis.yml"));
        $matrix     = $this->createMatrix($config);
        $services   = $this->getServices($config);
        $timezone   = ini_get('date.timezone');

        foreach ($matrix->compute() as $possibility) {
            $parameters   = array(
                'language' => $possibility['language'],
                'version' => $possibility['version'],
                'environment' => $possibility['environment'],
            );

            $description = sprintf('%s = %s', $possibility['language'], $possibility['version']);

            if ($possibility['environment'] !== null) {
                $description .= sprintf(', Environment: %s', json_encode($possibility['environment']));
            }

            $jobs[] = new Job($this->naming->getProjectName($directory), $this->getName(), $this->naming->getUniqueKey($parameters), array(
                'language'       => $possibility['language'],
                'version'        => $possibility['version'],
                'before_install' => $possibility['before_install'],
                'install'        => $possibility['install'],
                'before_script'  => $possibility['before_script'],
                'script'         => $possibility['script'],
                'env'            => $possibility['environment'],
                'global_env'     => $possibility['global_env'],
                'timezone'       => $timezone,
                'origin'         => realpath($directory),
            ), $description, null, $services);
        }

        return $jobs;
    }

    /**
     * {@inheritdoc}
     */
    public function prepareJob(Job $job)
    {
        $parameters = $job->getParameters();
        $origin     = $parameters['origin'];
        $target     = $this->buildPath.DIRECTORY_SEPARATOR. $job->getDirectory();

        // First mirroring target
        $this->filesystem->mirror($origin, $target, null, array(
            'delete' => true,
            'override' => true,
        ));

        // Create dockerfile
        $this->builder->setTemplateName(sprintf("%s/Dockerfile-%s.twig", $parameters['language'], $parameters['version']));
        $this->builder->setVariables($parameters);
        $this->builder->setOutputName('Dockerfile');
        $this->builder->writeOnDisk($target);
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return "TravisCi";
    }

    /**
     * {@inheritdoc}
     */
    public function supportProject($directory)
    {
        return file_exists($directory.DIRECTORY_SEPARATOR.".travis.yml") && is_file($directory.DIRECTORY_SEPARATOR.".travis.yml");
    }

    /**
     * Get command lines to add for a configuration value in .travis.yml file
     *
     * @param array  $config   Configuration of travis ci parsed
     * @param string $language Language for getting the default value if no value is set
     * @param string $key      Configuration key
     *
     * @return array A list of command to add to Dockerfile
     */
    private function getConfigValue($config, $language, $key)
    {
        if (!isset($config[$key]) || empty($config[$key])) {
            if (isset($this->defaults[$language][$key])) {
                return $this->defaults[$language][$key];
            }

            return array();
        }

        if (!is_array($config[$key])) {
            return array($config[$key]);
        }

        return $config[$key];
    }

    /**
     * Create matrix of build
     *
     * @param array $config
     *
     * @return Matrix
     */
    protected function createMatrix($config)
    {
        $language = isset($config['language']) ? $config['language'] : 'ruby';

        if (!isset($this->defaults[$language])) {
            throw new \Exception(sprintf('Language %s not supported', $language));
        }

        $versionKey       = isset($this->languageVersionKeyMapping[$language]) ? $this->languageVersionKeyMapping[$language] : $language;
        $environmentLines = $this->getConfigValue($config, $language, "env");
        $environnements   = array();
        $globalEnv        = array();
        $matrixEnv        = $environmentLines;
        $versions         = (array) (isset($config[$versionKey]) ? $config[$versionKey] : $this->defaults[$language]['default_versions']);

        foreach ($versions as $key => $version) {
            if (!$this->isLanguageVersionSupported($language, $version)) {
                unset($versions[$key]);
            }
        }

        if (isset($environmentLines['matrix'])) {
            $matrixEnv = $environmentLines['matrix'];
        }

        if (isset($environmentLines['global'])) {
            foreach ($environmentLines['global'] as $environementVariable) {
                if (is_array($environementVariable) && array_key_exists('secure', $environementVariable)) {
                    continue;
                }

                list ($key, $value) = $this->parseEnvironementVariable($environementVariable);
                $globalEnv = array_merge($globalEnv, array($key => $value));
            }

            if (!isset($environmentLines['matrix'])) {
                $matrixEnv = array();
            }
        }

        // Parsing environnements
        foreach ($matrixEnv as $environmentLine) {
            $environnements[] = $this->parseEnvironmentLine($environmentLine);
        }

        $matrix = new Matrix();
        $matrix->setDimension('language', array($language));
        $matrix->setDimension('environment', $environnements);
        $matrix->setDimension('global_env', array($globalEnv));
        $matrix->setDimension('version', $versions);
        $matrix->setDimension('before_install', array($this->getConfigValue($config, $language, 'before_install')));
        $matrix->setDimension('install', array($this->getConfigValue($config, $language, 'install')));
        $matrix->setDimension('before_script', array($this->getConfigValue($config, $language, 'before_script')));
        $matrix->setDimension('script', array($this->getConfigValue($config, $language, 'script')));


        return $matrix;
    }

    /**
     * Get services list from travis ci configuration file
     *
     * @param $config
     *
     * @return Service[]
     */
    protected function getServices($config)
    {
        $services       = array();
        $travisServices = isset($config['services']) && is_array($config['services']) ? $config['services'] : array();

        foreach ($travisServices as $service) {
            if (isset($this->servicesMapping[$service])) {
                $services[] = new Service(
                    $service,
                    $this->servicesMapping[$service]['repository'],
                    $this->servicesMapping[$service]['tag'],
                    $this->servicesMapping[$service]['config']
                );
            }
        }

        return $services;
    }

    /**
     * Parse an environnement line from Travis to return an array of variables
     *
     * Transform:
     *   "A=B C=D"
     * Into:
     *   array('a' => 'b', 'c' => 'd')
     *
     * @param $environmentLine
     * @return array
     */
    private function parseEnvironmentLine($environmentLine)
    {
        $variables     = array();@
        $variableLines = explode(' ', $environmentLine ?: '');

        foreach ($variableLines as $variableLine) {
            if (!empty($variableLine)) {
                list($key, $value) = $this->parseEnvironementVariable($variableLine);
                $variables[$key]   = $value;
            }
        }

        return $variables;
    }

    /**
     * Parse an envar
     *
     * @param $envVar
     * @return array<Key, Value>
     */
    private function parseEnvironementVariable($envVar)
    {
        return explode('=', $envVar);
    }

    private function isLanguageVersionSupported($language, $version)
    {
        return file_exists(__DIR__
            . DIRECTORY_SEPARATOR . '..'
            . DIRECTORY_SEPARATOR . '..'
            . DIRECTORY_SEPARATOR . '..'
            . DIRECTORY_SEPARATOR . '..'
            . DIRECTORY_SEPARATOR . 'resources'
            . DIRECTORY_SEPARATOR . 'templates'
            . DIRECTORY_SEPARATOR . $language
            . DIRECTORY_SEPARATOR . 'Dockerfile-' . $version . '.twig');
    }
}


================================================
FILE: src/Joli/JoliCi/Builder/DockerfileBuilder.php
================================================
<?php

namespace Joli\JoliCi\Builder;

use TwigGenerator\Builder\BaseBuilder;

class DockerfileBuilder extends BaseBuilder
{
}


================================================
FILE: src/Joli/JoliCi/Command/CleanCommand.php
================================================
<?php

namespace Joli\JoliCi\Command;

use Joli\JoliCi\Container;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;

class CleanCommand extends Command
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this->setName('clean');
        $this->setDescription('Clean images, containers and/or directories of previous build for this project');
        $this->addOption('project-path', 'p', InputOption::VALUE_OPTIONAL, "Path where you project is (default to current directory)", ".");
        $this->addOption('keep', 'k', InputOption::VALUE_OPTIONAL, "Number of images / containers / directories per build to keep", 1);
        $this->addOption('only-containers', null, InputOption::VALUE_NONE, "Only clean containers (no images or directories)");
        $this->addOption('only-directories', null, InputOption::VALUE_NONE, "Only clean directories (no images or containers)");
        $this->addOption('only-images', null, InputOption::VALUE_NONE, "Only clean images (no containers or directories), be aware that this may fail if containers are still attached to images (you may need to use force option)");
        $this->addOption('force', null, InputOption::VALUE_NONE, "Force removal for images");
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $container = new Container();
        $vacuum = $container->getVacuum();

        if ($input->getOption('only-containers')) {
            $vacuum->cleanContainers($vacuum->getJobsToRemove($input->getOption('project-path'), $input->getOption('keep')));

            return 0;
        }

        if ($input->getOption('only-directories')) {
            $vacuum->cleanDirectories($vacuum->getJobsToRemove($input->getOption('project-path'), $input->getOption('keep')));

            return 0;
        }

        if ($input->getOption('only-images')) {
            $vacuum->cleanImages($vacuum->getJobsToRemove($input->getOption('project-path'), $input->getOption('keep')), $input->getOption('force'));

            return 0;
        }

        $vacuum->clean($input->getOption('project-path'), $input->getOption('keep'), $input->getOption('force'));

        return 0;
    }
}


================================================
FILE: src/Joli/JoliCi/Command/RunCommand.php
================================================
<?php
/*
 * This file is part of JoliCi.
*
* (c) Joel Wurtz <jwurtz@jolicode.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Joli\JoliCi\Command;

use Joli\JoliCi\Container;
use Joli\JoliNotif\Notification;
use Joli\JoliNotif\NotifierFactory;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;

class RunCommand extends Command
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this->setName('run');
        $this->setDescription('Run tests on your project');
        $this->addOption('project-path', 'p', InputOption::VALUE_OPTIONAL, "Path where you project is (default to current directory)", ".");
        $this->addOption('keep', 'k', InputOption::VALUE_OPTIONAL, "Number of images / containers / directories per build to keep when cleaning at the end of run", 1);
        $this->addOption('no-cache', null, InputOption::VALUE_NONE, "Do not use cache of docker");
        $this->addOption('timeout', null, InputOption::VALUE_OPTIONAL, "Timeout for docker client in seconds (default to 5 minutes)", "300");
        $this->addOption('notify', null, InputOption::VALUE_NONE, "Show desktop notifications when a build is finished");
        $this->addArgument('cmd', InputArgument::OPTIONAL, "Override test command");
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $container  = new Container();
        $verbose    = (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity());
        $strategy   = $container->getChainStrategy();
        $executor   = $container->getExecutor(!$input->getOption('no-cache'), $verbose, $input->getOption('timeout'));
        $serviceManager = $container->getServiceManager($verbose);
        $notifier   = NotifierFactory::create();

        $output->writeln("<info>Creating builds...</info>");

        $jobs = $strategy->getJobs($input->getOption("project-path"));

        $output->writeln(sprintf("<info>%s builds created</info>", count($jobs)));

        $exitCode = 0;

        try {
            foreach ($jobs as $job) {
                $output->writeln(sprintf("\n<info>Running job %s</info>\n", $job->getDescription()));

                $serviceManager->start($job);
                $strategy->prepareJob($job);

                $success  = $executor->test($job, $input->getArgument('cmd'));
                $exitCode += $success == 0 ? 0 : 1;

                if ($input->getOption('notify')) {
                    $notification = new Notification();
                    $notification->setBody(sprintf('Test results for %s on %s', $container->getNaming()->getProjectName($input->getOption('project-path')), $job->getDescription()));
                    $notification->setTitle($success == 0 ? 'Tests passed' : 'Tests failed');

                    $notifier->send($notification);
                }

                $serviceManager->stop($job);
            }
        } catch (\Exception $e) {
            // Try stop last builds
            if (isset($job)) {
                $serviceManager->stop($job);
            }
            // We do not deal with exception (Console Component do it well),
            // we just catch it to allow cleaner to be runned even if one of the build failed hard
            // Simulation of a finally for php < 5.6 :-°
        }

        $container->getVacuum()->clean($input->getOption("project-path"), $input->getOption("keep"));

        if (isset($e)) {
            throw $e;
        }

        return $exitCode;
    }
}


================================================
FILE: src/Joli/JoliCi/Command/UpdateImageCommand.php
================================================
<?php

namespace Joli\JoliCi\Command;

use Joli\JoliCi\Container;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;

class UpdateImageCommand extends Command
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this->setName('images-update');
        $this->setDescription('Update docker images used to build test environnement');
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $container = new Container();
        $docker = $container->getDocker();
        $logger = $container->getLoggerCallback((OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()));

        foreach ($docker->getImageManager()->findAll() as $image) {
            if (preg_match('#^jolicode/(.+?)$#', $image->getRepository())) {
                $output->writeln(sprintf("Update %s image", $image->getRepository()));
                $docker->getImageManager()->pull($image->getRepository(), 'latest', $logger->getBuildCallback());
            }
        }
    }
}


================================================
FILE: src/Joli/JoliCi/Container.php
================================================
<?php

namespace Joli\JoliCi;

use Docker\Docker;
use Joli\JoliCi\BuildStrategy\ChainBuildStrategy;
use Joli\JoliCi\Filesystem\Filesystem;
use Joli\JoliCi\Log\SimpleFormatter;
use Joli\JoliCi\BuildStrategy\TravisCiBuildStrategy;
use Joli\JoliCi\BuildStrategy\JoliCiBuildStrategy;
use Joli\JoliCi\Builder\DockerfileBuilder;
use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
use Monolog\Handler\FingersCrossedHandler;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use TwigGenerator\Builder\Generator;

class Container
{
    private $docker;

    /**
     * Strategy based on the ".travis.yml" file
     *
     * @return TravisCiBuildStrategy
     */
    public function getTravisCiStrategy()
    {
        $builder   = new DockerfileBuilder();
        $generator = new Generator();
        $generator->setTemplateDirs(array(
            __DIR__."/../../../resources/templates",
        ));
        $generator->setMustOverwriteIfExists(true);
        $generator->addBuilder($builder);

        return new TravisCiBuildStrategy($builder, $this->getBuildPath(), $this->getNaming(), $this->getFilesystem());
    }

    /**
     * Strategy based on the ".jolici" folder
     *
     * @return JoliCiBuildStrategy
     */
    public function getJoliCiStrategy()
    {
        return new JoliCiBuildStrategy($this->getBuildPath(), $this->getNaming(), $this->getFilesystem());
    }

    /**
     * Chain strategy to allow multiples ones
     *
     * @return ChainBuildStrategy
     */
    public function getChainStrategy()
    {
        $strategy = new ChainBuildStrategy();
        $strategy->pushStrategy($this->getTravisCiStrategy());
        $strategy->pushStrategy($this->getJoliCiStrategy());

        return $strategy;
    }

    /**
     * Alias for the main strategy
     *
     * @return \Joli\JoliCi\BuildStrategy\BuildStrategyInterface
     */
    public function getStrategy()
    {
        return $this->getChainStrategy();
    }

    /**
     * Get a console with finger crossed handler
     *
     * @param bool $verbose
     *
     * @return Logger
     */
    public function getConsoleLogger($verbose = false)
    {
        $logger               = new Logger("standalone-logger");
        $handler              = new StreamHandler("php://stdout", $verbose ? Logger::DEBUG : Logger::INFO);
        $simpleFormatter      = new SimpleFormatter();

        $handler->setFormatter($simpleFormatter);
        $logger->pushHandler($handler);

        if (!$verbose) {
            $stdErrHandler = new StreamHandler("php://stderr", Logger::DEBUG);
            $fingerCrossedHandler = new FingersCrossedHandler($stdErrHandler, new ErrorLevelActivationStrategy(Logger::ERROR), 10);

            $logger->pushHandler($fingerCrossedHandler);
            $stdErrHandler->setFormatter($simpleFormatter);
        }

        return $logger;
    }

    public function getVacuum()
    {
        return new Vacuum($this->getDocker(), $this->getNaming(), $this->getStrategy(), $this->getFilesystem(), $this->getBuildPath());
    }

    public function getFilesystem()
    {
        return new Filesystem();
    }

    public function getDocker()
    {
        if (!$this->docker) {
            $this->docker = new Docker();
        }

        return $this->docker;
    }

    public function getExecutor($cache = true, $verbose = false, $timeout = 600)
    {
        return new Executor($this->getLoggerCallback($verbose), $this->getDocker(), $this->getBuildPath(), $cache, false, $timeout);
    }

    public function getServiceManager($verbose = false)
    {
        return new ServiceManager($this->getDocker(), $this->getLoggerCallback($verbose));
    }

    public function getBuildPath()
    {
        return sys_get_temp_dir().DIRECTORY_SEPARATOR.".jolici-builds";
    }

    public function getNaming()
    {
        return new Naming();
    }

    public function getLoggerCallback($verbose)
    {
        return new LoggerCallback($this->getConsoleLogger($verbose));
    }
}


================================================
FILE: src/Joli/JoliCi/Executor.php
================================================
<?php
/*
 * This file is part of JoliCi.
*
* (c) Joel Wurtz <jwurtz@jolicode.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Joli\JoliCi;

use Docker\API\Model\BuildInfo;
use Docker\API\Model\ContainerConfig;
use Docker\API\Model\HostConfig;
use Docker\Docker;
use Docker\Context\Context;
use Docker\Manager\ContainerManager;
use Docker\Manager\ImageManager;
use Docker\Stream\BuildStream;
use Http\Client\Plugin\Exception\ClientErrorException;
use Monolog\Logger;

class Executor
{
    /**
     * Docker client
     *
     * @var Docker
     */
    protected $docker;

    /**
     * Logger to log message when building
     *
     * @var LoggerCallback
     */
    protected $logger;

    /**
     * @var boolean Use cache when building
     */
    private $usecache = true;

    /**
     * @var boolean Use cache when building
     */
    private $quietBuild = true;

    /**
     * @var integer Default timeout for run
     */
    private $timeout = 600;

    /**
     * @var string Base directory where builds are located
     */
    private $buildPath;

    public function __construct(LoggerCallback $logger, Docker $docker, $buildPath, $usecache = true, $quietBuild = true, $timeout = 600)
    {
        $this->logger     = $logger;
        $this->docker     = $docker;
        $this->usecache   = $usecache;
        $this->quietBuild = $quietBuild;
        $this->timeout    = $timeout;
        $this->buildPath  = $buildPath;
    }

    /**
     * Test a build
     *
     * @param Job $build
     * @param array|string $command
     *
     * @return integer
     */
    public function test(Job $build, $command = null)
    {
        $exitCode = 1;

        if (false !== $this->create($build)) {
            $exitCode = $this->run($build, $command);
        }

        return $exitCode;
    }

    /**
     * Create a build
     *
     * @param Job $job Build used to create image
     *
     * @return \Docker\API\Model\Image|boolean Return the image created if successful or false otherwise
     */
    public function create(Job $job)
    {
        $context  = new Context($this->buildPath . DIRECTORY_SEPARATOR . $job->getDirectory());

        $buildStream = $this->docker->getImageManager()->build($context->toStream(), [
            't' => $job->getName(),
            'q' => $this->quietBuild,
            'nocache' => !$this->usecache
        ], ImageManager::FETCH_STREAM);

        $buildStream->onFrame($this->logger->getBuildCallback());
        $buildStream->wait();

        try {
            return $this->docker->getImageManager()->find($job->getName());
        } catch (ClientErrorException $e) {
            if ($e->getResponse()->getStatusCode() == 404) {
                return false;
            }

            throw $e;
        }
    }

    /**
     * Run a build (it's suppose the image exist in docker
     *
     * @param Job $job Build to run
     * @param string|array $command Command to use when run the build (null, by default, will use the command registered to the image)
     *
     * @return integer The exit code of the command run inside (0 = success, otherwise it has failed)
     */
    public function run(Job $job, $command)
    {
        if (is_string($command)) {
            $command = ['/bin/bash', '-c', $command];
        }

        $image = $this->docker->getImageManager()->find($job->getName());

        $hostConfig = new HostConfig();

        $config = new ContainerConfig();
        $config->setCmd($command);
        $config->setImage($image->getId());
        $config->setHostConfig($hostConfig);
        $config->setLabels(new \ArrayObject([
            'com.jolici.container=true'
        ]));
        $config->setAttachStderr(true);
        $config->setAttachStdout(true);

        $links = [];

        foreach ($job->getServices() as $service) {
            if ($service->getContainer()) {
                $serviceContainer = $this->docker->getContainerManager()->find($service->getContainer());

                $links[] = sprintf('%s:%s', $serviceContainer->getName(), $service->getName());
            }
        }

        $hostConfig->setLinks($links);

        $containerCreateResult = $this->docker->getContainerManager()->create($config);
        $attachStream = $this->docker->getContainerManager()->attach($containerCreateResult->getId(), [
            'stream' => true,
            'stdout' => true,
            'stderr' => true,
        ], ContainerManager::FETCH_STREAM);

        $attachStream->onStdout($this->logger->getRunStdoutCallback());
        $attachStream->onStderr($this->logger->getRunStderrCallback());

        $this->docker->getContainerManager()->start($containerCreateResult->getId());

        $attachStream->wait();

        $containerWait = $this->docker->getContainerManager()->wait($containerCreateResult->getId());

        return $containerWait->getStatusCode();
    }
}


================================================
FILE: src/Joli/JoliCi/Filesystem/Filesystem.php
================================================
<?php
/*
 * This file is part of JoliCi.
 *
 * (c) Joel Wurtz <jwurtz@jolicode.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Joli\JoliCi\Filesystem;

use Symfony\Component\Filesystem\Filesystem as BaseFilesystem;

class Filesystem extends BaseFilesystem
{
    /**
     * Add keeping same permissions as origin file
     *
     * @see \Symfony\Component\Filesystem\Filesystem::copy()
     *
     * @param string  $originFile
     * @param string  $targetFile
     * @param Boolean $override
     */
    public function copy($originFile, $targetFile, $override = false)
    {
        parent::copy($originFile, $targetFile, $override);

        $this->chmod($targetFile, fileperms($originFile));
    }
}


================================================
FILE: src/Joli/JoliCi/Job.php
================================================
<?php
/*
 * This file is part of JoliCi.
*
* (c) Joel Wurtz <jwurtz@jolicode.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Joli\JoliCi;

class Job
{
    const BASE_NAME = 'jolici';

    /**
     * @var string Name of job
     */
    protected $project;

    /**
     * @var string Strategy associated with this job
     */
    protected $strategy;

    /**
     * @var string Uniq key for this "kind" of job
     *
     * This key is not a identifier (the name is the identifier in a job), it's more like a category,
     * job with same parameters MUST have the same uniq key, this key is use to track the history
     * of a job over time (for cleaning, reports, etc ....)
     */
    protected $uniq;

    /**
     * @var array Parameters of this job
     *
     * It mainly depend on the strategy, ie for TravisCi strategy this will include the language used, version used, etc ...
     */
    protected $parameters;

    /**
     * @var string Description of this job (generally a nice name for end user)
     */
    protected $description;

    /**
     * @var \DateTime Date of creation of the job
     */
    protected $created;

    /**
     * @var Service[] Services linked to this job
     */
    private $services = array();

    /**
     * @param string    $project     Project of the job
     * @param string    $strategy    Strategy of the job
     * @param string    $uniq        A uniq identifier for this kind of job
     * @param array     $parameters  Parameters of the job (mainly depend on the strategy)
     * @param string    $description Description of this job (generally a nice name for end user)
     * @param \DateTime $created     Date of creation of the job
     * @param array $services Services linked to the job
     */
    public function __construct($project, $strategy, $uniq, $parameters = array(), $description = "", $created = null, $services = array())
    {
        $this->project     = $project;
        $this->description = $description;
        $this->strategy    = $strategy;
        $this->parameters  = $parameters;
        $this->uniq        = $uniq;

        if (null === $created) {
            $created = new \DateTime();
        }

        $this->created = $created;
        $this->services = $services;
    }

    /**
     * Get name of this job
     *
     * @return string
     */
    public function getName()
    {
        return sprintf('%s:%s', $this->getRepository(), $this->getTag());
    }

    /**
     * Get repository name for docker images job with this strategy
     *
     * @return string
     */
    public function getRepository()
    {
        return sprintf('%s_%s/%s', static::BASE_NAME, strtolower($this->strategy), $this->project);
    }

    /**
     * Generate the tag name for a docker image
     *
     * @return string
     */
    public function getTag()
    {
        return sprintf('%s-%s', $this->uniq, $this->created->format('U'));
    }

    /**
     * Add a service to the job
     *
     * @param Service $service
     */
    public function addService(Service $service)
    {
        $this->services[] = $service;
    }

    /**
     * Return all services linked to this job
     *
     * @return Service[]
     */
    public function getServices()
    {
        return $this->services;
    }

    /**
     * Return directory of job
     *
     * @return string
     */
    public function getDirectory()
    {
        return $this->getName();
    }

    /**
     * @return string
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * @return string
     */
    public function getStrategy()
    {
        return $this->strategy;
    }

    /**
     * @return string
     */
    public function getUniq()
    {
        return $this->uniq;
    }

    /**
     * @return array
     */
    public function getParameters()
    {
        return $this->parameters;
    }

    /**
     * @return \DateTime
     */
    public function getCreated()
    {
        return $this->created;
    }

    public function __toString()
    {
        return $this->getName();
    }
}


================================================
FILE: src/Joli/JoliCi/Log/SimpleFormatter.php
================================================
<?php
/*
 * This file is part of JoliCi.
*
* (c) Joel Wurtz <jwurtz@jolicode.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Joli\JoliCi\Log;

use Monolog\Formatter\FormatterInterface;

class SimpleFormatter implements FormatterInterface
{
    private $static = array();

    private $lineNumber = 0;

    /*
     * (non-PHPdoc) @see \Monolog\Formatter\FormatterInterface::format()
     */
    public function format(array $record)
    {
        if (isset($record['context']['clear-static'])) {
            $this->static = array();

            return "";
        }

        $message = $record['message'];

        if (isset($record['context']['static']) && $record['context']['static']) {
            $id      = $record['context']['static-id'];

            if (!isset($this->static[$id])) {
                $this->static[$id] = array(
                    'current_line' => $this->lineNumber,
                    'message'      => $message,
                );

                $message = sprintf("%s\n", $message);
            } else {
                $diff                         = ($this->lineNumber - $this->static[$id]['current_line']);
                $lastMessage                  = $this->static[$id]['message'];
                $this->static[$id]['message'] = $message;

                //Add space to replace old string in message
                if (mb_strlen($lastMessage) > mb_strlen($message)) {
                    $message = str_pad($message, mb_strlen($lastMessage), " ", STR_PAD_RIGHT);
                }

                $message = sprintf("\x0D\x1B[%sA%s\x1B[%sB\x0D", $diff, $message, $diff);
            }
        }

        if (preg_match('#\n#', $message)) {
            $this->lineNumber++;
        }

        return $message;
    }

    /*
     * (non-PHPdoc) @see \Monolog\Formatter\FormatterInterface::formatBatch()
     */
    public function formatBatch(array $records)
    {
        $message = '';
        foreach ($records as $record) {
            $message .= $this->format($record);
        }

        return $message;
    }
}


================================================
FILE: src/Joli/JoliCi/LoggerCallback.php
================================================
<?php

namespace Joli\JoliCi;

use Docker\API\Model\BuildInfo;
use Psr\Log\LoggerInterface;

class LoggerCallback
{
    /**
     * @var \Closure
     */
    private $buildCallback;

    /**
     * @var \Closure
     */
    private $runStdoutCallback;

    /**
     * @var \Closure
     */
    private $runStderrCallback;

    /**
     * @var LoggerInterface
     */
    private $logger;

    public function __construct(LoggerInterface $logger)
    {
        $build     = new \ReflectionMethod($this, 'buildCallback');
        $runStdout = new \ReflectionMethod($this, 'runStdoutCallback');
        $runStderr = new \ReflectionMethod($this, 'runStderrCallback');

        $this->buildCallback = $build->getClosure($this);
        $this->runStdoutCallback = $runStdout->getClosure($this);
        $this->runStderrCallback = $runStderr->getClosure($this);
        $this->logger = $logger;
    }

    /**
     * Get the build log callback when building / pulling an image
     *
     * @return callable
     */
    public function getBuildCallback()
    {
        return $this->buildCallback;
    }

    /**
     * Get the run stdout callback for docker
     *
     * @return callable
     */
    public function getRunStdoutCallback()
    {
        return $this->runStdoutCallback;
    }

    /**
     * Get the run stderr callback for docker
     *
     * @return callable
     */
    public function getRunStderrCallback()
    {
        return $this->runStderrCallback;
    }

    /**
     * Clear static log buffer
     */
    public function clearStatic()
    {
        $this->logger->debug("", array('clear-static' => true));
    }

    /**
     * The build callback when creating a image, useful to see what happens during building
     *
     * @param BuildInfo $output An encoded json string from docker daemon
     */
    private function buildCallback(BuildInfo $output)
    {
        $message = "";

        if ($output->getError()) {
            $this->logger->error(sprintf("Error when creating job: %s\n", $output->getError()), array('static' => false, 'static-id' => null));
            return;
        }

        if ($output->getStream()) {
            $message = $output->getStream();
        }

        if ($output->getStatus()) {
            $message = $output->getStatus();

            if ($output->getProgress()) {
                $message .= " " . $output->getProgress();
            }
        }

        // Force new line
        if (!$output->getId() && !preg_match('#\n#', $message)) {
            $message .= "\n";
        }

        $this->logger->debug($message, array(
            'static' => $output->getId() !== null,
            'static-id' => $output->getId(),
        ));
    }

    /**
     * Run callback to catch stdout logs of test running
     *
     * @param string $output Output from run (stdout)
     */
    private function runStdoutCallback($output)
    {
        $this->logger->info($output);
    }

    /**
     * Run callback to catch stderr logs of test running
     *
     * @param string $output Output from run (stderr)
     */
    private function runStderrCallback($output)
    {
        $this->logger->error($output);
    }
}


================================================
FILE: src/Joli/JoliCi/Matrix.php
================================================
<?php
/*
 * This file is part of JoliCi.
 *
 * (c) Joel Wurtz <jwurtz@jolicode.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Joli\JoliCi;

/**
 * Create the Job list by computing all available possibility through dimensions
 */
class Matrix
{
    private $dimensions = array();

    /**
     * Set a dimension for this matrix
     *
     * @param string $name   Name of the dimension
     * @param array  $values Value for this dimension
     */
    public function setDimension($name, array $values)
    {
        if (empty($values)) {
            $values = array(null);
        }

        $this->dimensions[$name] = $values;
    }

    /**
     * Return all possibility for the matrix
     *
     * @return array
     */
    public function compute()
    {
        $dimensions = $this->dimensions;

        if (empty($dimensions)) {
            return array();
        }

        // Pop first dimension
        $values = reset($dimensions);
        $name   = key($dimensions);
        unset($dimensions[$name]);

        // Create all possiblites for the first dimension
        $posibilities = array();

        foreach ($values as $v) {
            $posibilities[] = array($name => $v);
        }

        // If only one dimension return simple all the possibilites created (break point of recursivity)
        if (empty($dimensions)) {
            return $posibilities;
        }

        // If not create a new matrix with remaining dimension
        $matrix = new Matrix();

        foreach ($dimensions as $name => $values) {
            $matrix->setDimension($name, $values);
        }

        $result    = $matrix->compute();
        $newResult = array();

        foreach ($result as $value) {
            foreach ($posibilities as $possiblity) {
                $newResult[] = $value + $possiblity;
            }
        }

        return $newResult;
    }
}


================================================
FILE: src/Joli/JoliCi/Naming.php
================================================
<?php

namespace Joli\JoliCi;

use Behat\Transliterator\Transliterator;

class Naming
{
    /**
     * Return a translitared name for a project
     *
     * @param string $projectPath Project directory
     *
     * @return string
     */
    public function getProjectName($projectPath)
    {
        $project = basename(realpath($projectPath));
        $project = Transliterator::transliterate($project, '-');

        return $project;
    }

    /**
     * Generate a unique key for a list of parameters, with same parameters
     * the key must not change (no random stuff) and the order does not matter
     *
     * @param array $parameters
     *
     * @return integer
     */
    public function getUniqueKey($parameters = array())
    {
        // First ordering parameters
        ksort($parameters);

        // Return a hash of the serialzed parameters
        return crc32(serialize($parameters));
    }
}


================================================
FILE: src/Joli/JoliCi/Service.php
================================================
<?php

namespace Joli\JoliCi;

use Docker\Container as DockerContainer;

/**
 * A service is just an application or a tool link to a build which helps running tests
 *
 * It can be for example a MySQL database which contains the needed fixtures in order
 * to make functional tests
 *
 * Multiple services can be link to a Job and they are started before creation of the Job.
 * Once the Job is finished, all services linkes are shutdown and reset to initial state for subsequent Job
 */
class Service
{
    /**
     * @var string Service name (use in link to container)
     */
    private $name;

    /**
     * @var string Repository for this service (from docker hub)
     */
    private $repository;

    /**
     * @var string Tag for this service (generally the version)
     */
    private $tag;

    /**
     * @var array Config when creating a container
     */
    private $config;

    /**
     * @var string Container id used for this service
     */
    private $container;

    public function __construct($name, $repository, $tag, $config = array())
    {
        $this->name       = $name;
        $this->repository = $repository;
        $this->tag        = $tag;
        $this->config     = $config;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @return string
     */
    public function getRepository()
    {
        return $this->repository;
    }

    /**
     * @return string
     */
    public function getTag()
    {
        return $this->tag;
    }

    /**
     * @return array
     */
    public function getConfig()
    {
        return $this->config;
    }

    /**
     * @return string The container id
     */
    public function getContainer()
    {
        return $this->container;
    }

    /**
     * @param string $container The container id
     */
    public function setContainer($container)
    {
        $this->container = $container;
    }
}


================================================
FILE: src/Joli/JoliCi/ServiceManager.php
================================================
<?php

namespace Joli\JoliCi;

use Docker\API\Model\ContainerConfig;
use Docker\Container as DockerContainer;
use Docker\Docker;
use Docker\Exception\ImageNotFoundException;
use Docker\Exception\UnexpectedStatusCodeException;
use Docker\Manager\ImageManager;
use Http\Client\Plugin\Exception\ClientErrorException;
use Psr\Log\LoggerInterface;

class ServiceManager
{
    private $docker;

    private $logger;

    public function __construct(Docker $docker, LoggerCallback $logger)
    {
        $this->docker = $docker;
        $this->logger = $logger;
    }

    /**
     * Start services for a Job
     *
     * @param Job $build
     */
    public function start(Job $build)
    {
        foreach ($build->getServices() as $service) {
            try {
                $this->docker->getImageManager()->find(sprintf('%s:%s', $service->getRepository(), $service->getTag()));
            } catch (ClientErrorException $e) {
                if ($e->getResponse()->getStatusCode() == 404) {
                    $buildStream = $this->docker->getImageManager()->create(null, [
                        'fromImage' => sprintf('%s:%s', $service->getRepository(), $service->getTag())
                    ], ImageManager::FETCH_STREAM);

                    $buildStream->onFrame($this->logger->getBuildCallback());
                    $buildStream->wait();
                } else {
                    throw $e;
                }
            }

            $serviceConfig = $service->getConfig();
            $containerConfig = new ContainerConfig();
            $containerConfig->setImage(sprintf('%s:%s', $service->getRepository(), $service->getTag()));
            $containerConfig->setLabels([
                'com.jolici.container=true'
            ]);

            if (isset($serviceConfig['Env'])) {
                $containerConfig->setEnv($serviceConfig['Env']);
            }

            $containerCreateResult = $this->docker->getContainerManager()->create($containerConfig);
            $this->docker->getContainerManager()->start($containerCreateResult->getId());
            $service->setContainer($containerCreateResult->getId());
        }
    }

    /**
     * Stop services for a Job and reinit volumes
     *
     * @param Job $job     The job to stop services
     * @param int $timeout Timeout to wait before killing the service
     */
    public function stop(Job $job, $timeout = 10)
    {
        foreach ($job->getServices() as $service) {
            if ($service->getContainer()) {
                try {
                    $this->docker->getContainerManager()->stop($service->getContainer(), [
                        't' => $timeout
                    ]);
                } catch (ClientErrorException $e) {
                    if ($e->getResponse()->getStatusCode() != 304) {
                        throw $e;
                    }
                }

                $this->docker->getContainerManager()->remove($service->getContainer(), [
                    'v' => true,
                    'force' => true
                ]);

                $service->setContainer(null);
            }
        }
    }
}


================================================
FILE: src/Joli/JoliCi/Vacuum.php
================================================
<?php

namespace Joli\JoliCi;

use Docker\API\Model\ImageItem;
use Docker\Docker;
use Docker\Image;
use Joli\JoliCi\BuildStrategy\BuildStrategyInterface;
use Joli\JoliCi\Filesystem\Filesystem;

class Vacuum
{
    /**
     * @var \Docker\Docker Docker API Client
     */
    private $docker;

    /**
     * @var Naming Service use to create name for image and project
     */
    private $naming;

    /**
     * @var BuildStrategy\BuildStrategyInterface Strategy where we want to clean the builds
     */
    private $strategy;

    /**
     * @var string Location where the build are
     */
    private $buildPath;

    /**
     * @var \Joli\JoliCi\Filesystem\Filesystem
     */
    private $filesystem;

    /**
     * @param Docker                 $docker     Docker API Client
     * @param Naming                 $naming     Naming service
     * @param BuildStrategyInterface $strategy   Strategy used to create builds
     * @param Filesystem             $filesystem Filesystem service
     * @param string                 $buildPath  Directory where builds are created
     */
    public function __construct(Docker $docker, Naming $naming, BuildStrategyInterface $strategy, Filesystem $filesystem, $buildPath)
    {
        $this->docker     = $docker;
        $this->naming     = $naming;
        $this->strategy   = $strategy;
        $this->buildPath  = $buildPath;
        $this->filesystem = $filesystem;
    }

    /**
     * Clean containers, images and directory from a project
     *
     * @param string  $projectPath Location of the project
     * @param int     $keep        How many versions does we need to keep (1 is the default in order to have cache for each test)
     * @param boolean $force       Force removal for images
     */
    public function clean($projectPath, $keep = 1, $force = false)
    {
        $builds = $this->getJobsToRemove($projectPath, $keep);

        $this->cleanDirectories($builds);
        $this->cleanContainers($builds);
        $this->cleanImages($builds, $force);
    }

    /**
     * Clean directories for given builds
     *
     * @param \Joli\JoliCi\Job[] $jobs A list of jobs to remove images from
     */
    public function cleanDirectories($jobs = array())
    {
        foreach ($jobs as $job) {
            $this->filesystem->remove($job->getDirectory());
        }
    }

    /**
     * Clean images for given builds
     *
     * @param \Joli\JoliCi\Job[] $jobs A list of jobs to remove images from
     */
    public function cleanContainers($jobs = array())
    {
        $images     = array();
        $containers = array();

        foreach ($jobs as $job) {
            if (isset($job->getParameters()['image'])) {
                $images[] = $job->getParameters()['image'];
            } else {
                $images[] = sprintf('%s:%s', $job->getRepository(), $job->getTag());
            }
        }

        foreach ($this->docker->getContainerManager()->findAll(['all' => 1]) as $container) {
            $imageName = $container->getImage();

            foreach ($images as $image) {
                if ($image == $imageName) {
                    $containers[] = $container;
                }

                if (preg_match('#^'.$imageName.'#', $image->getId())) {
                    $containers[] = $container;
                }
            }
        }

        foreach ($containers as $container) {
            $this->docker->getContainerManager()->remove($container->getId(), [
                'v' => true,
                'force' => true
            ]);
        }
    }

    /**
     * Clean images for given builds
     *
     * @param \Joli\JoliCi\Job[] $jobs   A list of jobs to remove images from
     * @param boolean            $force  Force removal for images
     */
    public function cleanImages($jobs = array(), $force = false)
    {
        foreach ($jobs as $job) {
            $this->docker->getImageManager()->remove(sprintf('%s:%s', $job->getRepository(), $job->getTag()), [
                'force' => $force
            ]);
        }
    }

    /**
     * Get all jobs to remove given a project and how many versions to keep
     *
     * @param string $projectPath The project path
     * @param int    $keep        Number of project to keep
     *
     * @return \Joli\JoliCi\Job[] A list of jobs to remove
     */
    public function getJobsToRemove($projectPath, $keep = 1)
    {
        $currentJobs  = $this->strategy->getJobs($projectPath);
        $existingJobs = $this->getJobs($projectPath);
        $uniqList = array();
        $removes  = array();
        $ordered  = array();

        foreach ($currentJobs as $job) {
            $uniqList[] = $job->getUniq();
        }

        // Remove not existant image (old build configuration)
        foreach ($existingJobs as $job) {
            if (!in_array($job->getUniq(), $uniqList)) {
                $removes[] = $job;
            } else {
                $ordered[$job->getUniq()][$job->getCreated()->format('U')] = $job;
            }
        }

        // Remove old image
        foreach ($ordered as $jobs) {
            ksort($jobs);
            $keeped = count($jobs);

            while ($keeped > $keep) {
                $removes[] = array_shift($jobs);
                $keeped--;
            }
        }

        return $removes;
    }

    /**
     * Get all jobs related to a project
     *
     * @param string $projectPath Directory where the project is
     *
     * @return \Joli\JoliCi\Job[]
     */
    protected function getJobs($projectPath)
    {
        $jobs            = array();
        $project         = $this->naming->getProjectName($projectPath);
        $repositoryRegex = sprintf('#^%s_([a-z]+?)/%s:\d+-\d+$#', Job::BASE_NAME, $project);

        foreach ($this->docker->getImageManager()->findAll() as $image) {
            foreach ($image->getRepoTags() as $name) {
                if (preg_match($repositoryRegex, $name, $matches)) {
                    $jobs[] = $this->getJobFromImage($image, $name, $matches[1], $project);
                }
            }
        }

        return $jobs;
    }

    /**
     * Create a job from a docker image
     *
     * @param ImageItem $image
     * @param string    $strategy
     * @param string    $project
     *
     * @return \Joli\JoliCi\Job
     */
    protected function getJobFromImage(ImageItem $image, $imageName, $strategy, $project)
    {
        $tag = explode(':', $imageName)[1];
        list($uniq, $timestamp)     = explode('-', $tag);

        return new Job($project, $strategy, $uniq, array('image' => $image), "", \DateTime::createFromFormat('U', $timestamp));
    }
}


================================================
FILE: tests/Joli/JoliCi/BuildStrategy/ChainBuildStrategyTest.php
================================================
<?php

namespace Joli\JoliCi\BuildStrategy;

use Joli\JoliCi\Job;

class ChainBuildStrategyTest extends \PHPUnit_Framework_TestCase
{
    public function testGetJobs()
    {
        $builder = new ChainBuildStrategy();
        $builder->pushStrategy(new FooBuildStrategy());

        $jobs = $builder->getJobs("test");

        $this->assertCount(1, $jobs);

        $job = $jobs[0];

        $this->assertEquals("test", $job->getStrategy());
        $this->assertContains("jolici_test/test:test-", $job->getName());
        $this->assertContains("jolici_test/test:test-", $job->getDirectory());
    }

    public function testNoJobsEmpty()
    {
        $builder = new ChainBuildStrategy();
        $builder->pushStrategy(new NoBuildStrategy());

        $jobs = $builder->getJobs("test");

        $this->assertCount(0, $jobs);
    }
}

class FooBuildStrategy implements BuildStrategyInterface
{
    public function getJobs($directory)
    {
        return array(new Job("test", "test", "test"));
    }

    public function getName()
    {
        return "dummy";
    }

    public function prepareJob(Job $job)
    {
    }

    public function supportProject($directory)
    {
        return true;
    }
}

class NoBuildStrategy extends FooBuildStrategy
{
    public function supportProject($directory)
    {
        return false;
    }
}


================================================
FILE: tests/Joli/JoliCi/BuildStrategy/JoliCiBuildStrategyTest.php
================================================
<?php

namespace Joli\JoliCi;

use Joli\JoliCi\BuildStrategy\JoliCiBuildStrategy;
use Joli\JoliCi\Filesystem\Filesystem;
use org\bovigo\vfs\vfsStream;

class JoliCiBuildStrategyTest extends \PHPUnit_Framework_TestCase
{
    public function setUp()
    {
        $this->buildPath = vfsStream::setup('build-path');
        $this->strategy = new JoliCiBuildStrategy(vfsStream::url('build-path'), new Naming(), new Filesystem());
    }

    public function testCreateJobs()
    {
        $jobs  = $this->strategy->getJobs(__DIR__.DIRECTORY_SEPARATOR."fixtures".DIRECTORY_SEPARATOR."jolici".DIRECTORY_SEPARATOR."project1");

        $this->assertCount(1, $jobs);
    }

    public function testPrepareJob()
    {
        $jobs = $this->strategy->getJobs(__DIR__.DIRECTORY_SEPARATOR."fixtures".DIRECTORY_SEPARATOR."jolici".DIRECTORY_SEPARATOR."project1");
        $job  = $jobs[0];

        $this->strategy->prepareJob($job);

        $this->assertTrue($this->buildPath->hasChild(vfsStream::path(vfsStream::url('build-path') . DIRECTORY_SEPARATOR . $job->getDirectory())));
        $this->assertTrue($this->buildPath->hasChild(vfsStream::path(vfsStream::url('build-path') . DIRECTORY_SEPARATOR . $job->getDirectory())."/Dockerfile"));
        $this->assertTrue($this->buildPath->hasChild(vfsStream::path(vfsStream::url('build-path') . DIRECTORY_SEPARATOR . $job->getDirectory())."/foo"));
        $this->assertTrue($this->buildPath->hasChild(vfsStream::path(vfsStream::url('build-path') . DIRECTORY_SEPARATOR . $job->getDirectory())."/.jolici"));
        $this->assertTrue($this->buildPath->hasChild(vfsStream::path(vfsStream::url('build-path') . DIRECTORY_SEPARATOR . $job->getDirectory())."/.jolici/test"));
        $this->assertTrue($this->buildPath->hasChild(vfsStream::path(vfsStream::url('build-path') . DIRECTORY_SEPARATOR . $job->getDirectory())."/.jolici/test/Dockerfile"));
    }

    public function testSupportTrue()
    {
        $support = $this->strategy->supportProject(__DIR__.DIRECTORY_SEPARATOR."fixtures".DIRECTORY_SEPARATOR."jolici".DIRECTORY_SEPARATOR."project1");

        $this->assertTrue($support);
    }

    public function testSupportFalse()
    {
        $support = $this->strategy->supportProject(__DIR__.DIRECTORY_SEPARATOR."fixtures".DIRECTORY_SEPARATOR."jolici".DIRECTORY_SEPARATOR."project2");

        $this->assertFalse($support);
    }
}


================================================
FILE: tests/Joli/JoliCi/BuildStrategy/TravisCIBuildStrategyTest.php
================================================
<?php

namespace Joli\JoliCi\BuildStrategy;

use Joli\JoliCi\Filesystem\Filesystem;
use Joli\JoliCi\Naming;
use org\bovigo\vfs\vfsStream;
use Joli\JoliCi\Builder\DockerfileBuilder;

class TravisCIBuildStrategyTest extends \PHPUnit_Framework_TestCase
{
    public function setUp()
    {
        $this->buildPath = vfsStream::setup('build-path');
        $this->strategy = new TravisCiBuildStrategy(new DockerfileBuilder(), vfsStream::url('build-path'), new Naming(), new Filesystem());
    }

    public function testSupportTrue()
    {
        $support = $this->strategy->supportProject(__DIR__.DIRECTORY_SEPARATOR."fixtures".DIRECTORY_SEPARATOR."travisci".DIRECTORY_SEPARATOR."project1");

        $this->assertTrue($support);
    }

    public function testSupportFalse()
    {
        $support = $this->strategy->supportProject(__DIR__.DIRECTORY_SEPARATOR."fixtures".DIRECTORY_SEPARATOR."travisci".DIRECTORY_SEPARATOR."project2");

        $this->assertFalse($support);
    }

    /**
     * @dataProvider createMatrixVersionDataProvider
     */
    public function testCreateMatrixCanObtainVersions($versions)
    {
        $testConfig = [
            'language' => 'php',
            'php'      => $versions,
        ];

        $createMatrix = function ($config) {
            return $this->createMatrix($config);
        };
        $createMatrix = $createMatrix->bindTo($this->strategy, $this->strategy);

        $matrix = $createMatrix($testConfig);

        $this->assertAttributeContains((array) $versions, 'dimensions', $matrix);
    }

    /**
     * @return array
     */
    public function createMatrixVersionDataProvider()
    {
        return [
            // Test with float
            [5.5],
            // Test with string
            ['5.5'],
            // Test with list
            [['5.5', '5.6', '7']]
        ];
    }
}


================================================
FILE: tests/Joli/JoliCi/BuildStrategy/fixtures/jolici/project1/.jolici/test/Dockerfile
================================================
FROM ubuntu

CMD echo 'lol'

================================================
FILE: tests/Joli/JoliCi/BuildStrategy/fixtures/jolici/project1/foo
================================================
bar

================================================
FILE: tests/Joli/JoliCi/BuildStrategy/fixtures/jolici/project2/.gitkeep
================================================


================================================
FILE: tests/Joli/JoliCi/BuildStrategy/fixtures/travisci/project1/.travis.yml
================================================
language: php

php:
  - 5.3
  - 5.4
  - 5.5

before_script:
    - git config --global user.name travis-ci
    - git config --global user.email travis@example.com

script:
    - ./vendor/bin/phpunit

================================================
FILE: tests/Joli/JoliCi/JobTest.php
================================================
<?php

namespace Joli\JoliCi;

class JobTest extends \PHPUnit_Framework_TestCase
{
    public function testConstructInitialisesAllTheFields()
    {
        $projectMock     = uniqid();
        $strategyMock    = uniqid();
        $uniqMock        = uniqid();
        $parametersMock  = array('test' => uniqid());
        $descriptionMock = 'test';
        $createdMock     = new \DateTime();
        $servicesMock    = array('service' => uniqid());

        $jobMock = new Job(
            $projectMock,
            $strategyMock,
            $uniqMock,
            $parametersMock,
            $descriptionMock,
            $createdMock,
            $servicesMock
        );

        $this->assertAttributeSame($projectMock, 'project', $jobMock);
        $this->assertSame($strategyMock, $jobMock->getStrategy());
        $this->assertSame($uniqMock, $jobMock->getUniq());
        $this->assertSame($parametersMock, $jobMock->getParameters());
        $this->assertSame($descriptionMock, $jobMock->getDescription());
        $this->assertSame($createdMock, $jobMock->getCreated());
        $this->assertSame($servicesMock, $jobMock->getServices());

        return $jobMock;
    }

    /**
     * @param Job $jobMock
     * @depends testConstructInitialisesAllTheFields
     */
    public function testAddAndGetServices($jobMock)
    {
        $serviceMock     = new Service('test', 'test', 'test');
        $currentServices = $jobMock->getServices();

        $jobMock->addService($serviceMock);

        $this->assertEquals(
            array_merge($currentServices, array($serviceMock)),
            $jobMock->getServices()
        );
    }

    public function testToStringReturnsTheNameOfTheJob()
    {
        $jobMock = new Job('project', 'strategy', uniqid());

        $this->assertEquals($jobMock->getName(), $jobMock->__toString());
    }
}


================================================
FILE: tests/Joli/JoliCi/MatrixTest.php
================================================
<?php

namespace Joli\JoliCi;

class MatrixTest extends \PHPUnit_Framework_TestCase
{
    public function testSetDimensionWillUseDefaultValuesIfEmptyProvided()
    {
        $matrixMock = new Matrix();

        $matrixMock->setDimension('test', array());

        $this->assertAttributeContains(array(null), 'dimensions', $matrixMock);
    }

    public function testComputeWillReturnEmptyIfNoDimensions()
    {
        $matrixMock = new Matrix();

        $this->assertEquals(array(), $matrixMock->compute());
    }

    public function testCompute()
    {
        $matrix = new Matrix();
        $matrix->setDimension('a', array(1, 2, 3));
        $matrix->setDimension('b', array(1, 2, 3));
        $matrix->setDimension('c', array(1, 2, 3));

        $possibilities = $matrix->compute();

        $expected = array(
            array('a' => 1, 'b' => 1, 'c' => 1),
            array('a' => 1, 'b' => 1, 'c' => 2),
            array('a' => 1, 'b' => 1, 'c' => 3),

            array('a' => 1, 'b' => 2, 'c' => 1),
            array('a' => 1, 'b' => 2, 'c' => 2),
            array('a' => 1, 'b' => 2, 'c' => 3),

            array('a' => 1, 'b' => 3, 'c' => 1),
            array('a' => 1, 'b' => 3, 'c' => 2),
            array('a' => 1, 'b' => 3, 'c' => 3),

            array('a' => 2, 'b' => 1, 'c' => 1),
            array('a' => 2, 'b' => 1, 'c' => 2),
            array('a' => 2, 'b' => 1, 'c' => 3),

            array('a' => 2, 'b' => 2, 'c' => 1),
            array('a' => 2, 'b' => 2, 'c' => 2),
            array('a' => 2, 'b' => 2, 'c' => 3),

            array('a' => 2, 'b' => 3, 'c' => 1),
            array('a' => 2, 'b' => 3, 'c' => 2),
            array('a' => 2, 'b' => 3, 'c' => 3),

            array('a' => 3, 'b' => 1, 'c' => 1),
            array('a' => 3, 'b' => 1, 'c' => 2),
            array('a' => 3, 'b' => 1, 'c' => 3),

            array('a' => 3, 'b' => 2, 'c' => 1),
            array('a' => 3, 'b' => 2, 'c' => 2),
            array('a' => 3, 'b' => 2, 'c' => 3),

            array('a' => 3, 'b' => 3, 'c' => 1),
            array('a' => 3, 'b' => 3, 'c' => 2),
            array('a' => 3, 'b' => 3, 'c' => 3),
        );

        $this->assertCount(count($expected), $possibilities);

        foreach ($expected as $value) {
            $this->assertContains($value, $possibilities);
        }
    }
}


================================================
FILE: tests/Joli/JoliCi/ServiceTest.php
================================================
<?php

namespace Joli\JoliCi;

class ServiceTest extends \PHPUnit_Framework_TestCase
{
    public function testConstructInitialisesAllTheFields()
    {
        $nameMock       = uniqid();
        $repositoryMock = uniqid();
        $tagMock        = uniqid();
        $configMock     = array('test' => uniqid());

        $serviceMock = new Service($nameMock, $repositoryMock, $tagMock, $configMock);

        $this->assertSame($nameMock, $serviceMock->getName());
        $this->assertSame($repositoryMock, $serviceMock->getRepository());
        $this->assertSame($tagMock, $serviceMock->getTag());
        $this->assertSame($configMock, $serviceMock->getConfig());

        return $serviceMock;
    }

    /**
     * @param Service $serviceMock
     * @depends testConstructInitialisesAllTheFields
     */
    public function testSetAndGetContainer($serviceMock)
    {
        $dockerContainerMock =
            $this->getMockBuilder('Docker\Container')
                 ->setMethods(null)
                 ->getMock();

        $serviceMock->setContainer($dockerContainerMock);

        $this->assertSame($dockerContainerMock, $serviceMock->getContainer());
    }
}
Download .txt
gitextract_ckyocog0/

├── .gitignore
├── .gush.yml
├── .pharcc.yml
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── bin/
│   └── jolici
├── composer.json
├── docs/
│   ├── command/
│   │   ├── clean.md
│   │   ├── run.md
│   │   └── updates-image.md
│   ├── installation.md
│   └── strategies/
│       ├── JoliCiStrategy.md
│       └── TravisCiStrategy.md
├── phpunit.xml
├── resources/
│   └── templates/
│       ├── Dockerfile-travis.twig
│       ├── Dockerfile.twig
│       ├── node_js/
│       │   ├── Dockerfile-0.10.twig
│       │   ├── Dockerfile-0.11.twig
│       │   ├── Dockerfile-0.6.twig
│       │   └── Dockerfile-0.8.twig
│       ├── php/
│       │   ├── Dockerfile-5.3.twig
│       │   ├── Dockerfile-5.4.twig
│       │   ├── Dockerfile-5.5.twig
│       │   ├── Dockerfile-5.6.twig
│       │   ├── Dockerfile-7.twig
│       │   ├── Dockerfile-hhvm.twig
│       │   └── Dockerfile.twig
│       └── ruby/
│           ├── Dockerfile-1.9.3.twig
│           ├── Dockerfile-2.0.0.twig
│           └── Dockerfile-2.1.0.twig
├── src/
│   └── Joli/
│       └── JoliCi/
│           ├── BuildStrategy/
│           │   ├── BuildStrategyInterface.php
│           │   ├── ChainBuildStrategy.php
│           │   ├── JoliCiBuildStrategy.php
│           │   └── TravisCiBuildStrategy.php
│           ├── Builder/
│           │   └── DockerfileBuilder.php
│           ├── Command/
│           │   ├── CleanCommand.php
│           │   ├── RunCommand.php
│           │   └── UpdateImageCommand.php
│           ├── Container.php
│           ├── Executor.php
│           ├── Filesystem/
│           │   └── Filesystem.php
│           ├── Job.php
│           ├── Log/
│           │   └── SimpleFormatter.php
│           ├── LoggerCallback.php
│           ├── Matrix.php
│           ├── Naming.php
│           ├── Service.php
│           ├── ServiceManager.php
│           └── Vacuum.php
└── tests/
    └── Joli/
        └── JoliCi/
            ├── BuildStrategy/
            │   ├── ChainBuildStrategyTest.php
            │   ├── JoliCiBuildStrategyTest.php
            │   ├── TravisCIBuildStrategyTest.php
            │   └── fixtures/
            │       ├── jolici/
            │       │   ├── project1/
            │       │   │   ├── .jolici/
            │       │   │   │   └── test/
            │       │   │   │       └── Dockerfile
            │       │   │   └── foo
            │       │   └── project2/
            │       │       └── .gitkeep
            │       └── travisci/
            │           └── project1/
            │               └── .travis.yml
            ├── JobTest.php
            ├── MatrixTest.php
            └── ServiceTest.php
Download .txt
SYMBOL INDEX (147 symbols across 25 files)

FILE: src/Joli/JoliCi/BuildStrategy/BuildStrategyInterface.php
  type BuildStrategyInterface (line 20) | interface BuildStrategyInterface
    method getJobs (line 31) | public function getJobs($directory);
    method prepareJob (line 40) | public function prepareJob(Job $job);
    method getName (line 47) | public function getName();
    method supportProject (line 56) | public function supportProject($directory);

FILE: src/Joli/JoliCi/BuildStrategy/ChainBuildStrategy.php
  class ChainBuildStrategy (line 7) | class ChainBuildStrategy implements BuildStrategyInterface
    method pushStrategy (line 19) | public function pushStrategy(BuildStrategyInterface $strategy)
    method getJobs (line 27) | public function getJobs($directory)
    method prepareJob (line 43) | public function prepareJob(Job $job)
    method getName (line 51) | public function getName()
    method supportProject (line 59) | public function supportProject($directory)

FILE: src/Joli/JoliCi/BuildStrategy/JoliCiBuildStrategy.php
  class JoliCiBuildStrategy (line 25) | class JoliCiBuildStrategy implements BuildStrategyInterface
    method __construct (line 47) | public function __construct($buildPath, Naming $naming, Filesystem $fi...
    method getJobs (line 57) | public function getJobs($directory)
    method prepareJob (line 82) | public function prepareJob(Job $job)
    method getName (line 104) | public function getName()
    method supportProject (line 112) | public function supportProject($directory)
    method getJoliCiStrategyDirectory (line 123) | protected function getJoliCiStrategyDirectory($projectPath)

FILE: src/Joli/JoliCi/BuildStrategy/TravisCiBuildStrategy.php
  class TravisCiBuildStrategy (line 28) | class TravisCiBuildStrategy implements BuildStrategyInterface
    method __construct (line 146) | public function __construct(DockerfileBuilder $builder, $buildPath, Na...
    method getJobs (line 157) | public function getJobs($directory)
    method prepareJob (line 198) | public function prepareJob(Job $job)
    method getName (line 220) | public function getName()
    method supportProject (line 228) | public function supportProject($directory)
    method getConfigValue (line 242) | private function getConfigValue($config, $language, $key)
    method createMatrix (line 266) | protected function createMatrix($config)
    method getServices (line 332) | protected function getServices($config)
    method parseEnvironmentLine (line 362) | private function parseEnvironmentLine($environmentLine)
    method parseEnvironementVariable (line 383) | private function parseEnvironementVariable($envVar)
    method isLanguageVersionSupported (line 388) | private function isLanguageVersionSupported($language, $version)

FILE: src/Joli/JoliCi/Builder/DockerfileBuilder.php
  class DockerfileBuilder (line 7) | class DockerfileBuilder extends BaseBuilder

FILE: src/Joli/JoliCi/Command/CleanCommand.php
  class CleanCommand (line 11) | class CleanCommand extends Command
    method configure (line 16) | protected function configure()
    method execute (line 31) | protected function execute(InputInterface $input, OutputInterface $out...

FILE: src/Joli/JoliCi/Command/RunCommand.php
  class RunCommand (line 22) | class RunCommand extends Command
    method configure (line 27) | protected function configure()
    method execute (line 42) | protected function execute(InputInterface $input, OutputInterface $out...

FILE: src/Joli/JoliCi/Command/UpdateImageCommand.php
  class UpdateImageCommand (line 12) | class UpdateImageCommand extends Command
    method configure (line 17) | protected function configure()
    method execute (line 26) | protected function execute(InputInterface $input, OutputInterface $out...

FILE: src/Joli/JoliCi/Container.php
  class Container (line 18) | class Container
    method getTravisCiStrategy (line 27) | public function getTravisCiStrategy()
    method getJoliCiStrategy (line 45) | public function getJoliCiStrategy()
    method getChainStrategy (line 55) | public function getChainStrategy()
    method getStrategy (line 69) | public function getStrategy()
    method getConsoleLogger (line 81) | public function getConsoleLogger($verbose = false)
    method getVacuum (line 101) | public function getVacuum()
    method getFilesystem (line 106) | public function getFilesystem()
    method getDocker (line 111) | public function getDocker()
    method getExecutor (line 120) | public function getExecutor($cache = true, $verbose = false, $timeout ...
    method getServiceManager (line 125) | public function getServiceManager($verbose = false)
    method getBuildPath (line 130) | public function getBuildPath()
    method getNaming (line 135) | public function getNaming()
    method getLoggerCallback (line 140) | public function getLoggerCallback($verbose)

FILE: src/Joli/JoliCi/Executor.php
  class Executor (line 24) | class Executor
    method __construct (line 60) | public function __construct(LoggerCallback $logger, Docker $docker, $b...
    method test (line 78) | public function test(Job $build, $command = null)
    method create (line 96) | public function create(Job $job)
    method run (line 128) | public function run(Job $job, $command)

FILE: src/Joli/JoliCi/Filesystem/Filesystem.php
  class Filesystem (line 15) | class Filesystem extends BaseFilesystem
    method copy (line 26) | public function copy($originFile, $targetFile, $override = false)

FILE: src/Joli/JoliCi/Job.php
  class Job (line 13) | class Job
    method __construct (line 67) | public function __construct($project, $strategy, $uniq, $parameters = ...
    method getName (line 88) | public function getName()
    method getRepository (line 98) | public function getRepository()
    method getTag (line 108) | public function getTag()
    method addService (line 118) | public function addService(Service $service)
    method getServices (line 128) | public function getServices()
    method getDirectory (line 138) | public function getDirectory()
    method getDescription (line 146) | public function getDescription()
    method getStrategy (line 154) | public function getStrategy()
    method getUniq (line 162) | public function getUniq()
    method getParameters (line 170) | public function getParameters()
    method getCreated (line 178) | public function getCreated()
    method __toString (line 183) | public function __toString()

FILE: src/Joli/JoliCi/Log/SimpleFormatter.php
  class SimpleFormatter (line 15) | class SimpleFormatter implements FormatterInterface
    method format (line 24) | public function format(array $record)
    method formatBatch (line 68) | public function formatBatch(array $records)

FILE: src/Joli/JoliCi/LoggerCallback.php
  class LoggerCallback (line 8) | class LoggerCallback
    method __construct (line 30) | public function __construct(LoggerInterface $logger)
    method getBuildCallback (line 47) | public function getBuildCallback()
    method getRunStdoutCallback (line 57) | public function getRunStdoutCallback()
    method getRunStderrCallback (line 67) | public function getRunStderrCallback()
    method clearStatic (line 75) | public function clearStatic()
    method buildCallback (line 85) | private function buildCallback(BuildInfo $output)
    method runStdoutCallback (line 122) | private function runStdoutCallback($output)
    method runStderrCallback (line 132) | private function runStderrCallback($output)

FILE: src/Joli/JoliCi/Matrix.php
  class Matrix (line 16) | class Matrix
    method setDimension (line 26) | public function setDimension($name, array $values)
    method compute (line 40) | public function compute()

FILE: src/Joli/JoliCi/Naming.php
  class Naming (line 7) | class Naming
    method getProjectName (line 16) | public function getProjectName($projectPath)
    method getUniqueKey (line 32) | public function getUniqueKey($parameters = array())

FILE: src/Joli/JoliCi/Service.php
  class Service (line 16) | class Service
    method __construct (line 43) | public function __construct($name, $repository, $tag, $config = array())
    method getName (line 54) | public function getName()
    method getRepository (line 62) | public function getRepository()
    method getTag (line 70) | public function getTag()
    method getConfig (line 78) | public function getConfig()
    method getContainer (line 86) | public function getContainer()
    method setContainer (line 94) | public function setContainer($container)

FILE: src/Joli/JoliCi/ServiceManager.php
  class ServiceManager (line 14) | class ServiceManager
    method __construct (line 20) | public function __construct(Docker $docker, LoggerCallback $logger)
    method start (line 31) | public function start(Job $build)
    method stop (line 72) | public function stop(Job $job, $timeout = 10)

FILE: src/Joli/JoliCi/Vacuum.php
  class Vacuum (line 11) | class Vacuum
    method __construct (line 45) | public function __construct(Docker $docker, Naming $naming, BuildStrat...
    method clean (line 61) | public function clean($projectPath, $keep = 1, $force = false)
    method cleanDirectories (line 75) | public function cleanDirectories($jobs = array())
    method cleanContainers (line 87) | public function cleanContainers($jobs = array())
    method cleanImages (line 128) | public function cleanImages($jobs = array(), $force = false)
    method getJobsToRemove (line 145) | public function getJobsToRemove($projectPath, $keep = 1)
    method getJobs (line 187) | protected function getJobs($projectPath)
    method getJobFromImage (line 213) | protected function getJobFromImage(ImageItem $image, $imageName, $stra...

FILE: tests/Joli/JoliCi/BuildStrategy/ChainBuildStrategyTest.php
  class ChainBuildStrategyTest (line 7) | class ChainBuildStrategyTest extends \PHPUnit_Framework_TestCase
    method testGetJobs (line 9) | public function testGetJobs()
    method testNoJobsEmpty (line 25) | public function testNoJobsEmpty()
  class FooBuildStrategy (line 36) | class FooBuildStrategy implements BuildStrategyInterface
    method getJobs (line 38) | public function getJobs($directory)
    method getName (line 43) | public function getName()
    method prepareJob (line 48) | public function prepareJob(Job $job)
    method supportProject (line 52) | public function supportProject($directory)
  class NoBuildStrategy (line 58) | class NoBuildStrategy extends FooBuildStrategy
    method supportProject (line 60) | public function supportProject($directory)

FILE: tests/Joli/JoliCi/BuildStrategy/JoliCiBuildStrategyTest.php
  class JoliCiBuildStrategyTest (line 9) | class JoliCiBuildStrategyTest extends \PHPUnit_Framework_TestCase
    method setUp (line 11) | public function setUp()
    method testCreateJobs (line 17) | public function testCreateJobs()
    method testPrepareJob (line 24) | public function testPrepareJob()
    method testSupportTrue (line 39) | public function testSupportTrue()
    method testSupportFalse (line 46) | public function testSupportFalse()

FILE: tests/Joli/JoliCi/BuildStrategy/TravisCIBuildStrategyTest.php
  class TravisCIBuildStrategyTest (line 10) | class TravisCIBuildStrategyTest extends \PHPUnit_Framework_TestCase
    method setUp (line 12) | public function setUp()
    method testSupportTrue (line 18) | public function testSupportTrue()
    method testSupportFalse (line 25) | public function testSupportFalse()
    method testCreateMatrixCanObtainVersions (line 35) | public function testCreateMatrixCanObtainVersions($versions)
    method createMatrixVersionDataProvider (line 55) | public function createMatrixVersionDataProvider()

FILE: tests/Joli/JoliCi/JobTest.php
  class JobTest (line 5) | class JobTest extends \PHPUnit_Framework_TestCase
    method testConstructInitialisesAllTheFields (line 7) | public function testConstructInitialisesAllTheFields()
    method testAddAndGetServices (line 42) | public function testAddAndGetServices($jobMock)
    method testToStringReturnsTheNameOfTheJob (line 55) | public function testToStringReturnsTheNameOfTheJob()

FILE: tests/Joli/JoliCi/MatrixTest.php
  class MatrixTest (line 5) | class MatrixTest extends \PHPUnit_Framework_TestCase
    method testSetDimensionWillUseDefaultValuesIfEmptyProvided (line 7) | public function testSetDimensionWillUseDefaultValuesIfEmptyProvided()
    method testComputeWillReturnEmptyIfNoDimensions (line 16) | public function testComputeWillReturnEmptyIfNoDimensions()
    method testCompute (line 23) | public function testCompute()

FILE: tests/Joli/JoliCi/ServiceTest.php
  class ServiceTest (line 5) | class ServiceTest extends \PHPUnit_Framework_TestCase
    method testConstructInitialisesAllTheFields (line 7) | public function testConstructInitialisesAllTheFields()
    method testSetAndGetContainer (line 28) | public function testSetAndGetContainer($serviceMock)
Condensed preview — 62 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (102K chars).
[
  {
    "path": ".gitignore",
    "chars": 130,
    "preview": "# IDE\n/.build_dir\n/.externalTollBuilders\n/.settings\n/.project\n/.buildpath\n\n# Dependencies\n/vendor\n\n# Bin\n/pharcc.phar\n/j"
  },
  {
    "path": ".gush.yml",
    "chars": 38,
    "preview": "adapter: github\nissue_tracker: github\n"
  },
  {
    "path": ".pharcc.yml",
    "chars": 174,
    "preview": "name: jolici.phar\nmain: bin/jolici\ninclude:\n    - src/\n    - vendor/\n    - resources/\nexclude:\n    - \"/[Tt]ests?/\"\n    -"
  },
  {
    "path": ".travis.yml",
    "chars": 228,
    "preview": "language: php\n\nphp:\n    - 5.5\n    - 5.6\n    - hhvm\n    - 7.0\n\nmatrix:\n    allow_failures:\n        - php: hhvm\n\nbefore_sc"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 3492,
    "preview": "## Change Log\n\n### v0.3.1 (2015/04/02)\n\n- Add [images-update command](docs/command/updates-image.md)\n- Add php7 support\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1021,
    "preview": "# Contributing\n\nFirst, thank you!\n\nHere are a few rules to follow in order to ease code reviews, and discussions before "
  },
  {
    "path": "LICENSE",
    "chars": 1066,
    "preview": "Copyright (C) 2014 Joel Wurtz\n\nPermission is hereby granted, free of charge, to any person obtaining a copy \nof this sof"
  },
  {
    "path": "README.md",
    "chars": 2445,
    "preview": "# JoliCi\n\nJoliCi is a free and open source Continuous Integration _Client_ written in PHP (5.4 minimum) and powered by D"
  },
  {
    "path": "bin/jolici",
    "chars": 810,
    "preview": "#!/usr/bin/env php\n<?php\n\nuse Joli\\JoliCi\\Command\\RunCommand;\nuse Joli\\JoliCi\\Command\\CleanCommand;\nuse Symfony\\Componen"
  },
  {
    "path": "composer.json",
    "chars": 1054,
    "preview": "{\n    \"name\": \"jolicode/jolici\",\n    \"description\": \"Run your TravisCi builds locally\",\n    \"type\": \"library\",\n    \"lice"
  },
  {
    "path": "docs/command/clean.md",
    "chars": 998,
    "preview": "# The clean command\n\nJoliCi try to keep the number of docker images, containers and build directories as low as possible"
  },
  {
    "path": "docs/command/run.md",
    "chars": 3589,
    "preview": "# The run command\n\nThe run command is the main command of JoliCi. It create and prepare the differents environments (job"
  },
  {
    "path": "docs/command/updates-image.md",
    "chars": 255,
    "preview": "# The images-update command\n\nThis command is used to update all images jolici used to create test environments.\n\n```\n$ j"
  },
  {
    "path": "docs/installation.md",
    "chars": 866,
    "preview": "# Installation\n\n## Install globally\n\n### Download\n\n[Download the last version of jolici](https://github.com/jolicode/Jol"
  },
  {
    "path": "docs/strategies/JoliCiStrategy.md",
    "chars": 1068,
    "preview": "# JoliCiBuildStrategy\n\nThis strategy is based on a directory structure and Dockerfile, this is the most flexible strateg"
  },
  {
    "path": "docs/strategies/TravisCiStrategy.md",
    "chars": 725,
    "preview": "# TravisCiBuildStrategy\n\nThis strategy parses the .travis.yml file at the root of your project to create a Dockerfile fo"
  },
  {
    "path": "phpunit.xml",
    "chars": 869,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!-- http://www.phpunit.de/manual/current/en/appendixes.configuration.html -->\n<"
  },
  {
    "path": "resources/templates/Dockerfile-travis.twig",
    "chars": 886,
    "preview": "{% extends \"Dockerfile.twig\" %}\n\n{% block env %}\n{% for key,value in global_env %}\nENV {{ key }}={{ value }}\n{% endfor %"
  },
  {
    "path": "resources/templates/Dockerfile.twig",
    "chars": 417,
    "preview": "{% block from %}\nFROM jolicode/base:latest\n{% endblock %}\n\n{% block content %}\n{% endblock %}\n\nLABEL com.jolici.image=\"t"
  },
  {
    "path": "resources/templates/node_js/Dockerfile-0.10.twig",
    "chars": 102,
    "preview": "{% extends \"Dockerfile-travis.twig\" %}\n\n{% block from %}\nFROM jolicode/node-0.10:latest\n{% endblock %}"
  },
  {
    "path": "resources/templates/node_js/Dockerfile-0.11.twig",
    "chars": 102,
    "preview": "{% extends \"Dockerfile-travis.twig\" %}\n\n{% block from %}\nFROM jolicode/node-0.11:latest\n{% endblock %}"
  },
  {
    "path": "resources/templates/node_js/Dockerfile-0.6.twig",
    "chars": 101,
    "preview": "{% extends \"Dockerfile-travis.twig\" %}\n\n{% block from %}\nFROM jolicode/node-0.6:latest\n{% endblock %}"
  },
  {
    "path": "resources/templates/node_js/Dockerfile-0.8.twig",
    "chars": 101,
    "preview": "{% extends \"Dockerfile-travis.twig\" %}\n\n{% block from %}\nFROM jolicode/node-0.8:latest\n{% endblock %}"
  },
  {
    "path": "resources/templates/php/Dockerfile-5.3.twig",
    "chars": 173,
    "preview": "{% extends \"php/Dockerfile.twig\" %}\n\n{% block env %}\n{{ parent() }}\nENV TRAVIS_PHP_VERSION php5.3\n{% endblock %}\n\n{% blo"
  },
  {
    "path": "resources/templates/php/Dockerfile-5.4.twig",
    "chars": 173,
    "preview": "{% extends \"php/Dockerfile.twig\" %}\n\n{% block env %}\n{{ parent() }}\nENV TRAVIS_PHP_VERSION php5.4\n{% endblock %}\n\n{% blo"
  },
  {
    "path": "resources/templates/php/Dockerfile-5.5.twig",
    "chars": 173,
    "preview": "{% extends \"php/Dockerfile.twig\" %}\n\n{% block env %}\n{{ parent() }}\nENV TRAVIS_PHP_VERSION php5.5\n{% endblock %}\n\n{% blo"
  },
  {
    "path": "resources/templates/php/Dockerfile-5.6.twig",
    "chars": 173,
    "preview": "{% extends \"php/Dockerfile.twig\" %}\n\n{% block env %}\n{{ parent() }}\nENV TRAVIS_PHP_VERSION php5.6\n{% endblock %}\n\n{% blo"
  },
  {
    "path": "resources/templates/php/Dockerfile-7.twig",
    "chars": 182,
    "preview": "{% extends \"php/Dockerfile.twig\" %}\n\n{% block env %}\n{{ parent() }}\nENV TRAVIS_PHP_VERSION php7.0-dev\n{% endblock %}\n\n{%"
  },
  {
    "path": "resources/templates/php/Dockerfile-hhvm.twig",
    "chars": 288,
    "preview": "{% extends \"Dockerfile-travis.twig\" %}\n\n{% block from %}\nFROM jolicode/hhvm:latest\n{% endblock %}\n\n{% block env %}\n{{ pa"
  },
  {
    "path": "resources/templates/php/Dockerfile.twig",
    "chars": 235,
    "preview": "{% extends \"Dockerfile-travis.twig\" %}\n\n{% block env %}\n{{ parent() }}\n{% endblock %}\n\n{% block before_install %}\nRUN ec"
  },
  {
    "path": "resources/templates/ruby/Dockerfile-1.9.3.twig",
    "chars": 103,
    "preview": "{% extends \"Dockerfile-travis.twig\" %}\n\n{% block from %}\nFROM jolicode/ruby-1.9.3:latest\n{% endblock %}"
  },
  {
    "path": "resources/templates/ruby/Dockerfile-2.0.0.twig",
    "chars": 103,
    "preview": "{% extends \"Dockerfile-travis.twig\" %}\n\n{% block from %}\nFROM jolicode/ruby-2.0.0:latest\n{% endblock %}"
  },
  {
    "path": "resources/templates/ruby/Dockerfile-2.1.0.twig",
    "chars": 103,
    "preview": "{% extends \"Dockerfile-travis.twig\" %}\n\n{% block from %}\nFROM jolicode/ruby-2.1.0:latest\n{% endblock %}"
  },
  {
    "path": "src/Joli/JoliCi/BuildStrategy/BuildStrategyInterface.php",
    "chars": 1211,
    "preview": "<?php\n/*\n * This file is part of JoliCi.\n*\n* (c) Joel Wurtz <jwurtz@jolicode.com>\n*\n* For the full copyright and license"
  },
  {
    "path": "src/Joli/JoliCi/BuildStrategy/ChainBuildStrategy.php",
    "chars": 1403,
    "preview": "<?php\n\nnamespace Joli\\JoliCi\\BuildStrategy;\n\nuse Joli\\JoliCi\\Job;\n\nclass ChainBuildStrategy implements BuildStrategyInte"
  },
  {
    "path": "src/Joli/JoliCi/BuildStrategy/JoliCiBuildStrategy.php",
    "chars": 3319,
    "preview": "<?php\n/*\n * This file is part of JoliCi.\n*\n* (c) Joel Wurtz <jwurtz@jolicode.com>\n*\n* For the full copyright and license"
  },
  {
    "path": "src/Joli/JoliCi/BuildStrategy/TravisCiBuildStrategy.php",
    "chars": 12873,
    "preview": "<?php\n/*\n * This file is part of JoliCi.\n *\n * (c) Joel Wurtz <jwurtz@jolicode.com>\n *\n * For the full copyright and lic"
  },
  {
    "path": "src/Joli/JoliCi/Builder/DockerfileBuilder.php",
    "chars": 127,
    "preview": "<?php\n\nnamespace Joli\\JoliCi\\Builder;\n\nuse TwigGenerator\\Builder\\BaseBuilder;\n\nclass DockerfileBuilder extends BaseBuild"
  },
  {
    "path": "src/Joli/JoliCi/Command/CleanCommand.php",
    "chars": 2399,
    "preview": "<?php\n\nnamespace Joli\\JoliCi\\Command;\n\nuse Joli\\JoliCi\\Container;\nuse Symfony\\Component\\Console\\Command\\Command;\nuse Sym"
  },
  {
    "path": "src/Joli/JoliCi/Command/RunCommand.php",
    "chars": 3835,
    "preview": "<?php\n/*\n * This file is part of JoliCi.\n*\n* (c) Joel Wurtz <jwurtz@jolicode.com>\n*\n* For the full copyright and license"
  },
  {
    "path": "src/Joli/JoliCi/Command/UpdateImageCommand.php",
    "chars": 1273,
    "preview": "<?php\n\nnamespace Joli\\JoliCi\\Command;\n\nuse Joli\\JoliCi\\Container;\nuse Symfony\\Component\\Console\\Command\\Command;\nuse Sym"
  },
  {
    "path": "src/Joli/JoliCi/Container.php",
    "chars": 4001,
    "preview": "<?php\n\nnamespace Joli\\JoliCi;\n\nuse Docker\\Docker;\nuse Joli\\JoliCi\\BuildStrategy\\ChainBuildStrategy;\nuse Joli\\JoliCi\\File"
  },
  {
    "path": "src/Joli/JoliCi/Executor.php",
    "chars": 4980,
    "preview": "<?php\n/*\n * This file is part of JoliCi.\n*\n* (c) Joel Wurtz <jwurtz@jolicode.com>\n*\n* For the full copyright and license"
  },
  {
    "path": "src/Joli/JoliCi/Filesystem/Filesystem.php",
    "chars": 805,
    "preview": "<?php\n/*\n * This file is part of JoliCi.\n *\n * (c) Joel Wurtz <jwurtz@jolicode.com>\n *\n * For the full copyright and lic"
  },
  {
    "path": "src/Joli/JoliCi/Job.php",
    "chars": 4209,
    "preview": "<?php\n/*\n * This file is part of JoliCi.\n*\n* (c) Joel Wurtz <jwurtz@jolicode.com>\n*\n* For the full copyright and license"
  },
  {
    "path": "src/Joli/JoliCi/Log/SimpleFormatter.php",
    "chars": 2160,
    "preview": "<?php\n/*\n * This file is part of JoliCi.\n*\n* (c) Joel Wurtz <jwurtz@jolicode.com>\n*\n* For the full copyright and license"
  },
  {
    "path": "src/Joli/JoliCi/LoggerCallback.php",
    "chars": 3180,
    "preview": "<?php\n\nnamespace Joli\\JoliCi;\n\nuse Docker\\API\\Model\\BuildInfo;\nuse Psr\\Log\\LoggerInterface;\n\nclass LoggerCallback\n{\n    "
  },
  {
    "path": "src/Joli/JoliCi/Matrix.php",
    "chars": 1976,
    "preview": "<?php\n/*\n * This file is part of JoliCi.\n *\n * (c) Joel Wurtz <jwurtz@jolicode.com>\n *\n * For the full copyright and lic"
  },
  {
    "path": "src/Joli/JoliCi/Naming.php",
    "chars": 921,
    "preview": "<?php\n\nnamespace Joli\\JoliCi;\n\nuse Behat\\Transliterator\\Transliterator;\n\nclass Naming\n{\n    /**\n     * Return a translit"
  },
  {
    "path": "src/Joli/JoliCi/Service.php",
    "chars": 1974,
    "preview": "<?php\n\nnamespace Joli\\JoliCi;\n\nuse Docker\\Container as DockerContainer;\n\n/**\n * A service is just an application or a to"
  },
  {
    "path": "src/Joli/JoliCi/ServiceManager.php",
    "chars": 3136,
    "preview": "<?php\n\nnamespace Joli\\JoliCi;\n\nuse Docker\\API\\Model\\ContainerConfig;\nuse Docker\\Container as DockerContainer;\nuse Docker"
  },
  {
    "path": "src/Joli/JoliCi/Vacuum.php",
    "chars": 6644,
    "preview": "<?php\n\nnamespace Joli\\JoliCi;\n\nuse Docker\\API\\Model\\ImageItem;\nuse Docker\\Docker;\nuse Docker\\Image;\nuse Joli\\JoliCi\\Buil"
  },
  {
    "path": "tests/Joli/JoliCi/BuildStrategy/ChainBuildStrategyTest.php",
    "chars": 1342,
    "preview": "<?php\n\nnamespace Joli\\JoliCi\\BuildStrategy;\n\nuse Joli\\JoliCi\\Job;\n\nclass ChainBuildStrategyTest extends \\PHPUnit_Framewo"
  },
  {
    "path": "tests/Joli/JoliCi/BuildStrategy/JoliCiBuildStrategyTest.php",
    "chars": 2370,
    "preview": "<?php\n\nnamespace Joli\\JoliCi;\n\nuse Joli\\JoliCi\\BuildStrategy\\JoliCiBuildStrategy;\nuse Joli\\JoliCi\\Filesystem\\Filesystem;"
  },
  {
    "path": "tests/Joli/JoliCi/BuildStrategy/TravisCIBuildStrategyTest.php",
    "chars": 1849,
    "preview": "<?php\n\nnamespace Joli\\JoliCi\\BuildStrategy;\n\nuse Joli\\JoliCi\\Filesystem\\Filesystem;\nuse Joli\\JoliCi\\Naming;\nuse org\\bovi"
  },
  {
    "path": "tests/Joli/JoliCi/BuildStrategy/fixtures/jolici/project1/.jolici/test/Dockerfile",
    "chars": 27,
    "preview": "FROM ubuntu\n\nCMD echo 'lol'"
  },
  {
    "path": "tests/Joli/JoliCi/BuildStrategy/fixtures/jolici/project1/foo",
    "chars": 3,
    "preview": "bar"
  },
  {
    "path": "tests/Joli/JoliCi/BuildStrategy/fixtures/jolici/project2/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/Joli/JoliCi/BuildStrategy/fixtures/travisci/project1/.travis.yml",
    "chars": 197,
    "preview": "language: php\n\nphp:\n  - 5.3\n  - 5.4\n  - 5.5\n\nbefore_script:\n    - git config --global user.name travis-ci\n    - git conf"
  },
  {
    "path": "tests/Joli/JoliCi/JobTest.php",
    "chars": 1853,
    "preview": "<?php\n\nnamespace Joli\\JoliCi;\n\nclass JobTest extends \\PHPUnit_Framework_TestCase\n{\n    public function testConstructInit"
  },
  {
    "path": "tests/Joli/JoliCi/MatrixTest.php",
    "chars": 2343,
    "preview": "<?php\n\nnamespace Joli\\JoliCi;\n\nclass MatrixTest extends \\PHPUnit_Framework_TestCase\n{\n    public function testSetDimensi"
  },
  {
    "path": "tests/Joli/JoliCi/ServiceTest.php",
    "chars": 1170,
    "preview": "<?php\n\nnamespace Joli\\JoliCi;\n\nclass ServiceTest extends \\PHPUnit_Framework_TestCase\n{\n    public function testConstruct"
  }
]

About this extraction

This page contains the full source code of the jolicode/JoliCi GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 62 files (91.6 KB), approximately 25.6k tokens, and a symbol index with 147 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!