public function testGetAppConfigNested()
 {
     $fakeAppRoot = 'tests/data/repositories/multiple/nest/nested';
     $builder = new LocalBuild();
     $config = $builder->getAppConfig($fakeAppRoot);
     $this->assertEquals(array('name' => 'nested1'), $config);
 }
 /**
  * Test building a project from dummy source code.
  *
  * @param string $sourceDir
  *   A directory containing source code for the project or app. Files will
  *   be copied into a dummy project.
  *
  * @return string
  *   The project root for the dummy project.
  */
 protected function assertBuildSucceeds($sourceDir)
 {
     $projectRoot = $this->createDummyProject($sourceDir);
     self::$output->writeln("\nTesting build for directory: " . $sourceDir);
     $success = $this->builder->buildProject($projectRoot);
     $this->assertTrue($success, 'Build success for dir: ' . $sourceDir);
     return $projectRoot;
 }
Example #3
0
 /**
  * Test with a custom destination.
  */
 public function testBuildCustomDestination()
 {
     $projectRoot = $this->createDummyProject('tests/data/apps/vanilla');
     $destination = $projectRoot . '/web';
     $builder = new LocalBuild($this->buildSettings, self::$output);
     $builder->buildProject($projectRoot, null, $destination);
     $this->assertFileExists($destination . '/index.html');
 }
Example #4
0
 public function testBuildUpdateLock()
 {
     $sourceDir = 'tests/data/apps/drupal/project-yaml';
     $projectRoot = $this->createDummyProject($sourceDir);
     self::$output->writeln("\nTesting build (with --lock) for directory: " . $sourceDir);
     $builder = new LocalBuild($this->buildSettings + array('drushUpdateLock' => true), self::$output);
     $success = $builder->buildProject($projectRoot);
     $this->assertTrue($success, 'Build success for dir: ' . $sourceDir);
     $repositoryDir = $projectRoot . '/' . LocalProject::REPOSITORY_DIR;
     $this->assertFileExists("{$repositoryDir}/project.make.yml.lock");
 }
 protected function execute(InputInterface $input, OutputInterface $output)
 {
     $projectRoot = $this->getProjectRoot();
     if (empty($projectRoot)) {
         $output->writeln("<error>You must run this command from a project folder.</error>");
         return 1;
     }
     if ($this->isLoggedIn()) {
         $project = $this->getCurrentProject();
         if (!$project) {
             throw new \RuntimeException("Could not determine the current project");
         }
         $environment = $this->getCurrentEnvironment($project);
         if (!$environment) {
             throw new \RuntimeException("Could not determine the current environment");
         }
         $envId = $environment['id'];
     } else {
         // Login was skipped so we figure out the environment ID from git.
         $head = file($projectRoot . '/' . LocalProject::REPOSITORY_DIR . '/.git/HEAD');
         $branchRef = $head[0];
         $branch = trim(substr($branchRef, 16));
         $envId = $branch;
     }
     $apps = $input->getArgument('app');
     $settings = array();
     // The environment ID is used in making the build directory name.
     $settings['environmentId'] = $envId;
     $settings['verbosity'] = $output->getVerbosity();
     $settings['drushConcurrency'] = $input->hasOption('concurrency') ? $input->getOption('concurrency') : 3;
     // Some simple settings flags.
     $settingsMap = array('absoluteLinks' => 'abslinks', 'drushWorkingCopy' => 'working-copy', 'noArchive' => 'no-archive', 'noCache' => 'no-cache', 'noClean' => 'no-clean');
     foreach ($settingsMap as $setting => $option) {
         $settings[$setting] = $input->hasOption($option) && $input->getOption($option);
     }
     try {
         $builder = new LocalBuild($settings, $output);
         $success = $builder->buildProject($projectRoot, $apps);
     } catch (\Exception $e) {
         $output->writeln("<error>The build failed with an error</error>");
         $formattedMessage = $this->getHelper('formatter')->formatBlock($e->getMessage(), 'error');
         $output->writeln($formattedMessage);
         return 1;
     }
     return $success ? 0 : 2;
 }
