├── VERSION
├── recipes
├── mongodb
│ ├── Dockerfile
│ └── link.sh
├── redis
│ ├── Dockerfile
│ └── link.sh
├── postgresql
│ ├── Dockerfile
│ └── link.sh
├── mysql
│ ├── Dockerfile
│ └── link.sh
├── rabbitmq
│ ├── Dockerfile
│ └── link.sh
├── base
│ ├── travis.json
│ └── Dockerfile
└── php
│ └── Dockerfile
├── .gitignore
├── CHANGELOG.md
├── src
├── Exception.php
├── Task
│ ├── RunContainer.php
│ ├── BuildRecipe.php
│ ├── RunService.php
│ ├── CreateRunScript.php
│ └── CreateStartScript.php
├── Tasks.php
├── ConfigParser
│ ├── RoboEnv.php
│ └── Travis.php
├── Cleanup.php
├── Command
│ ├── Travis
│ │ ├── Build.php
│ │ └── Prepare.php
│ └── CI.php
├── Config.php
└── Runner.php
├── composer.json
├── RoboFile.php
├── LICENSE
├── README.md
└── composer.lock
/VERSION:
--------------------------------------------------------------------------------
1 | 0.1.0
--------------------------------------------------------------------------------
/recipes/mongodb/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mongo
--------------------------------------------------------------------------------
/recipes/redis/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM redis
--------------------------------------------------------------------------------
/recipes/postgresql/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM postgres
--------------------------------------------------------------------------------
/recipes/mysql/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM centurylink/mysql
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | composer.phar
2 | vendor/
3 | .idea
4 |
--------------------------------------------------------------------------------
/recipes/redis/link.sh:
--------------------------------------------------------------------------------
1 | socat TCP4-LISTEN:6379,fork,reuseaddr TCP4:redis:6379 &
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | #### 0.1.0
4 |
5 | * initial *2014-09-27*
6 |
--------------------------------------------------------------------------------
/recipes/mongodb/link.sh:
--------------------------------------------------------------------------------
1 | socat TCP4-LISTEN:27017,fork,reuseaddr TCP4:mongodb:27017 &
--------------------------------------------------------------------------------
/recipes/rabbitmq/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM frodenas/rabbitmq
2 | ENV RABBITMQ_USERNAME guest
3 | ENV RABBITMQ_PASSWORD guest
--------------------------------------------------------------------------------
/src/Exception.php:
--------------------------------------------------------------------------------
1 | /etc/init.d/xvfb
3 | RUN chef-solo -o php::multi,composer -j travis.json
4 | RUN curl -s http://getcomposer.org/installer | php
5 | RUN mv composer.phar /usr/bin/composer
6 | USER travis
7 | ENV PATH $PATH:/home/travis/.phpenv/bin
8 | RUN ["/bin/bash", "-l", "-c", "eval \"$(phpenv init -)\""]
9 | RUN phpenv rehash 2>/dev/null
--------------------------------------------------------------------------------
/src/Task/RunContainer.php:
--------------------------------------------------------------------------------
1 | option('link', $linkedService);
15 | }
16 |
17 | return $this;
18 | }
19 | }
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "codegyre/robo-ci",
3 | "description": "Travis CI cli runner",
4 | "license": "MIT",
5 | "authors": [
6 | {
7 | "name": "Davert",
8 | "email": "davert.php@resend.cc"
9 | }
10 | ],
11 | "autoload":{
12 | "psr-4":{
13 | "Codegyre\\RoboCI\\":"src"
14 | }
15 | },
16 | "require": {
17 | "php": ">=5.4.0",
18 | "symfony/yaml": "~2.0",
19 | "codegyre/robo-docker": "0.1.*",
20 | "codegyre/Robo": "0.4.*"
21 | }
22 | }
--------------------------------------------------------------------------------
/src/Tasks.php:
--------------------------------------------------------------------------------
1 | > /etc/sudoers
--------------------------------------------------------------------------------
/src/Task/BuildRecipe.php:
--------------------------------------------------------------------------------
1 | recipe = $recipe;
17 | $this->path = Config::getRecipeDir($recipe);
18 | }
19 |
20 | public function run()
21 | {
22 | if (!$this->path) {
23 | throw new TaskException($this, "Recipe for {$this->recipe} not found in ".Config::getUserRecipesDir());
24 | }
25 | return parent::run();
26 | }
27 | }
--------------------------------------------------------------------------------
/src/ConfigParser/RoboEnv.php:
--------------------------------------------------------------------------------
1 | config = Yaml::parse($file);
20 | }
21 |
22 | function services()
23 | {
24 | return isset($this->config['services']) ? $this->config['services'] : [];
25 | }
26 | }
--------------------------------------------------------------------------------
/RoboFile.php:
--------------------------------------------------------------------------------
1 | taskChangelog()
14 | ->version($this->getVersion())
15 | ->change($change)
16 | ->run();
17 | }
18 |
19 | public function release()
20 | {
21 | $this->say("Releasing");
22 |
23 | $this->taskGitStack()
24 | ->add('CHANGELOG.md')
25 | ->commit('updated')
26 | ->push()
27 | ->run();
28 |
29 | $this->taskGitHubRelease($this->version())
30 | ->uri('Codegyre/RoboCI')
31 | ->run();
32 | }
33 |
34 |
35 | protected function getVersion()
36 | {
37 | return trim(file_get_contents('VERSION'));
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Codegyre developers team
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/src/Task/RunService.php:
--------------------------------------------------------------------------------
1 | service = $service;
18 | }
19 |
20 | public function name($name)
21 | {
22 | $this->name = $name;
23 | return $this;
24 | }
25 |
26 | public function run()
27 | {
28 | $image = "roboci_service_".$this->service;
29 | $images = [];
30 | exec('docker images | grep '.$image, $images);
31 |
32 | if (empty($images)) {
33 | $this->printTaskInfo("Service {$this->service} does not exist, building...");
34 | (new BuildRecipe($this->service))
35 | ->tag($image)
36 | ->run();
37 | }
38 |
39 | $this->printTaskInfo("Running {$this->service} service container");
40 | return (new Run($image))
41 | ->option('-d')
42 | ->name($this->name)
43 | ->run();
44 | }
45 | }
--------------------------------------------------------------------------------
/src/Cleanup.php:
--------------------------------------------------------------------------------
1 | printTaskInfo("Getting all robo_* containers");
18 | $res = $this->taskExec('docker ps -a | grep robo_')->printed(false)->run();
19 | if (!$res->wasSuccessful()) {
20 | $this->printTaskInfo("No containers matched");
21 | return;
22 | }
23 | $containerLines = explode("\n", $res->getMessage());
24 | foreach ($containerLines as $container) {
25 | $data = explode(' ', $container);
26 | $id = trim(reset($data));
27 | if (!$id) continue;
28 | $this->containers[] = $id;
29 | }
30 | $this->printTaskInfo("Containers are: ".implode(', ', $this->containers)."");
31 | }
32 |
33 | public function stopContainers()
34 | {
35 | foreach ($this->containers as $container) {
36 | $this->taskDockerStop($container)->run();
37 | }
38 | }
39 |
40 | public function removeContainers()
41 | {
42 | foreach ($this->containers as $container) {
43 | $this->taskDockerRemove($container)->run();
44 | }
45 | }
46 |
47 |
48 | }
--------------------------------------------------------------------------------
/src/Task/CreateRunScript.php:
--------------------------------------------------------------------------------
1 | filename = $dir.DIRECTORY_SEPARATOR.Config::RUN_SCRIPT;
21 | }
22 |
23 | function script($script)
24 | {
25 | $this->body .= 'echo "---------------------------"'."\n";
26 | $this->body .= 'echo "running '.str_replace('"', '', $script).'"' ."\n";
27 | $this->body .= 'echo "---------------------------"'."\n";
28 | $this->body .= $script ."\n";
29 | return $this;
30 | }
31 |
32 | public function scripts(array $scripts)
33 | {
34 | foreach ($scripts as $script) {
35 | $this->script($script);
36 | }
37 | return $this;
38 | }
39 |
40 | public function run()
41 | {
42 | $this->printTaskInfo("Writing {$this->filename}");
43 | $res = file_put_contents($this->filename, $this->body);
44 | if ($res === false) return Result::error($this, "File {$this->filename} couldnt be created");
45 | return Result::success($this);
46 | }
47 | }
--------------------------------------------------------------------------------
/src/Task/CreateStartScript.php:
--------------------------------------------------------------------------------
1 | filename = $dir.DIRECTORY_SEPARATOR.Config::START_SCRIPT;
21 | }
22 |
23 | public function line($line)
24 | {
25 | $this->body .= $line ."\n";
26 | return $this;
27 | }
28 |
29 | public function lines(array $lines)
30 | {
31 | $this->body .= implode("\n", $lines)."\n";
32 | return $this;
33 | }
34 |
35 | public function comment($line)
36 | {
37 | return $this->line("# ".$line);
38 | }
39 |
40 | public function linkService($service)
41 | {
42 | $linkDir = Config::getRecipeDir($service);
43 | if (!file_exists($linkDir.DIRECTORY_SEPARATOR.Config::LINK_SCRIPT)) return $this;
44 | $this->comment("linking $service service");
45 | $linkScript = file_get_contents($linkDir.DIRECTORY_SEPARATOR.Config::LINK_SCRIPT);
46 | return $this->line($linkScript);
47 | }
48 |
49 | public function run()
50 | {
51 | $this->printTaskInfo("Writing {$this->filename}");
52 | $res = file_put_contents($this->filename, $this->body);
53 | if ($res === false) return Result::error($this, "File {$this->filename} couldnt be created");
54 | return Result::success($this);
55 | }
56 | }
--------------------------------------------------------------------------------
/src/Command/Travis/Build.php:
--------------------------------------------------------------------------------
1 | null, 'recipes' => false, 'image' => false])
18 | {
19 | if (!$opts['image']) $opts['image'] = Config::$baseImage;
20 | return (new Builder)->execute($opts);
21 | }
22 |
23 | }
24 |
25 | class Builder
26 | {
27 | use \Robo\Task\Exec;
28 | use \Robo\Task\FileSystem;
29 | use \Robo\Output;
30 | use \Codegyre\RoboDocker\DockerTasks;
31 |
32 | function execute($opts)
33 | {
34 | $answer = $this->ask("Bulding Docker container takes some time, you can use already provisioned image instead.\nDo you want to continue? (y/n)");
35 | if (trim(strtolower($answer)) != 'y') return;
36 |
37 | Result::$stopOnFail = true;
38 |
39 | $recipes = $opts['recipes'] ?: implode(',', Config::$travisRecipes);
40 |
41 | (new BuildRecipe('base'))
42 | ->tag('roboci_base')
43 | ->run();
44 |
45 | $res = (new DockerRun('base'))
46 | ->option('-i')
47 | ->option('--privileged')
48 | ->exec('/usr/bin/chef-solo -j /travis.json -o ' . $recipes)
49 | ->run();
50 |
51 | $this->taskDeleteDir(Config::$buildDir)->run();
52 | $data = $res->getData();
53 | $this->yell("Container built in successfully. Run `docker commit {$data['cid']} yourname/imagename` to save it");
54 | }
55 | }
--------------------------------------------------------------------------------
/src/Config.php:
--------------------------------------------------------------------------------
1 | [
67 | 'user' => 'travis',
68 | 'group' => 'travis',
69 | 'home' => '/home/travis/',
70 | 'update_hosts' => false,
71 | ]
72 | ];
73 |
74 | static function getDefaultRecipesDir()
75 | {
76 | return __DIR__.'/../recipes';
77 | }
78 |
79 | static function getUserRecipesDir()
80 | {
81 | return self::$runDir.'/_recipes';
82 | }
83 |
84 | /**
85 | * @param $recipe
86 | * @return string $recipePath
87 | */
88 | static function getRecipeDir($recipe)
89 | {
90 | $userRecipe = Config::getUserRecipesDir() . "/$recipe";
91 | if (file_exists($userRecipe . '/Dockerfile')) {
92 | return $userRecipe;
93 | }
94 |
95 | $defaultRecipe = Config::getDefaultRecipesDir() . "/$recipe";
96 | if (file_exists($defaultRecipe . '/Dockerfile')) {
97 | return $defaultRecipe;
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/src/Runner.php:
--------------------------------------------------------------------------------
1 | env = $env;
24 | $this->envConfig = new RoboEnv($env);
25 | $this->build = uniqid();
26 | $this->dir = Config::$runDir . "/$env/";
27 |
28 | if (!file_exists($this->dir)) throw new Exception("Roboci environment directory {$this->dir} not prepared!");
29 | }
30 |
31 | public function buildImage()
32 | {
33 | $preserveDockerFile = file_exists('Dockerfile');
34 | $fs = $this->taskFileSystemStack();
35 | if ($preserveDockerFile) {
36 | $fs->remove('Dockerfile.saved')->rename('Dockerfile', 'Dockerfile.saved');
37 | }
38 | $fs->copy($this->dir . "Dockerfile", "Dockerfile")
39 | ->run();
40 |
41 | // load start script in shell
42 | $this->taskWriteToFile('Dockerfile')
43 | ->append()
44 | ->line("RUN cat {$this->dir}".Config::START_SCRIPT." >> /home/travis/.bashrc")
45 | ->run();
46 |
47 | $res = $this->taskDockerBuild()
48 | ->tag(Config::$runImage)
49 | ->run();
50 |
51 | $fs = $this->taskFileSystemStack()->remove('Dockerfile');
52 | if ($preserveDockerFile) {
53 | $fs->rename('Dockerfile.saved', 'Dockerfile');
54 | }
55 | $fs->run();
56 | return $res;
57 | }
58 |
59 | public function runServices()
60 | {
61 | foreach ($this->envConfig->services() as $service) {
62 | $container = 'robo_service_' . $service . $this->build;
63 | $this->services[$container.':'.$service] = $this->taskRunService($service)
64 | ->name($container)
65 | ->run();
66 | }
67 | }
68 |
69 | /**
70 | * @return RunContainer
71 | */
72 | public function getContainerRunner()
73 | {
74 | return $this->taskRunContainer(Config::$runImage)
75 | ->name('robo_build_'.$this->build)
76 | ->env('TRAVIS_BUILD_NUMBER', $this->build)
77 | ->linkServices(array_keys($this->services))
78 | ->containerWorkdir(Config::$containerWorkDir)
79 | ->privileged();
80 | }
81 |
82 | public function getRunCommand()
83 | {
84 | $start = $this->getStartCommand();
85 | $command = "";
86 | if (file_exists($this->dir.Config::RUN_SCRIPT)) {
87 | $command = '/bin/bash '.$this->dir.Config::RUN_SCRIPT;
88 | }
89 | if ($start) $command = "$start && $command";
90 | return $command;
91 | }
92 |
93 | public function getStartCommand()
94 | {
95 | if (file_exists($this->dir.Config::START_SCRIPT)) {
96 | return '/bin/bash '.$this->dir.Config::START_SCRIPT;
97 | }
98 | return "";
99 | }
100 |
101 | public function getServices()
102 | {
103 | return $this->services;
104 | }
105 |
106 | public function stopServices()
107 | {
108 | foreach ($this->services as $service) {
109 | if (!$service instanceof \Codegyre\RoboDocker\Result) continue;
110 | $this->taskDockerStop($service)->run();
111 | }
112 | }
113 | }
--------------------------------------------------------------------------------
/src/ConfigParser/Travis.php:
--------------------------------------------------------------------------------
1 | config = Yaml::parse($file);
22 |
23 | if (!isset($this->config['php'])) {
24 | throw new ParseException("No PHP versions in this config, exiting");
25 | }
26 |
27 | if (!isset($this->config['script'])) {
28 | throw new ParseException('No scripts defined in .travis.yml to run, exiting');
29 | }
30 | }
31 |
32 | function install()
33 | {
34 | $beforeInstall = isset($this->config['before_install']) ? $this->config['before_install'] : [];
35 | $install = isset($this->config['install']) ? $this->config['install'] : [];
36 | return array_merge($beforeInstall, $install);
37 | }
38 |
39 | function services()
40 | {
41 | $services = isset($this->config['services']) ? $this->config['services'] : [];
42 | return array_merge(Config::$defaultServices, $services);
43 | }
44 |
45 | function beforeScript()
46 | {
47 | return isset($this->config['before_script']) ? $this->config['before_script'] : [];
48 | }
49 |
50 | function scripts()
51 | {
52 | return isset($this->config['script']) ? $this->config['script'] : [];
53 | }
54 |
55 | public function getConfig()
56 | {
57 | return $this->config;
58 | }
59 |
60 | /**
61 | * (PHP 5 >= 5.0.0)
62 | * Whether a offset exists
63 | * @link http://php.net/manual/en/arrayaccess.offsetexists.php
64 | * @param mixed $offset
65 | * An offset to check for.
66 | *
67 | * @return boolean true on success or false on failure.
68 | *
69 | *
70 | * The return value will be casted to boolean if non-boolean was returned.
71 | */
72 | public function offsetExists($offset)
73 | {
74 | return isset($this->config[$offset]);
75 | }
76 |
77 | /**
78 | * (PHP 5 >= 5.0.0)
79 | * Offset to retrieve
80 | * @link http://php.net/manual/en/arrayaccess.offsetget.php
81 | * @param mixed $offset
82 | * The offset to retrieve.
83 | *
84 | * @return mixed Can return all value types.
85 | */
86 | public function offsetGet($offset)
87 | {
88 | return isset($this->config[$offset]) ? $this->config[$offset] : null;
89 | }
90 |
91 | /**
92 | * (PHP 5 >= 5.0.0)
93 | * Offset to set
94 | * @link http://php.net/manual/en/arrayaccess.offsetset.php
95 | * @param mixed $offset
96 | * The offset to assign the value to.
97 | *
98 | * @param mixed $value
99 | * The value to set.
100 | *
101 | * @return void
102 | */
103 | public function offsetSet($offset, $value)
104 | {
105 | // TODO: Implement offsetSet() method.
106 | }
107 |
108 | /**
109 | * (PHP 5 >= 5.0.0)
110 | * Offset to unset
111 | * @link http://php.net/manual/en/arrayaccess.offsetunset.php
112 | * @param mixed $offset
113 | * The offset to unset.
114 | *
115 | * @return void
116 | */
117 | public function offsetUnset($offset)
118 | {
119 | // TODO: Implement offsetUnset() method.
120 | }
121 | }
--------------------------------------------------------------------------------
/src/Command/CI.php:
--------------------------------------------------------------------------------
1 | buildImage();
29 | $runner->runServices();
30 | $res = $runner->getContainerRunner()
31 | ->exec($runner->getRunCommand())
32 | ->args($args)
33 | ->run();
34 |
35 | $runner->stopServices();
36 |
37 | !$res->getExitCode()
38 | ? $this->yell('BUILD SUCCESSFUL')
39 | : $this->say(" BUILD FAILED ");
40 |
41 | $data = $res->getData();
42 | $this->say("To enter this container, save it with docker commit {$data['cid']} travis_build_failed");
43 | $this->say('Then you can run it: docker run -i -t travis_build_failed bash');
44 | }
45 |
46 | /**
47 | * Runs interactive bash shell in RoboCI environment
48 | * @param $environment
49 | * @param string $args
50 | */
51 | public function ciShell($environment, $args = '')
52 | {
53 | $runner = new Runner($environment);
54 |
55 | $res = $runner->buildImage();
56 | if (!$res->wasSuccessful()) return 1;
57 | $runner->runServices();
58 |
59 | $links = array_keys($runner->getServices());
60 | $links = implode(' ', array_map(function($l) {return "--link $l";}, $links ));
61 |
62 | $command = (new DockerRun(Config::$runImage))
63 | ->option('-t')
64 | ->option('-i')
65 | ->name('robo_shell_'.uniqid())
66 | ->arg($links)
67 | ->arg($args)
68 | ->exec('bash')
69 | ->getCommand();
70 |
71 | $this->yell("To enter shell run `$command`\n");
72 | }
73 |
74 | /**
75 | * Creates raw RoboCI configuration (not taken from .travis.yml)
76 | */
77 | public function ciBootstrap()
78 | {
79 | if (file_exists(Config::$runDir)) {
80 | $this->say(Config::$runDir . " exists, no need to bootstrap");
81 | return;
82 | }
83 | @mkdir(Config::$runDir);
84 |
85 | while ($env = $this->ask("Create new build environment. Enter to exit")) {
86 | $dir = Config::$runDir."/$env/";
87 | @mkdir($dir);
88 | touch($dir.'Dockerfile');
89 | $this->say($dir."Dockerfile created -> Use it to configure build image");
90 | touch($dir.'start.sh');
91 | $this->say($dir."start.sh created. -> executed when container is started");
92 | touch($dir.'run.sh');
93 | $this->say($dir."run.sh created. -> executed on build");
94 | file_put_contents($dir.'env.yml', Yaml::dump(['services' => []]));
95 | $this->say($dir."env.yml created. -> defines running service");
96 | }
97 |
98 | $this->yell('RoboCI raw setup is prepared. See'.Config::$runDir);
99 | }
100 |
101 | /**
102 | * Stops and removes old RoboCI containers
103 | */
104 | public function ciCleanup()
105 | {
106 | $cleaner = new Cleanup();
107 | $cleaner->retrieveContainers();
108 | $cleaner->stopContainers();
109 | $cleaner->removeContainers();
110 | }
111 | }
112 |
113 |
--------------------------------------------------------------------------------
/src/Command/Travis/Prepare.php:
--------------------------------------------------------------------------------
1 | createDockerFile();
31 | $generator->createStartScript();
32 | $generator->createRunScript();
33 | $generator->createEnvConfig();
34 |
35 | }
36 | $this->say("Make sure it is available when executing travis:run");
37 | }
38 |
39 | }
40 |
41 | class EnvGenerator
42 | {
43 | use \Robo\Task\FileSystem;
44 | use \Robo\Output;
45 | use \Codegyre\RoboCI\Tasks;
46 |
47 | protected $php;
48 |
49 | /**
50 | * @var TravisConfig
51 | */
52 | protected $config;
53 |
54 | public function __construct(TravisConfig $config, $php)
55 | {
56 | $this->php = $php;
57 | $this->config = $config;
58 | if (!file_exists(Config::$runDir."/$php")) {
59 | @mkdir(Config::$runDir."/$php");
60 | }
61 | }
62 |
63 | public function createDockerFile()
64 | {
65 | $phpVersion = $this->php;
66 | // create Dockerfile
67 | $filename = Config::$runDir."/$phpVersion/Dockerfile";
68 |
69 | $dockerFile = $this->taskWriteToFile($filename)
70 | ->line('FROM '.Config::$defaultImage)
71 | ->line('WORKDIR '.Config::$containerWorkDir)
72 | ->line('USER root')
73 | ->line('RUN phpenv global '.$phpVersion)
74 | ->line('RUN ["/bin/bash", "-l", "-c", "eval \"$(phpenv init -)\""]')
75 | ->line('ENV TRAVIS_PHP_VERSION '. $phpVersion)
76 | ->line('ENV TRAVIS true')
77 | ->line('ENV CONTINUOUS_INTEGRATION true')
78 | ->line('ENV TRAVIS_BUILD_DIR /home/travis/builds/current')
79 | ->line('ENV TRAVIS_BUILD_NUMBER 0')
80 | ->line('ENV TRAVIS_OS_NAME linux');
81 |
82 | foreach ($this->config->install() as $command) {
83 | $command = str_replace('"', '\"', $command);
84 | $dockerFile->line('RUN ["/bin/bash", "-l", "-c", "'.$command.'"]');
85 | }
86 |
87 | $dockerFile->line('ADD . '.Config::$containerWorkDir)
88 | ->line('USER root')
89 | ->line('RUN chown -R '.Config::TRAVIS_USER.' '.Config::$containerWorkDir) // https://github.com/docker/docker/issues/1295
90 | ->line('USER travis')
91 | ->line('ENV HOME /home/travis')
92 | ->line('ENV PATH $PATH:/home/travis/.phpenv/bin')
93 | ->run();
94 |
95 | $this->say("Dockerfile for running Travis saved to $filename");
96 | }
97 |
98 | public function createStartScript()
99 | {
100 | $php = $this->php;
101 | $startScript = $this->taskCreateStartScript(Config::$runDir."/$php")
102 | ->line('echo "------[ RUNNING START SCRIPT ]-----"')
103 | ->comment("switching PHP version")
104 | ->line('phpenv global '.$php)
105 | ->line('eval "$(phpenv init -)"')
106 | ->line('php -v');
107 |
108 | foreach ($this->config->services() as $service) {
109 | $startScript->linkService($service);
110 | }
111 |
112 | $startScript
113 | ->comment("before_script section .travis.yml")
114 | ->lines($this->config->beforeScript())
115 | ->run();
116 | }
117 |
118 | public function createRunScript()
119 | {
120 | $this->taskCreateRunScript(Config::$runDir."/".$this->php)
121 | ->scripts($this->config->scripts())
122 | ->run();
123 | }
124 |
125 | public function createEnvConfig()
126 | {
127 | $this->taskWriteToFile(Config::$runDir."/".$this->php.DIRECTORY_SEPARATOR.Config::ROBOCI_ENV_CONFIG_FILE)
128 | ->line(Yaml::dump(['services' => $this->config->services()]))
129 | ->run();
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RoboCI
2 |
3 | RoboCI is virtualized environment runner for Continuous Integration servers.
4 | RoboCI is aimed to **run Travis CI builds locally** inside [Docker](http://docker.io) containers as well creating custom build setup.
5 |
6 | ## RoboCI is used to:
7 |
8 | * create virtualized environments with Docker.
9 | * run acceptance, functional, unit, integration tests in isolated containers.
10 | * run Travis CI builds locally or on CI server.
11 | * debug builds inside containers
12 |
13 | ## Requirements
14 |
15 | Requires [Docker](http://docker.io) and [Robo PHP Task Runner](http://robo.li) to be installed.
16 |
17 | ## Installation
18 |
19 | Use Composer
20 |
21 | ```
22 | {
23 | "require-dev": {
24 | "codegyre/robo": "*",
25 | "codegyre/robo-ci": "@dev"
26 | }
27 | }
28 |
29 | ```
30 |
31 | Create `RoboFile.php` in the root of your project (if it is not already there), by simply running `robo`.
32 |
33 | Attach composer autoloader to include `Codegyre\RoboCI` into your RoboFile:
34 |
35 | ``` php
36 | =5.4.0",
27 | "symfony/console": "~2.1",
28 | "symfony/filesystem": "~2.1",
29 | "symfony/finder": "~2.1",
30 | "symfony/process": "~2.1"
31 | },
32 | "require-dev": {
33 | "codeception/aspect-mock": "0.4.*",
34 | "codeception/codeception": "~2.0",
35 | "codeception/verify": "0.2.*",
36 | "natxet/cssmin": "~3.0",
37 | "patchwork/jsqueeze": "~1.0"
38 | },
39 | "bin": [
40 | "robo"
41 | ],
42 | "type": "library",
43 | "autoload": {
44 | "psr-4": {
45 | "Robo\\": "src"
46 | }
47 | },
48 | "notification-url": "https://packagist.org/downloads/",
49 | "license": [
50 | "MIT"
51 | ],
52 | "authors": [
53 | {
54 | "name": "Davert",
55 | "email": "davert.php@resend.cc"
56 | }
57 | ],
58 | "description": "Modern task runner",
59 | "time": "2014-12-26 04:22:04"
60 | },
61 | {
62 | "name": "codegyre/robo-docker",
63 | "version": "0.1.0",
64 | "source": {
65 | "type": "git",
66 | "url": "https://github.com/Codegyre/robo-docker.git",
67 | "reference": "69aec8b828890e86d5c74bb53c74045ce5ca2f70"
68 | },
69 | "dist": {
70 | "type": "zip",
71 | "url": "https://api.github.com/repos/Codegyre/robo-docker/zipball/69aec8b828890e86d5c74bb53c74045ce5ca2f70",
72 | "reference": "69aec8b828890e86d5c74bb53c74045ce5ca2f70",
73 | "shasum": ""
74 | },
75 | "type": "library",
76 | "autoload": {
77 | "psr-4": {
78 | "Codegyre\\RoboDocker\\": "src"
79 | }
80 | },
81 | "notification-url": "https://packagist.org/downloads/",
82 | "license": [
83 | "MIT"
84 | ],
85 | "authors": [
86 | {
87 | "name": "Davert",
88 | "email": "davert.php@resend.cc"
89 | }
90 | ],
91 | "description": "Docker Tasks for Robo Task Runner",
92 | "time": "2014-09-26 23:58:29"
93 | },
94 | {
95 | "name": "henrikbjorn/lurker",
96 | "version": "1.0.0",
97 | "source": {
98 | "type": "git",
99 | "url": "https://github.com/henrikbjorn/Lurker.git",
100 | "reference": "a020d45b3bc37810aeafe27343c51af8a74c9419"
101 | },
102 | "dist": {
103 | "type": "zip",
104 | "url": "https://api.github.com/repos/henrikbjorn/Lurker/zipball/a020d45b3bc37810aeafe27343c51af8a74c9419",
105 | "reference": "a020d45b3bc37810aeafe27343c51af8a74c9419",
106 | "shasum": ""
107 | },
108 | "require": {
109 | "php": ">=5.3.3",
110 | "symfony/config": "~2.2",
111 | "symfony/event-dispatcher": "~2.2"
112 | },
113 | "suggest": {
114 | "ext-inotify": ">=0.1.6"
115 | },
116 | "type": "library",
117 | "extra": {
118 | "branch-alias": {
119 | "dev-master": "1.0.x-dev"
120 | }
121 | },
122 | "autoload": {
123 | "psr-0": {
124 | "Lurker": "src"
125 | }
126 | },
127 | "notification-url": "https://packagist.org/downloads/",
128 | "license": [
129 | "MIT"
130 | ],
131 | "authors": [
132 | {
133 | "name": "Henrik Bjornskov",
134 | "email": "henrik@bjrnskov.dk",
135 | "homepage": "http://henrik.bjrnskov.dk"
136 | },
137 | {
138 | "name": "Konstantin Kudryashov",
139 | "email": "ever.zet@gmail.com",
140 | "homepage": "http://everzet.com"
141 | },
142 | {
143 | "name": "Yaroslav Kiliba",
144 | "email": "om.dattaya@gmail.com"
145 | }
146 | ],
147 | "description": "Resource Watcher.",
148 | "keywords": [
149 | "filesystem",
150 | "resource",
151 | "watching"
152 | ],
153 | "time": "2013-05-24 06:47:29"
154 | },
155 | {
156 | "name": "symfony/config",
157 | "version": "v2.7.5",
158 | "source": {
159 | "type": "git",
160 | "url": "https://github.com/symfony/config.git",
161 | "reference": "9698fdf0a750d6887d5e7729d5cf099765b20e61"
162 | },
163 | "dist": {
164 | "type": "zip",
165 | "url": "https://api.github.com/repos/symfony/config/zipball/9698fdf0a750d6887d5e7729d5cf099765b20e61",
166 | "reference": "9698fdf0a750d6887d5e7729d5cf099765b20e61",
167 | "shasum": ""
168 | },
169 | "require": {
170 | "php": ">=5.3.9",
171 | "symfony/filesystem": "~2.3"
172 | },
173 | "require-dev": {
174 | "symfony/phpunit-bridge": "~2.7"
175 | },
176 | "type": "library",
177 | "extra": {
178 | "branch-alias": {
179 | "dev-master": "2.7-dev"
180 | }
181 | },
182 | "autoload": {
183 | "psr-4": {
184 | "Symfony\\Component\\Config\\": ""
185 | }
186 | },
187 | "notification-url": "https://packagist.org/downloads/",
188 | "license": [
189 | "MIT"
190 | ],
191 | "authors": [
192 | {
193 | "name": "Fabien Potencier",
194 | "email": "fabien@symfony.com"
195 | },
196 | {
197 | "name": "Symfony Community",
198 | "homepage": "https://symfony.com/contributors"
199 | }
200 | ],
201 | "description": "Symfony Config Component",
202 | "homepage": "https://symfony.com",
203 | "time": "2015-09-21 15:02:29"
204 | },
205 | {
206 | "name": "symfony/console",
207 | "version": "v2.7.5",
208 | "source": {
209 | "type": "git",
210 | "url": "https://github.com/symfony/console.git",
211 | "reference": "06cb17c013a82f94a3d840682b49425cd00a2161"
212 | },
213 | "dist": {
214 | "type": "zip",
215 | "url": "https://api.github.com/repos/symfony/console/zipball/06cb17c013a82f94a3d840682b49425cd00a2161",
216 | "reference": "06cb17c013a82f94a3d840682b49425cd00a2161",
217 | "shasum": ""
218 | },
219 | "require": {
220 | "php": ">=5.3.9"
221 | },
222 | "require-dev": {
223 | "psr/log": "~1.0",
224 | "symfony/event-dispatcher": "~2.1",
225 | "symfony/phpunit-bridge": "~2.7",
226 | "symfony/process": "~2.1"
227 | },
228 | "suggest": {
229 | "psr/log": "For using the console logger",
230 | "symfony/event-dispatcher": "",
231 | "symfony/process": ""
232 | },
233 | "type": "library",
234 | "extra": {
235 | "branch-alias": {
236 | "dev-master": "2.7-dev"
237 | }
238 | },
239 | "autoload": {
240 | "psr-4": {
241 | "Symfony\\Component\\Console\\": ""
242 | }
243 | },
244 | "notification-url": "https://packagist.org/downloads/",
245 | "license": [
246 | "MIT"
247 | ],
248 | "authors": [
249 | {
250 | "name": "Fabien Potencier",
251 | "email": "fabien@symfony.com"
252 | },
253 | {
254 | "name": "Symfony Community",
255 | "homepage": "https://symfony.com/contributors"
256 | }
257 | ],
258 | "description": "Symfony Console Component",
259 | "homepage": "https://symfony.com",
260 | "time": "2015-09-25 08:32:23"
261 | },
262 | {
263 | "name": "symfony/event-dispatcher",
264 | "version": "v2.7.5",
265 | "source": {
266 | "type": "git",
267 | "url": "https://github.com/symfony/event-dispatcher.git",
268 | "reference": "ae4dcc2a8d3de98bd794167a3ccda1311597c5d9"
269 | },
270 | "dist": {
271 | "type": "zip",
272 | "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ae4dcc2a8d3de98bd794167a3ccda1311597c5d9",
273 | "reference": "ae4dcc2a8d3de98bd794167a3ccda1311597c5d9",
274 | "shasum": ""
275 | },
276 | "require": {
277 | "php": ">=5.3.9"
278 | },
279 | "require-dev": {
280 | "psr/log": "~1.0",
281 | "symfony/config": "~2.0,>=2.0.5",
282 | "symfony/dependency-injection": "~2.6",
283 | "symfony/expression-language": "~2.6",
284 | "symfony/phpunit-bridge": "~2.7",
285 | "symfony/stopwatch": "~2.3"
286 | },
287 | "suggest": {
288 | "symfony/dependency-injection": "",
289 | "symfony/http-kernel": ""
290 | },
291 | "type": "library",
292 | "extra": {
293 | "branch-alias": {
294 | "dev-master": "2.7-dev"
295 | }
296 | },
297 | "autoload": {
298 | "psr-4": {
299 | "Symfony\\Component\\EventDispatcher\\": ""
300 | }
301 | },
302 | "notification-url": "https://packagist.org/downloads/",
303 | "license": [
304 | "MIT"
305 | ],
306 | "authors": [
307 | {
308 | "name": "Fabien Potencier",
309 | "email": "fabien@symfony.com"
310 | },
311 | {
312 | "name": "Symfony Community",
313 | "homepage": "https://symfony.com/contributors"
314 | }
315 | ],
316 | "description": "Symfony EventDispatcher Component",
317 | "homepage": "https://symfony.com",
318 | "time": "2015-09-22 13:49:29"
319 | },
320 | {
321 | "name": "symfony/filesystem",
322 | "version": "v2.7.5",
323 | "source": {
324 | "type": "git",
325 | "url": "https://github.com/symfony/filesystem.git",
326 | "reference": "a17f8a17c20e8614c15b8e116e2f4bcde102cfab"
327 | },
328 | "dist": {
329 | "type": "zip",
330 | "url": "https://api.github.com/repos/symfony/filesystem/zipball/a17f8a17c20e8614c15b8e116e2f4bcde102cfab",
331 | "reference": "a17f8a17c20e8614c15b8e116e2f4bcde102cfab",
332 | "shasum": ""
333 | },
334 | "require": {
335 | "php": ">=5.3.9"
336 | },
337 | "require-dev": {
338 | "symfony/phpunit-bridge": "~2.7"
339 | },
340 | "type": "library",
341 | "extra": {
342 | "branch-alias": {
343 | "dev-master": "2.7-dev"
344 | }
345 | },
346 | "autoload": {
347 | "psr-4": {
348 | "Symfony\\Component\\Filesystem\\": ""
349 | }
350 | },
351 | "notification-url": "https://packagist.org/downloads/",
352 | "license": [
353 | "MIT"
354 | ],
355 | "authors": [
356 | {
357 | "name": "Fabien Potencier",
358 | "email": "fabien@symfony.com"
359 | },
360 | {
361 | "name": "Symfony Community",
362 | "homepage": "https://symfony.com/contributors"
363 | }
364 | ],
365 | "description": "Symfony Filesystem Component",
366 | "homepage": "https://symfony.com",
367 | "time": "2015-09-09 17:42:36"
368 | },
369 | {
370 | "name": "symfony/finder",
371 | "version": "v2.7.5",
372 | "source": {
373 | "type": "git",
374 | "url": "https://github.com/symfony/finder.git",
375 | "reference": "8262ab605973afbb3ef74b945daabf086f58366f"
376 | },
377 | "dist": {
378 | "type": "zip",
379 | "url": "https://api.github.com/repos/symfony/finder/zipball/8262ab605973afbb3ef74b945daabf086f58366f",
380 | "reference": "8262ab605973afbb3ef74b945daabf086f58366f",
381 | "shasum": ""
382 | },
383 | "require": {
384 | "php": ">=5.3.9"
385 | },
386 | "require-dev": {
387 | "symfony/phpunit-bridge": "~2.7"
388 | },
389 | "type": "library",
390 | "extra": {
391 | "branch-alias": {
392 | "dev-master": "2.7-dev"
393 | }
394 | },
395 | "autoload": {
396 | "psr-4": {
397 | "Symfony\\Component\\Finder\\": ""
398 | }
399 | },
400 | "notification-url": "https://packagist.org/downloads/",
401 | "license": [
402 | "MIT"
403 | ],
404 | "authors": [
405 | {
406 | "name": "Fabien Potencier",
407 | "email": "fabien@symfony.com"
408 | },
409 | {
410 | "name": "Symfony Community",
411 | "homepage": "https://symfony.com/contributors"
412 | }
413 | ],
414 | "description": "Symfony Finder Component",
415 | "homepage": "https://symfony.com",
416 | "time": "2015-09-19 19:59:23"
417 | },
418 | {
419 | "name": "symfony/process",
420 | "version": "v2.7.5",
421 | "source": {
422 | "type": "git",
423 | "url": "https://github.com/symfony/process.git",
424 | "reference": "b27c8e317922cd3cdd3600850273cf6b82b2e8e9"
425 | },
426 | "dist": {
427 | "type": "zip",
428 | "url": "https://api.github.com/repos/symfony/process/zipball/b27c8e317922cd3cdd3600850273cf6b82b2e8e9",
429 | "reference": "b27c8e317922cd3cdd3600850273cf6b82b2e8e9",
430 | "shasum": ""
431 | },
432 | "require": {
433 | "php": ">=5.3.9"
434 | },
435 | "require-dev": {
436 | "symfony/phpunit-bridge": "~2.7"
437 | },
438 | "type": "library",
439 | "extra": {
440 | "branch-alias": {
441 | "dev-master": "2.7-dev"
442 | }
443 | },
444 | "autoload": {
445 | "psr-4": {
446 | "Symfony\\Component\\Process\\": ""
447 | }
448 | },
449 | "notification-url": "https://packagist.org/downloads/",
450 | "license": [
451 | "MIT"
452 | ],
453 | "authors": [
454 | {
455 | "name": "Fabien Potencier",
456 | "email": "fabien@symfony.com"
457 | },
458 | {
459 | "name": "Symfony Community",
460 | "homepage": "https://symfony.com/contributors"
461 | }
462 | ],
463 | "description": "Symfony Process Component",
464 | "homepage": "https://symfony.com",
465 | "time": "2015-09-19 19:59:23"
466 | },
467 | {
468 | "name": "symfony/yaml",
469 | "version": "v2.7.5",
470 | "source": {
471 | "type": "git",
472 | "url": "https://github.com/symfony/yaml.git",
473 | "reference": "31cb2ad0155c95b88ee55fe12bc7ff92232c1770"
474 | },
475 | "dist": {
476 | "type": "zip",
477 | "url": "https://api.github.com/repos/symfony/yaml/zipball/31cb2ad0155c95b88ee55fe12bc7ff92232c1770",
478 | "reference": "31cb2ad0155c95b88ee55fe12bc7ff92232c1770",
479 | "shasum": ""
480 | },
481 | "require": {
482 | "php": ">=5.3.9"
483 | },
484 | "require-dev": {
485 | "symfony/phpunit-bridge": "~2.7"
486 | },
487 | "type": "library",
488 | "extra": {
489 | "branch-alias": {
490 | "dev-master": "2.7-dev"
491 | }
492 | },
493 | "autoload": {
494 | "psr-4": {
495 | "Symfony\\Component\\Yaml\\": ""
496 | }
497 | },
498 | "notification-url": "https://packagist.org/downloads/",
499 | "license": [
500 | "MIT"
501 | ],
502 | "authors": [
503 | {
504 | "name": "Fabien Potencier",
505 | "email": "fabien@symfony.com"
506 | },
507 | {
508 | "name": "Symfony Community",
509 | "homepage": "https://symfony.com/contributors"
510 | }
511 | ],
512 | "description": "Symfony Yaml Component",
513 | "homepage": "https://symfony.com",
514 | "time": "2015-09-14 14:14:09"
515 | }
516 | ],
517 | "packages-dev": [],
518 | "aliases": [],
519 | "minimum-stability": "stable",
520 | "stability-flags": [],
521 | "prefer-stable": false,
522 | "prefer-lowest": false,
523 | "platform": {
524 | "php": ">=5.4.0"
525 | },
526 | "platform-dev": []
527 | }
528 |
--------------------------------------------------------------------------------