Exemplo n.º 1
0
 public function build(Build $build, BuildScript $script, $timeout)
 {
     $logger = $this->logger;
     $docker = $this->docker;
     $objectManager = $this->objectManager;
     $websocketProducer = $this->websocketProducer;
     $publish = function ($content) use($build, $websocketProducer) {
         $message = new BuildMessage($build, $content);
         $websocketProducer->publish((string) $message);
     };
     $options = $build->getOptions();
     $project = $build->getProject();
     /**
      * Launch actual build
      */
     $logger->info('building base build container', ['build' => $build->getId(), 'image_name' => $build->getImageName()]);
     // $publish('  building base container'.PHP_EOL);
     $baseImage = strpos($options['image'], 'stage1/') !== 0 ? 'stage1/' . $options['image'] : $options['image'];
     $builder = $project->getDockerContextBuilder();
     $builder->add('/usr/local/bin/yuhao_build', $script->getBuildScript());
     $builder->add('/usr/local/bin/yuhao_run', $script->getRunScript());
     $builder->run('chmod -R +x /usr/local/bin/');
     $builder->from($baseImage);
     $response = $docker->build($builder->getContext(), $build->getImageName(), false, true, true);
     $buildContainer = new BuildContainer($build);
     $buildContainer->addEnv($options['env']);
     $script->setRuntimeEnv($buildContainer->getEnv());
     if ($build->getForceLocalBuildYml()) {
         $buildContainer->addEnv(['FORCE_LOCAL_STAGE1_YML=1']);
     }
     $manager = $docker->getContainerManager();
     $logger->info('starting actual build', ['build' => $build->getId(), 'timeout' => $timeout]);
     // $publish('  starting actual build'.PHP_EOL);
     $hostConfig = [];
     if ($this->getOption('composer_enable_global_cache')) {
         $logger->info('enabling composer global cache', ['build' => $build->getId()]);
         $hostConfig['Binds'] = [$this->getOption('composer_cache_path') . '/global:/.composer/cache'];
     } elseif ($this->getOption('composer_enable_project_cache')) {
         $cachePath = $this->getOption('composer_cache_path') . '/' . $project->getFullName();
         $logger->info('enabling composer project cache', ['build' => $build->getId(), 'project' => $project->getFullName(), 'cache_path' => $cachePath]);
         if (!is_dir($cachePath)) {
             mkdir($cachePath, 0777, true);
         }
         $hostConfig['Binds'] = [realpath($cachePath) . ':/.composer/cache'];
     }
     $manager->create($buildContainer);
     $build->setContainer($buildContainer);
     $manager->start($buildContainer, $hostConfig);
     $manager->wait($buildContainer, $timeout);
     if ($buildContainer->getExitCode() !== 0) {
         $exitCode = $buildContainer->getExitCode();
         $exitCodeLabel = isset(Process::$exitCodes[$exitCode]) ? Process::$exitCodes[$exitCode] : '';
         $message = sprintf('build container stopped with exit code %d (%s)', $exitCode, $exitCodeLabel);
         $logger->error($message, ['build' => $build->getId(), 'container' => $buildContainer->getId(), 'container_name' => $buildContainer->getName(), 'exit_code' => $exitCode, 'exit_code_label' => $exitCodeLabel]);
         $docker->commit($buildContainer, ['repo' => $build->getImageName(), 'tag' => 'failed']);
         throw new Exception($message, $buildContainer->getExitCode());
     }
     /**
      * Build successful!
      *
      * @todo the commit can timeout for no obvious reason, while actually committing
      *       catch the timeout and check if the image has been committed
      *          if yes, proceed
      *          if not, retry (3 times ?)
      */
     $logger->info('build successful, committing', ['build' => $build->getId(), 'container' => $buildContainer->getId()]);
     // $publish('  committing app container'.PHP_EOL);
     $docker->commit($buildContainer, ['repo' => $build->getImageName()]);
     $logger->info('removing build container', ['build' => $build->getId(), 'container' => $buildContainer->getId()]);
     // $publish('  removing build container'.PHP_EOL);
     $manager->remove($buildContainer);
 }