Example #6
0
 public function testBuildDrupalInProjectMode()
 {
     $sourceDir = 'tests/data/apps/drupal/project';
     $projectRoot = $this->createDummyProject($sourceDir);
     $webRoot = $projectRoot . '/' . self::$config->get('local.web_root');
     $shared = $projectRoot . '/' . self::$config->get('local.shared_dir');
     $buildDir = $projectRoot . '/' . self::$config->get('local.build_dir') . '/default';
     // Insert a dummy file into 'shared'.
     if (!file_exists($shared)) {
         mkdir($shared, 0755, true);
     }
     touch($shared . '/symlink_me');
     self::$output->writeln("\nTesting build for directory: " . $sourceDir);
     $buildSettings = ['abslinks' => true];
     $builder = new LocalBuild($buildSettings + $this->buildSettings, null, self::$output);
     $success = $builder->build($projectRoot);
     $this->assertTrue($success, 'Build success for dir: ' . $sourceDir);
     // Test build results.
     $this->assertFileExists($webRoot . '/index.php');
     // Test installation results: firstly, the mounts.
     $this->assertFileExists($webRoot . '/sites/default/files');
     $this->assertFileExists($buildDir . '/tmp');
     $this->assertFileExists($buildDir . '/private');
     $this->assertFileExists($buildDir . '/drush-backups');
     $this->assertEquals($shared . '/files', readlink($webRoot . '/sites/default/files'));
     $this->assertEquals($shared . '/tmp', readlink($buildDir . '/tmp'));
     $this->assertEquals($shared . '/private', readlink($buildDir . '/private'));
     $this->assertEquals($shared . '/drush-backups', readlink($buildDir . '/drush-backups'));
     // Secondly, the special Drupal settings files.
     $this->assertFileExists($webRoot . '/sites/default/settings.php');
     $this->assertFileExists($webRoot . '/sites/default/settings.local.php');
     // Thirdly, the ability for any files in 'shared' to be symlinked into
     // sites/default (this is a legacy feature of the CLI's Drupal
     // toolstack).
     $this->assertFileExists($webRoot . '/sites/default/symlink_me');
     // Test custom build hooks' results.
     // Build hooks are not Drupal-specific, but they can only run if the
     // build process creates a build directory outside the repository -
     // Drupal is the only current example of this.
     $this->assertFileNotExists($webRoot . '/robots.txt');
     $this->assertFileExists($webRoot . '/test.txt');
     // Test building the same project again.
     $success2 = $builder->build($projectRoot);
     $this->assertTrue($success2, 'Second build success for dir: ' . $sourceDir);
 }
 protected function execute(InputInterface $input, OutputInterface $output)
 {
     $projectRoot = $this->getProjectRoot();
     if (!$projectRoot) {
         throw new RootNotFoundException();
     }
     $builder = new LocalBuild(array(), $this->stdErr);
     $result = $builder->cleanBuilds($projectRoot, $input->getOption('max-age'), $input->getOption('keep'), $input->getOption('include-active'), false);
     if (!$result[0] && !$result[1]) {
         $this->stdErr->writeln("There are no builds to delete");
     } else {
         if ($result[0]) {
             $this->stdErr->writeln("Deleted <info>{$result[0]}</info> build(s)");
         }
         if ($result[1]) {
             $this->stdErr->writeln("Kept <info>{$result[1]}</info> build(s)");
         }
     }
     $archivesResult = $builder->cleanArchives($projectRoot);
     if ($archivesResult[0]) {
         $this->stdErr->writeln("Deleted <info>{$archivesResult[0]}</info> archive(s)");
     }
 }
 protected function execute(InputInterface $input, OutputInterface $output)
 {
     $projectRoot = $this->getProjectRoot();
     if (empty($projectRoot)) {
         $output->writeln("<error>You must run this command from a project folder.</error>");
         return;
     }
     $builder = new LocalBuild(array(), $output);
     $result = $builder->cleanBuilds($projectRoot, $input->getOption('max-age'), $input->getOption('keep'), $input->getOption('include-active'), false);
     if (!$result[0] && !$result[1]) {
         $output->writeln("There are no builds to delete");
     } else {
         if ($result[0]) {
             $output->writeln("Deleted <info>{$result[0]}</info> build(s)");
         }
         if ($result[1]) {
             $output->writeln("Kept <info>{$result[1]}</info> build(s)");
         }
     }
     $archivesResult = $builder->cleanArchives($projectRoot);
     if ($archivesResult[0]) {
         $output->writeln("Deleted <info>{$archivesResult[0]}</info> archive(s)");
     }
 }
 protected function execute(InputInterface $input, OutputInterface $output)
 {
     $projectId = $input->getArgument('id');
     if (empty($projectId)) {
         if ($input->isInteractive() && ($projects = $this->getProjects(true))) {
             $projectId = $this->offerProjectChoice($projects, $input, $output);
         } else {
             $output->writeln("<error>You must specify a project.</error>");
             return 1;
         }
     }
     $project = $this->getProject($projectId);
     if (!$project) {
         $output->writeln("<error>Project not found: {$projectId}</error>");
         return 1;
     }
     $directoryName = $input->getArgument('directory-name');
     if (empty($directoryName)) {
         $directoryName = $projectId;
     }
     if (file_exists($directoryName)) {
         $output->writeln("<error>The project directory '{$directoryName}' already exists.</error>");
         return 1;
     }
     if ($projectRoot = $this->getProjectRoot()) {
         if (strpos(realpath(dirname($directoryName)), $projectRoot) === 0) {
             $output->writeln("<error>A project cannot be cloned inside another project.</error>");
             return 1;
         }
     }
     // Create the directory structure.
     mkdir($directoryName);
     $projectRoot = realpath($directoryName);
     if (!$projectRoot) {
         throw new \Exception("Failed to create project directory: {$directoryName}");
     }
     $output->writeln("Created project directory: {$directoryName}");
     $local = new LocalProject();
     $local->createProjectFiles($projectRoot, $projectId);
     $environments = $this->getEnvironments($project, true);
     $environmentOption = $input->getOption('environment');
     if ($environmentOption) {
         if (!isset($environments[$environmentOption])) {
             $output->writeln("<error>Environment not found: {$environmentOption}</error>");
             return 1;
         }
         $environment = $environmentOption;
     } elseif (count($environments) === 1) {
         $environment = key($environments);
     } elseif ($environments && $input->isInteractive()) {
         $environment = $this->offerEnvironmentChoice($environments, $input, $output);
     } else {
         $environment = 'master';
     }
     /** @var \Platformsh\Cli\Helper\FilesystemHelper $fsHelper */
     $fsHelper = $this->getHelper('fs');
     // Prepare to talk to the Platform.sh repository.
     if (isset($project['repository'])) {
         $gitUrl = $project['repository']['url'];
     } else {
         $projectUriParts = explode('/', str_replace(array('http://', 'https://'), '', $project['uri']));
         $cluster = $projectUriParts[0];
         $gitUrl = "{$projectId}@git.{$cluster}:{$projectId}.git";
     }
     $repositoryDir = $directoryName . '/' . LocalProject::REPOSITORY_DIR;
     $gitHelper = new GitHelper(new ShellHelper($output));
     // First check if the repo actually exists.
     $repoHead = $gitHelper->execute(array('ls-remote', $gitUrl, 'HEAD'), false);
     if ($repoHead === false) {
         // The ls-remote command failed.
         $fsHelper->rmdir($projectRoot);
         $output->writeln('<error>Failed to connect to the Platform.sh Git server</error>');
         $output->writeln('Please check your SSH credentials or contact Platform.sh support');
         return 1;
     } elseif (is_bool($repoHead)) {
         // The repository doesn't have a HEAD, which means it is empty.
         // We need to create the folder, run git init, and attach the remote.
         mkdir($repositoryDir);
         // Initialize the repo and attach our remotes.
         $output->writeln("<info>Initializing empty project repository...</info>");
         $gitHelper->execute(array('init'), $repositoryDir, true);
         $output->writeln("<info>Adding Platform.sh remote endpoint to Git...</info>");
         $gitHelper->execute(array('remote', 'add', '-m', 'master', 'origin', $gitUrl), $repositoryDir, true);
         $output->writeln("<info>Your repository has been initialized and connected to Platform.sh!</info>");
         $output->writeln("<info>Commit and push to the {$environment} branch and Platform.sh will build your project automatically.</info>");
         return 0;
     }
     // We have a repo! Yay. Clone it.
     if (!$gitHelper->cloneRepo($gitUrl, $repositoryDir, $environment)) {
         // The clone wasn't successful. Clean up the folders we created
         // and then bow out with a message.
         $fsHelper->rmdir($projectRoot);
         $output->writeln('<error>Failed to clone Git repository</error>');
         $output->writeln('Please check your SSH credentials or contact Platform.sh support');
         return 1;
     }
     $output->writeln("The project <info>{$project['name']}</info> was successfully downloaded to: <info>{$directoryName}</info>");
     // Ensure that Drush aliases are created.
     $this->setProjectRoot($projectRoot);
     $this->updateDrushAliases($project, $environments);
     // Allow the build to be skipped.
     if ($input->getOption('no-build')) {
         return 0;
     }
     // Always skip the build if the cloned repository is empty ('.', '..',
     // '.git' being the only found items)
     if (count(scandir($repositoryDir)) <= 3) {
         return 0;
     }
     // Launch the first build.
     try {
         $builder = new LocalBuild(array('environmentId' => $environment), $output);
         $builder->buildProject($projectRoot);
     } catch (\Exception $e) {
         $output->writeln("<comment>The build failed with an error</comment>");
         $formattedMessage = $this->getHelper('formatter')->formatBlock($e->getMessage(), 'comment');
         $output->writeln($formattedMessage);
     }
     return 0;
 }
 protected function execute(InputInterface $input, OutputInterface $output)
 {
     $projectId = $input->getArgument('id');
     $environmentOption = $input->getOption('environment');
     $hostOption = $input->getOption('host');
     if (empty($projectId)) {
         if ($input->isInteractive() && ($projects = $this->getProjects(true))) {
             $projectId = $this->offerProjectChoice($projects, $input);
         } else {
             $this->stdErr->writeln("<error>You must specify a project.</error>");
             return 1;
         }
     } else {
         $result = $this->parseProjectId($projectId);
         $projectId = $result['projectId'];
         $hostOption = $hostOption ?: $result['host'];
         $environmentOption = $environmentOption ?: $result['environmentId'];
     }
     $project = $this->getProject($projectId, $hostOption, true);
     if (!$project) {
         $this->stdErr->writeln("<error>Project not found: {$projectId}</error>");
         return 1;
     }
     $environments = $this->getEnvironments($project);
     if ($environmentOption) {
         if (!isset($environments[$environmentOption])) {
             // Reload the environments list.
             $environments = $this->getEnvironments($project, true);
             if (!isset($environments[$environmentOption])) {
                 $this->stdErr->writeln("Environment not found: <error>{$environmentOption}</error>");
             }
             return 1;
         }
         $environment = $environmentOption;
     } elseif (count($environments) === 1) {
         $environment = key($environments);
     } else {
         $environment = 'master';
     }
     /** @var \Platformsh\Cli\Helper\PlatformQuestionHelper $questionHelper */
     $questionHelper = $this->getHelper('question');
     $directory = $input->getArgument('directory');
     if (empty($directory)) {
         $slugify = new Slugify();
         $directory = $project->title ? $slugify->slugify($project->title) : $project->id;
         $directory = $questionHelper->askInput('Directory', $input, $this->stdErr, $directory);
     }
     if ($projectRoot = $this->getProjectRoot()) {
         if (strpos(realpath(dirname($directory)), $projectRoot) === 0) {
             $this->stdErr->writeln("<error>A project cannot be cloned inside another project.</error>");
             return 1;
         }
     }
     /** @var \Platformsh\Cli\Helper\FilesystemHelper $fsHelper */
     $fsHelper = $this->getHelper('fs');
     // Create the directory structure.
     $existed = false;
     if (file_exists($directory)) {
         $existed = true;
         $this->stdErr->writeln("The directory <error>{$directory}</error> already exists");
         if (file_exists($directory . '/' . LocalProject::PROJECT_CONFIG) && $questionHelper->confirm("Overwrite?", $input, $this->stdErr, false)) {
             $fsHelper->remove($directory);
         } else {
             return 1;
         }
     }
     mkdir($directory);
     $projectRoot = realpath($directory);
     if (!$projectRoot) {
         throw new \Exception("Failed to create project directory: {$directory}");
     }
     if ($existed) {
         $this->stdErr->writeln("Re-created project directory: {$directory}");
     } else {
         $this->stdErr->writeln("Created new project directory: {$directory}");
     }
     $cleanUp = function () use($projectRoot, $fsHelper) {
         $fsHelper->remove($projectRoot);
     };
     $local = new LocalProject();
     $hostname = parse_url($project->getUri(), PHP_URL_HOST) ?: null;
     $local->createProjectFiles($projectRoot, $project->id, $hostname);
     // Prepare to talk to the Platform.sh repository.
     $gitUrl = $project->getGitUrl();
     $repositoryDir = $projectRoot . '/' . LocalProject::REPOSITORY_DIR;
     $gitHelper = new GitHelper(new ShellHelper($this->stdErr));
     $gitHelper->ensureInstalled();
     // First check if the repo actually exists.
     $repoHead = $gitHelper->execute(['ls-remote', $gitUrl, 'HEAD'], false);
     if ($repoHead === false) {
         // The ls-remote command failed.
         $cleanUp();
         $this->stdErr->writeln('<error>Failed to connect to the Platform.sh Git server</error>');
         // Suggest SSH key commands.
         $sshKeys = [];
         try {
             $sshKeys = $this->getClient(false)->getSshKeys();
         } catch (\Exception $e) {
             // Ignore exceptions.
         }
         if (!empty($sshKeys)) {
             $this->stdErr->writeln('');
             $this->stdErr->writeln('Please check your SSH credentials');
             $this->stdErr->writeln('You can list your keys with: <comment>platform ssh-keys</comment>');
         } else {
             $this->stdErr->writeln('You probably need to add an SSH key, with: <comment>platform ssh-key:add</comment>');
         }
         return 1;
     } elseif (is_bool($repoHead)) {
         // The repository doesn't have a HEAD, which means it is empty.
         // We need to create the folder, run git init, and attach the remote.
         mkdir($repositoryDir);
         // Initialize the repo and attach our remotes.
         $this->stdErr->writeln("Initializing empty project repository");
         $gitHelper->execute(['init'], $repositoryDir, true);
         $this->stdErr->writeln("Adding Platform.sh Git remote");
         $local->ensureGitRemote($repositoryDir, $gitUrl);
         $this->stdErr->writeln("Your repository has been initialized and connected to <info>Platform.sh</info>!");
         $this->stdErr->writeln("Commit and push to the <info>{$environment}</info> branch and Platform.sh will build your project automatically");
         return 0;
     }
     // We have a repo! Yay. Clone it.
     $cloneArgs = ['--branch', $environment, '--origin', 'platform'];
     $cloned = $gitHelper->cloneRepo($gitUrl, $repositoryDir, $cloneArgs);
     if (!$cloned) {
         // The clone wasn't successful. Clean up the folders we created
         // and then bow out with a message.
         $cleanUp();
         $this->stdErr->writeln('<error>Failed to clone Git repository</error>');
         $this->stdErr->writeln('Please check your SSH credentials or contact Platform.sh support');
         return 1;
     }
     $gitHelper->updateSubmodules(true, $repositoryDir);
     $local->ensureGitRemote($repositoryDir, $gitUrl);
     $this->setProjectRoot($projectRoot);
     $this->stdErr->writeln("\nThe project <info>{$project->title}</info> was successfully downloaded to: <info>{$directory}</info>");
     // Return early if there is no code in the repository.
     if (!glob($repositoryDir . '/*')) {
         return 0;
     }
     // Ensure that Drush aliases are created.
     if (Drupal::isDrupal($projectRoot . '/' . LocalProject::REPOSITORY_DIR)) {
         $this->stdErr->writeln('');
         $this->runOtherCommand('local:drush-aliases', ['--group' => basename($directory)]);
     }
     // Launch the first build.
     $success = true;
     if ($input->getOption('build')) {
         // Launch the first build.
         $this->stdErr->writeln('');
         $this->stdErr->writeln('Building the project locally for the first time. Run <info>platform build</info> to repeat this.');
         $options = ['environmentId' => $environment, 'noClean' => true];
         $builder = new LocalBuild($options, $output);
         $success = $builder->buildProject($projectRoot);
     } else {
         $this->stdErr->writeln("\nYou can build the project with: " . "\n    cd {$directory}" . "\n    platform build");
     }
     return $success ? 0 : 1;
 }
 protected function execute(InputInterface $input, OutputInterface $output)
 {
     $projectRoot = $this->getProjectRoot();
     /** @var \Platformsh\Cli\Helper\QuestionHelper $questionHelper */
     $questionHelper = $this->getHelper('question');
     $sourceDirOption = $input->getOption('source');
     // If no project root is found, ask the user for a source directory.
     if (!$projectRoot && !$sourceDirOption && $input->isInteractive()) {
         $default = file_exists(self::$config->get('service.app_config_file')) || is_dir('.git') ? '.' : null;
         $sourceDirOption = $questionHelper->askInput('Source directory', $default);
     }
     if ($sourceDirOption) {
         $sourceDir = realpath($sourceDirOption);
         if (!is_dir($sourceDir)) {
             throw new \InvalidArgumentException('Source directory not found: ' . $sourceDirOption);
         } elseif (file_exists($sourceDir . self::$config->get('local.project_config'))) {
             $projectRoot = $sourceDir;
             $sourceDir = $projectRoot;
         }
     } elseif (!$projectRoot) {
         throw new RootNotFoundException('Project root not found. Specify --source or go to a project directory.');
     } else {
         $sourceDir = $projectRoot;
     }
     $destination = $input->getOption('destination');
     // If no project root is found, ask the user for a destination path.
     if (!$projectRoot && !$destination && $input->isInteractive()) {
         $default = is_dir($sourceDir . '/.git') && $sourceDir === getcwd() ? self::$config->get('local.web_root') : null;
         $destination = $questionHelper->askInput('Build destination', $default);
     }
     if ($destination) {
         /** @var \Platformsh\Cli\Helper\FilesystemHelper $fsHelper */
         $fsHelper = $this->getHelper('fs');
         $destination = $fsHelper->makePathAbsolute($destination);
     } elseif (!$projectRoot) {
         throw new RootNotFoundException('Project root not found. Specify --destination or go to a project directory.');
     } else {
         $destination = $projectRoot . '/' . self::$config->get('local.web_root');
     }
     // Ensure no conflicts between source and destination.
     if (strpos($sourceDir, $destination) === 0) {
         throw new \InvalidArgumentException("The destination '{$destination}' conflicts with the source '{$sourceDir}'");
     }
     // Ask the user about overwriting the destination, if a project root was
     // not found.
     if (!$projectRoot && file_exists($destination)) {
         if (!is_writable($destination)) {
             $this->stdErr->writeln("The destination exists and is not writable: <error>{$destination}</error>");
             return 1;
         }
         $default = is_link($destination);
         if (!$questionHelper->confirm("The destination exists: <comment>{$destination}</comment>. Overwrite?", $default)) {
             return 1;
         }
     }
     // Map input options to build settings.
     $settings = [];
     foreach ($input->getOptions() as $name => $value) {
         $settings[$name] = $value;
     }
     $apps = $input->getArgument('app');
     $builder = new LocalBuild($settings, self::$config, $this->stdErr);
     $success = $builder->build($sourceDir, $destination, $apps);
     return $success ? 0 : 1;
 }
 protected function execute(InputInterface $input, OutputInterface $output)
 {
     $this->envArgName = 'parent';
     $this->validateInput($input, true);
     $selectedProject = $this->getSelectedProject();
     $branchName = $input->getArgument('name');
     if (empty($branchName)) {
         if ($input->isInteractive()) {
             // List environments.
             return $this->runOtherCommand('environments', array('--project' => $selectedProject->id));
         }
         $this->stdErr->writeln("<error>You must specify the name of the new branch.</error>");
         return 1;
     }
     $machineName = Environment::sanitizeId($branchName);
     $environmentId = $this->getSelectedEnvironment()['id'];
     if ($machineName == $environmentId) {
         $this->stdErr->writeln("<comment>Already on {$machineName}</comment>");
         return 1;
     }
     if ($environment = $this->getEnvironment($machineName, $selectedProject)) {
         $checkout = $this->getHelper('question')->confirm("The environment <comment>{$machineName}</comment> already exists. Check out?", $input, $this->stdErr);
         if ($checkout) {
             return $this->runOtherCommand('environment:checkout', array('id' => $environment->id));
         }
         return 1;
     }
     if (!$this->getSelectedEnvironment()->operationAvailable('branch')) {
         $this->stdErr->writeln("Operation not available: The environment <error>{$environmentId}</error> can't be branched.");
         return 1;
     }
     $force = $input->getOption('force');
     $projectRoot = $this->getProjectRoot();
     if (!$projectRoot && $force) {
         $this->stdErr->writeln("<comment>This command was run from outside your local project root, the new Platform.sh branch cannot be checked out in your local Git repository." . " Make sure to run 'platform checkout' or 'git checkout' in your repository directory to switch to the branch you are expecting.</comment>");
         $local_error = true;
     } elseif (!$projectRoot) {
         $this->stdErr->writeln("<error>You must run this command inside the project root, or specify --force.</error>");
         return 1;
     }
     $selectedEnvironment = $this->getSelectedEnvironment();
     $this->stdErr->writeln("Creating a new environment <info>{$branchName}</info>, branched from <info>{$selectedEnvironment['title']}</info>");
     $activity = $selectedEnvironment->branch($branchName, $machineName);
     // Clear the environments cache, as branching has started.
     $this->clearEnvironmentsCache($selectedProject);
     if ($projectRoot) {
         $gitHelper = new GitHelper(new ShellHelper($this->stdErr));
         $gitHelper->setDefaultRepositoryDir($projectRoot . '/' . LocalProject::REPOSITORY_DIR);
         // If the Git branch already exists locally, just check it out.
         $existsLocally = $gitHelper->branchExists($machineName);
         if ($existsLocally) {
             $this->stdErr->writeln("Checking out <info>{$machineName}</info> locally");
             if (!$gitHelper->checkOut($machineName)) {
                 $this->stdErr->writeln('<error>Failed to check out branch locally: ' . $machineName . '</error>');
                 $local_error = true;
                 if (!$force) {
                     return 1;
                 }
             }
         } else {
             // Create a new branch, using the current or specified environment as the parent if it exists locally.
             $parent = $this->getSelectedEnvironment()['id'];
             if (!$gitHelper->branchExists($parent)) {
                 $parent = null;
             }
             $this->stdErr->writeln("Creating local branch <info>{$machineName}</info>");
             if (!$gitHelper->checkOutNew($machineName, $parent)) {
                 $this->stdErr->writeln('<error>Failed to create branch locally: ' . $machineName . '</error>');
                 $local_error = true;
                 if (!$force) {
                     return 1;
                 }
             }
         }
     }
     $remoteSuccess = true;
     if (!$input->getOption('no-wait')) {
         $remoteSuccess = ActivityUtil::waitAndLog($activity, $this->stdErr, "The environment <info>{$branchName}</info> has been branched.", '<error>Branching failed</error>');
         // Clear the environments cache again.
         $this->clearEnvironmentsCache($selectedProject);
     }
     $build = $input->getOption('build');
     if (empty($local_error) && $build && $projectRoot) {
         // Build the new branch.
         try {
             $buildSettings = array('environmentId' => $machineName, 'verbosity' => $output->getVerbosity());
             $builder = new LocalBuild($buildSettings, $output);
             $builder->buildProject($projectRoot);
         } catch (\Exception $e) {
             $this->stdErr->writeln("<comment>The new branch could not be built: \n" . $e->getMessage() . "</comment>");
             return 1;
         }
     }
     $this->clearEnvironmentsCache();
     return $remoteSuccess ? 0 : 1;
 }
 protected function execute(InputInterface $input, OutputInterface $output)
 {
     $projectId = $input->getArgument('id');
     $environmentOption = $input->getOption('environment');
     $hostOption = $input->getOption('host');
     if (empty($projectId)) {
         if ($input->isInteractive() && ($projects = $this->api()->getProjects(true))) {
             $projectId = $this->offerProjectChoice($projects, $input);
         } else {
             $this->stdErr->writeln("<error>You must specify a project.</error>");
             return 1;
         }
     } else {
         $result = $this->parseProjectId($projectId);
         $projectId = $result['projectId'];
         $hostOption = $hostOption ?: $result['host'];
         $environmentOption = $environmentOption ?: $result['environmentId'];
     }
     $project = $this->api()->getProject($projectId, $hostOption, true);
     if (!$project) {
         $this->stdErr->writeln("<error>Project not found: {$projectId}</error>");
         return 1;
     }
     $environments = $this->api()->getEnvironments($project);
     if ($environmentOption) {
         if (!isset($environments[$environmentOption])) {
             // Reload the environments list.
             $environments = $this->api()->getEnvironments($project, true);
             if (!isset($environments[$environmentOption])) {
                 $this->stdErr->writeln("Environment not found: <error>{$environmentOption}</error>");
             }
             return 1;
         }
         $environmentId = $environmentOption;
     } elseif (count($environments) === 1) {
         $environmentId = key($environments);
     } else {
         $environmentId = 'master';
     }
     $directory = $input->getArgument('directory');
     if (empty($directory)) {
         $slugify = new Slugify();
         $directory = $project->title ? $slugify->slugify($project->title) : $project->id;
         /** @var \Platformsh\Cli\Helper\QuestionHelper $questionHelper */
         $questionHelper = $this->getHelper('question');
         $directory = $questionHelper->askInput('Directory', $directory);
     }
     if ($projectRoot = $this->getProjectRoot()) {
         if (strpos(realpath(dirname($directory)), $projectRoot) === 0) {
             $this->stdErr->writeln("<error>A project cannot be cloned inside another project.</error>");
             return 1;
         }
     }
     // Create the directory structure.
     if (file_exists($directory)) {
         $this->stdErr->writeln("The directory <error>{$directory}</error> already exists");
         return 1;
     }
     if (!($parent = realpath(dirname($directory)))) {
         throw new \Exception("Not a directory: " . dirname($directory));
     }
     $projectRoot = $parent . '/' . basename($directory);
     // Prepare to talk to the remote repository.
     $gitUrl = $project->getGitUrl();
     $gitHelper = new GitHelper(new ShellHelper($this->stdErr));
     $gitHelper->ensureInstalled();
     // First check if the repo actually exists.
     try {
         $exists = $gitHelper->remoteRepoExists($gitUrl);
     } catch (\Exception $e) {
         // The ls-remote command failed.
         $this->stdErr->writeln('<error>Failed to connect to the ' . self::$config->get('service.name') . ' Git server</error>');
         // Suggest SSH key commands.
         $sshKeys = [];
         try {
             $sshKeys = $this->api()->getClient(false)->getSshKeys();
         } catch (\Exception $e) {
             // Ignore exceptions.
         }
         if (!empty($sshKeys)) {
             $this->stdErr->writeln('');
             $this->stdErr->writeln('Please check your SSH credentials');
             $this->stdErr->writeln('You can list your keys with: <comment>' . self::$config->get('application.executable') . ' ssh-keys</comment>');
         } else {
             $this->stdErr->writeln('You probably need to add an SSH key, with: <comment>' . self::$config->get('application.executable') . ' ssh-key:add</comment>');
         }
         return 1;
     }
     $projectConfig = ['id' => $projectId];
     $host = parse_url($project->getUri(), PHP_URL_HOST);
     if ($host) {
         $projectConfig['host'] = $host;
     }
     // If the remote repository exists, then locally we need to create the
     // folder, run git init, and attach the remote.
     if (!$exists) {
         $this->stdErr->writeln('Creating project directory: <info>' . $directory . '</info>');
         if (mkdir($projectRoot) === false) {
             $this->stdErr->writeln('Failed to create the project directory.');
             return 1;
         }
         // Initialize the repo and attach our remotes.
         $this->debug('Initializing the repository');
         $gitHelper->init($projectRoot, true);
         // As soon as there is a Git repo present, add the project config file.
         $this->localProject->writeCurrentProjectConfig($projectConfig, $projectRoot);
         $this->debug('Adding Git remote(s)');
         $this->localProject->ensureGitRemote($projectRoot, $gitUrl);
         $this->stdErr->writeln('');
         $this->stdErr->writeln('Your project has been initialized and connected to <info>' . self::$config->get('service.name') . '</info>!');
         $this->stdErr->writeln('');
         $this->stdErr->writeln('Commit and push to the <info>master</info> branch of the <info>' . self::$config->get('detection.git_remote_name') . '</info> Git remote, and ' . self::$config->get('service.name') . ' will build your project automatically.');
         return 0;
     }
     // We have a repo! Yay. Clone it.
     $projectLabel = $this->api()->getProjectLabel($project);
     $this->stdErr->writeln('Downloading project ' . $projectLabel);
     $cloneArgs = ['--branch', $environmentId, '--origin', self::$config->get('detection.git_remote_name')];
     if ($output->isDecorated()) {
         $cloneArgs[] = '--progress';
     }
     $cloned = $gitHelper->cloneRepo($gitUrl, $projectRoot, $cloneArgs);
     if ($cloned === false) {
         // The clone wasn't successful. Clean up the folders we created
         // and then bow out with a message.
         $this->stdErr->writeln('<error>Failed to clone Git repository</error>');
         $this->stdErr->writeln('Please check your SSH credentials or contact ' . self::$config->get('service.name') . ' support');
         return 1;
     }
     $this->setProjectRoot($projectRoot);
     $this->localProject->writeCurrentProjectConfig($projectConfig, $projectRoot);
     $this->localProject->ensureGitRemote($projectRoot, $gitUrl);
     $gitHelper->updateSubmodules(true, $projectRoot);
     $this->stdErr->writeln("\nThe project <info>{$projectLabel}</info> was successfully downloaded to: <info>{$directory}</info>");
     // Return early if there is no code in the repository.
     if (!glob($projectRoot . '/*', GLOB_NOSORT)) {
         return 0;
     }
     // Ensure that Drush aliases are created.
     if (Drupal::isDrupal($projectRoot)) {
         $this->stdErr->writeln('');
         $this->runOtherCommand('local:drush-aliases', ['--group' => basename($directory)]);
     }
     // Launch the first build.
     $success = true;
     if ($input->getOption('build')) {
         // Launch the first build.
         $this->stdErr->writeln('');
         $this->stdErr->writeln('Building the project locally for the first time. Run <info>' . self::$config->get('application.executable') . ' build</info> to repeat this.');
         $options = ['no-clean' => true];
         $builder = new LocalBuild($options, self::$config, $output);
         $success = $builder->build($projectRoot);
     } else {
         $this->stdErr->writeln("\nYou can build the project with: " . "\n    cd {$directory}" . "\n    " . self::$config->get('application.executable') . " build");
     }
     return $success ? 0 : 1;
 }
 protected function execute(InputInterface $input, OutputInterface $output)
 {
     $projectRoot = $this->getProjectRoot();
     /** @var \Platformsh\Cli\Helper\PlatformQuestionHelper $questionHelper */
     $questionHelper = $this->getHelper('question');
     $sourceDirOption = $input->getOption('source');
     // If no project root is found, ask the user for a source directory.
     if (!$projectRoot && !$sourceDirOption && $input->isInteractive()) {
         $sourceDirOption = $questionHelper->askInput('Source directory', $input, $this->stdErr);
     }
     if ($sourceDirOption) {
         $sourceDir = realpath($sourceDirOption);
         if (!is_dir($sourceDir)) {
             throw new \InvalidArgumentException('Source directory not found: ' . $sourceDirOption);
         } elseif (file_exists($sourceDir . '/.platform-project')) {
             $projectRoot = $sourceDir;
             $sourceDir = $projectRoot . '/' . LocalProject::REPOSITORY_DIR;
         }
     } elseif (!$projectRoot) {
         throw new RootNotFoundException('Project root not found. Specify --source or go to a project directory.');
     } else {
         $sourceDir = $projectRoot . '/' . LocalProject::REPOSITORY_DIR;
     }
     $destination = $input->getOption('destination');
     // If no project root is found, ask the user for a destination path.
     if (!$projectRoot && !$destination && $input->isInteractive()) {
         $destination = $questionHelper->askInput('Build destination', $input, $this->stdErr);
     }
     if ($destination) {
         /** @var \Platformsh\Cli\Helper\FilesystemHelper $fsHelper */
         $fsHelper = $this->getHelper('fs');
         $destination = $fsHelper->makePathAbsolute($destination);
     } elseif (!$projectRoot) {
         throw new RootNotFoundException('Project root not found. Specify --destination or go to a project directory.');
     } else {
         $destination = $projectRoot . '/' . LocalProject::WEB_ROOT;
     }
     // Ensure no conflicts between source and destination.
     if (strpos($sourceDir, $destination) === 0) {
         throw new \InvalidArgumentException("The destination '{$destination}' conflicts with the source '{$sourceDir}'");
     }
     // Ask the user about overwriting the destination, if a project root was
     // not found.
     if (!$projectRoot && file_exists($destination)) {
         if (!is_writable($destination)) {
             $this->stdErr->writeln("The destination exists and is not writable: <error>{$destination}</error>");
             return 1;
         }
         $default = is_link($destination);
         if (!$questionHelper->confirm("The destination exists: <comment>{$destination}</comment>. Overwrite?", $input, $this->stdErr, $default)) {
             return 1;
         }
     }
     $settings = [];
     $settings['projectRoot'] = $projectRoot;
     $settings['environmentId'] = $this->determineEnvironmentId($sourceDir, $projectRoot);
     $settings['drushConcurrency'] = $input->hasOption('concurrency') ? $input->getOption('concurrency') : $this->defaultDrushConcurrency;
     // Some simple settings flags.
     $settingsMap = ['absoluteLinks' => 'abslinks', 'copy' => 'copy', 'drushWorkingCopy' => 'working-copy', 'drushUpdateLock' => 'lock', 'noArchive' => 'no-archive', 'noCache' => 'no-cache', 'noClean' => 'no-clean', 'noBuildHooks' => 'no-build-hooks'];
     foreach ($settingsMap as $setting => $option) {
         $settings[$setting] = $input->hasOption($option) && $input->getOption($option);
     }
     $apps = $input->getArgument('app');
     $builder = new LocalBuild($settings, $this->stdErr);
     $success = $builder->build($sourceDir, $destination, $apps);
     return $success ? 0 : 1;
 }
 public function testGetTreeId()
 {
     $builder = new LocalBuild();
     $treeId = $builder->getTreeId('tests/data/apps/composer');
     $this->assertEquals('944cb5782066b6bd501677a35a6399b6b7a7c573', $treeId);
 }
 protected function execute(InputInterface $input, OutputInterface $output)
 {
     $projectId = $input->getArgument('id');
     if (empty($projectId)) {
         if ($input->isInteractive() && ($projects = $this->getProjects(true))) {
             $projectId = $this->offerProjectChoice($projects, $input);
         } else {
             $this->stdErr->writeln("<error>You must specify a project.</error>");
             return 1;
         }
     }
     $project = $this->getProject($projectId, $input->getOption('host'));
     if (!$project) {
         $this->stdErr->writeln("<error>Project not found: {$projectId}</error>");
         return 1;
     }
     /** @var \Platformsh\Cli\Helper\PlatformQuestionHelper $questionHelper */
     $questionHelper = $this->getHelper('question');
     $directoryName = $input->getArgument('directory-name');
     if (empty($directoryName)) {
         $slugify = new Slugify();
         $directoryName = $project->title ? $slugify->slugify($project->title) : $project->id;
         $directoryName = $questionHelper->askInput('Directory name', $input, $this->stdErr, $directoryName);
     }
     if ($projectRoot = $this->getProjectRoot()) {
         if (strpos(realpath(dirname($directoryName)), $projectRoot) === 0) {
             $this->stdErr->writeln("<error>A project cannot be cloned inside another project.</error>");
             return 1;
         }
     }
     /** @var \Platformsh\Cli\Helper\FilesystemHelper $fsHelper */
     $fsHelper = $this->getHelper('fs');
     // Create the directory structure.
     $existed = false;
     if (file_exists($directoryName)) {
         $existed = true;
         $this->stdErr->writeln("The directory <error>{$directoryName}</error> already exists");
         if ($questionHelper->confirm("Overwrite?", $input, $this->stdErr, false)) {
             $fsHelper->remove($directoryName);
         } else {
             return 1;
         }
     }
     mkdir($directoryName);
     $projectRoot = realpath($directoryName);
     if (!$projectRoot) {
         throw new \Exception("Failed to create project directory: {$directoryName}");
     }
     if ($existed) {
         $this->stdErr->writeln("Re-created project directory: <info>{$directoryName}</info>");
     } else {
         $this->stdErr->writeln("Created new project directory: <info>{$directoryName}</info>");
     }
     $local = new LocalProject();
     $hostname = parse_url($project->getUri(), PHP_URL_HOST) ?: null;
     $local->createProjectFiles($projectRoot, $project->id, $hostname);
     $environments = $this->getEnvironments($project, true);
     $environmentOption = $input->getOption('environment');
     if ($environmentOption) {
         if (!isset($environments[$environmentOption])) {
             $this->stdErr->writeln("Environment not found: <error>{$environmentOption}</error>");
             return 1;
         }
         $environment = $environmentOption;
     } elseif (count($environments) === 1) {
         $environment = key($environments);
     } elseif ($environments && $input->isInteractive()) {
         $environment = $this->offerEnvironmentChoice($environments, $input);
     } else {
         $environment = 'master';
     }
     // Prepare to talk to the Platform.sh repository.
     $gitUrl = $project->getGitUrl();
     $repositoryDir = $directoryName . '/' . LocalProject::REPOSITORY_DIR;
     $gitHelper = new GitHelper(new ShellHelper($this->stdErr));
     $gitHelper->ensureInstalled();
     // First check if the repo actually exists.
     $repoHead = $gitHelper->execute(array('ls-remote', $gitUrl, 'HEAD'), false);
     if ($repoHead === false) {
         // The ls-remote command failed.
         $fsHelper->rmdir($projectRoot);
         $this->stdErr->writeln('<error>Failed to connect to the Platform.sh Git server</error>');
         $this->stdErr->writeln('Please check your SSH credentials or contact Platform.sh support');
         return 1;
     } elseif (is_bool($repoHead)) {
         // The repository doesn't have a HEAD, which means it is empty.
         // We need to create the folder, run git init, and attach the remote.
         mkdir($repositoryDir);
         // Initialize the repo and attach our remotes.
         $this->stdErr->writeln("Initializing empty project repository");
         $gitHelper->execute(array('init'), $repositoryDir, true);
         $this->stdErr->writeln("Adding Platform.sh Git remote");
         $local->ensureGitRemote($repositoryDir, $gitUrl);
         $this->stdErr->writeln("Your repository has been initialized and connected to <info>Platform.sh</info>!");
         $this->stdErr->writeln("Commit and push to the <info>{$environment}</info> branch and Platform.sh will build your project automatically");
         return 0;
     }
     // We have a repo! Yay. Clone it.
     $cloneArgs = array('--branch', $environment, '--origin', 'platform');
     $cloned = $gitHelper->cloneRepo($gitUrl, $repositoryDir, $cloneArgs);
     if (!$cloned) {
         // The clone wasn't successful. Clean up the folders we created
         // and then bow out with a message.
         $fsHelper->rmdir($projectRoot);
         $this->stdErr->writeln('<error>Failed to clone Git repository</error>');
         $this->stdErr->writeln('Please check your SSH credentials or contact Platform.sh support');
         return 1;
     }
     $local->ensureGitRemote($repositoryDir, $gitUrl);
     $this->setProjectRoot($projectRoot);
     $this->stdErr->writeln('');
     $this->stdErr->writeln("The project <info>{$project->title}</info> was successfully downloaded to: <info>{$directoryName}</info>");
     // Ensure that Drush aliases are created.
     if (Drupal::isDrupal($projectRoot . '/' . LocalProject::REPOSITORY_DIR)) {
         $this->stdErr->writeln('');
         $this->runOtherCommand('local:drush-aliases', array('--group' => $directoryName), $input);
     }
     // Allow the build to be skipped.
     if ($input->getOption('no-build')) {
         return 0;
     }
     // Always skip the build if the cloned repository is empty ('.', '..',
     // '.git' being the only found items)
     if (count(scandir($repositoryDir)) <= 3) {
         return 0;
     }
     // Launch the first build.
     $this->stdErr->writeln('');
     $this->stdErr->writeln('Building the project locally for the first time. Run <info>platform build</info> to repeat this.');
     $builder = new LocalBuild(array('environmentId' => $environment), $output);
     $success = $builder->buildProject($projectRoot);
     return $success ? 0 : 1;
 }