Exemplo n.º 2
0
 public function build(Build $build, BuildScript $script, $timeout)
 {
     $logger = $this->logger;
     $docker = $this->docker;
     $websocketProducer = $this->websocketProducer;
     $redis = $this->redis;
     $publish = function ($content) use($build, $websocketProducer, $redis) {
         static $fragment = 0;
         $message = new BuildMessage($build, $content);
         $websocketProducer->publish((string) $message);
         $redis->rpush($build->getLogsList(), json_encode(['type' => Build::LOG_OUTPUT, 'message' => $content, 'stream' => 'stdout', 'microtime' => microtime(true), 'fragment_id' => $fragment++, 'build_id' => $build->getId()]));
     };
     $project = $build->getProject();
     $options = $build->getOptions();
     $workdir = sys_get_temp_dir() . '/stage1/workdir/' . $build->getId();
     if (is_dir($workdir)) {
         $fs = new Filesystem();
         $fs->remove($workdir);
     }
     mkdir($workdir, 0777, true);
     $logger->info('using workdir', ['workdir' => $workdir]);
     mkdir($workdir . '/ssh', 0755, true);
     $project->dumpSshKeys($workdir . '/ssh', 'root');
     file_put_contents($workdir . '/ssh/config', $project->getSshConfig($workdir . '/ssh'));
     file_put_contents($workdir . '/git_ssh', "#!/bin/bash\nexec /usr/bin/ssh -F {$workdir}/ssh/config \"\$@\"");
     chmod($workdir . '/git_ssh', 0777);
     $GIT_SSH = $workdir . '/git_ssh';
     if ($build->isPullRequest()) {
         $clone = ProcessBuilder::create(['git', 'clone', '--quiet', '--depth', '1', $project->getGitUrl(), $workdir . '/source'])->setEnv('GIT_SSH', $GIT_SSH)->getProcess();
         $commandLine = $clone->getCommandLine();
         $logger->info('cloning repository', ['command_line' => $commandLine]);
         $publish('$ ' . substr($commandLine, 0, strrpos($commandLine, ' ')) . PHP_EOL);
         $clone->run();
         $fetch = ProcessBuilder::create(['git', 'fetch', '--quiet', 'origin', 'refs/' . $build->getRef()])->setEnv('GIT_SSH', $GIT_SSH)->setWorkingDirectory($workdir . '/source')->getProcess();
         $logger->info('fecthing pull request', ['command_line' => $fetch->getCommandLine()]);
         $publish('$ ' . $fetch->getCommandLine() . PHP_EOL);
         $fetch->run();
         $checkout = ProcessBuilder::create(['git', 'checkout', '--quiet', '-b', 'pull_request', 'FETCH_HEAD'])->setEnv('GIT_SSH', $GIT_SSH)->setWorkingDirectory($workdir . '/source')->getProcess();
         $logger->info('checkouting pull request', ['command_line' => $checkout->getCommandLine()]);
         $publish('$ ' . $checkout->getCommandLine() . PHP_EOL);
         $checkout->run();
     } else {
         $clone = ProcessBuilder::create(['git', 'clone', '--quiet', '--depth', '1', '--branch', $build->getRef(), $project->getGitUrl(), $workdir . '/source'])->setEnv('GIT_SSH', $GIT_SSH)->getProcess();
         $commandLine = $clone->getCommandLine();
         $logger->info('cloning repository', ['command_line' => $commandLine]);
         $publish('$ ' . substr($commandLine, 0, strrpos($commandLine, ' ')) . PHP_EOL);
         $clone->run();
     }
     $contextPath = $workdir . '/source/' . $options['dockerfile']['path'];
     $logger->info('creating docker build context from path', ['context_path' => $contextPath]);
     $context = new Context($contextPath);
     $logger->info('starting actual build', ['build' => $build->getId(), 'timeout' => $timeout]);
     $response = $docker->build($context, $build->getImageName(), false, false, true, false);
     $error = false;
     $response->read(function ($output) use($logger, $response, $publish, &$error) {
         if ($response->headers->get('content-type') === 'application/json') {
             $output = json_decode($output, true);
             $logger->info('got data chunk', ['output' => $output]);
             if (isset($output['stream'])) {
                 $publish($output['stream']);
             }
         } else {
             $message = $output;
         }
     });
 }