/** * @param string $dir * * @throws \RuntimeException * If the directory is not a clone of a Platform.sh Git repository. * * @return string|false * The project ID, or false if it cannot be determined. */ protected function getProjectIdFromGit($dir) { if (!file_exists("{$dir}/.git")) { throw new \RuntimeException('The directory is not a Git repository'); } $gitHelper = new GitHelper(); $gitHelper->ensureInstalled(); $originUrl = $gitHelper->getConfig("remote.origin.url", $dir); if (!$originUrl) { throw new \RuntimeException("Git remote 'origin' not found"); } if (!preg_match('/^([a-z][a-z0-9]{12})@git\\.[a-z\\-]+\\.platform\\.sh:\\1\\.git$/', $originUrl, $matches)) { throw new \RuntimeException("The Git remote 'origin' is not a Platform.sh URL"); } return $matches[1]; }
/** * Get a hash of the application files. * * This should change if any of the application files or build settings * change. * * @param string $appRoot * * @return string|false */ public function getTreeId($appRoot) { $hashes = []; // Get a hash representing all the files in the application, excluding // the project config folder (CLI_PROJECT_CONFIG_DIR). $tree = $this->gitHelper->execute(['ls-files', '-s'], $appRoot); if ($tree === false) { return false; } $tree = preg_replace('#^|\\n[^\\n]+?' . preg_quote($this->config->get('service.project_config_dir')) . '\\n|$#', "\n", $tree); $hashes[] = sha1($tree); // Include the hashes of untracked and modified files. $others = $this->gitHelper->execute(['ls-files', '--modified', '--others', '--exclude-standard', '-x ' . $this->config->get('service.project_config_dir'), '.'], $appRoot); if ($others === false) { return false; } $count = 0; foreach (explode("\n", $others) as $filename) { if ($count > 5000) { return false; } $filename = "{$appRoot}/{$filename}"; if (is_file($filename)) { $hashes[] = sha1_file($filename); $count++; } } // Include relevant build settings. $relevant = ['abslinks', 'copy', 'clone', 'no-cache', 'working-copy', 'lock']; $settings = array_intersect_key($this->settings, array_flip($relevant)); $hashes[] = serialize($settings); $hashes[] = self::BUILD_VERSION; // Combine them all. return sha1(implode(' ', $hashes)); }
/** * Test GitHelper::getConfig(). */ public function testGetConfig() { $email = '*****@*****.**'; $repository = $this->getRepositoryDir(); chdir($repository); shell_exec('git config user.email ' . $email); $config = $this->gitHelper->getConfig('user.email', $repository); $this->assertEquals($email, $config); }
/** * Check whether a branch exists, locally in Git or on the remote. * * @param string $branch * @param Project $project * @param GitHelper $gitHelper * * @return string|false */ protected function branchExists($branch, Project $project, GitHelper $gitHelper) { // Check if the Git branch exists locally. $candidates = array_unique(array(Environment::sanitizeId($branch), $branch)); foreach ($candidates as $candidate) { if ($gitHelper->branchExists($candidate)) { return $candidate; } } // Check if the environment exists by title or ID. This is usually faster // than running 'git ls-remote'. $environments = $this->getEnvironments($project); foreach ($environments as $environment) { if ($environment['title'] == $branch || $environment['id'] == $branch) { return $environment['id']; } } return false; }
/** * Create a default .gitignore file for the app. * * @param string $source The path to a default .gitignore file, relative to * the 'resources' directory. */ protected function copyGitIgnore($source) { $source = CLI_ROOT . '/resources/' . $source; $gitRoot = $this->gitHelper->getRoot($this->appRoot); if (!$gitRoot) { return; } $appGitIgnore = $this->appRoot . '/.gitignore'; if (!file_exists($appGitIgnore) && !file_exists($gitRoot . '/.gitignore')) { $this->output->writeln("Creating a .gitignore file"); copy($source, $appGitIgnore); } }
/** * Ensure there are appropriate Git remotes in the repository. * * @param string $dir * @param string $url */ public function ensureGitRemote($dir, $url) { if (!file_exists($dir . '/.git')) { throw new \InvalidArgumentException('The directory is not a Git repository'); } $gitHelper = new GitHelper(); $gitHelper->ensureInstalled(); $gitHelper->setDefaultRepositoryDir($dir); $currentUrl = $gitHelper->getConfig("remote." . $this->config->get('detection.git_remote_name') . ".url", $dir); if (!$currentUrl) { $gitHelper->execute(['remote', 'add', $this->config->get('detection.git_remote_name'), $url], $dir, true); } elseif ($currentUrl != $url) { $gitHelper->execute(['remote', 'set-url', $this->config->get('detection.git_remote_name'), $url], $dir, true); } // Add an origin remote too. if ($this->config->get('detection.git_remote_name') !== 'origin' && !$gitHelper->getConfig("remote.origin.url", $dir)) { $gitHelper->execute(['remote', 'add', 'origin', $url]); } }
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) { $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; }
/** * Test GitHelper::getConfig(). */ public function testGetConfig() { $config = $this->gitHelper->getConfig('user.email'); $this->assertEquals('*****@*****.**', $config); }
protected function execute(InputInterface $input, OutputInterface $output) { $project = $this->getCurrentProject(); if (!$project) { throw new RootNotFoundException(); } $projectRoot = $this->getProjectRoot(); $repositoryDir = $projectRoot . '/' . LocalProject::REPOSITORY_DIR; $gitHelper = new GitHelper(new ShellHelper($output)); $gitHelper->setDefaultRepositoryDir($repositoryDir); $specifiedEnvironmentId = $input->getArgument('environment'); if ($specifiedEnvironmentId != '0' && !($specifiedEnvironment = $this->getEnvironment($specifiedEnvironmentId, $project))) { $this->stdErr->writeln("Environment not found: <error>{$specifiedEnvironmentId}</error>"); return 1; } $specifiedBranch = $input->getArgument('branch'); if ($specifiedBranch) { if (!$gitHelper->branchExists($specifiedBranch)) { $this->stdErr->writeln("Branch not found: <error>{$specifiedBranch}</error>"); return 1; } } else { $specifiedBranch = $gitHelper->getCurrentBranch(); } // Check whether the branch is mapped by default (its name or its Git // upstream is the same as the remote environment ID). $mappedByDefault = isset($specifiedEnvironment) && $specifiedEnvironment->status != 'inactive' && $specifiedEnvironmentId === $specifiedBranch; if ($specifiedEnvironmentId != '0' && !$mappedByDefault) { $upstream = $gitHelper->getUpstream($specifiedBranch); if (strpos($upstream, '/')) { list(, $upstream) = explode('/', $upstream, 2); } if ($upstream === $specifiedEnvironmentId) { $mappedByDefault = true; } if (!$mappedByDefault && $gitHelper->branchExists($specifiedEnvironmentId)) { $this->stdErr->writeln("A local branch already exists named <comment>{$specifiedEnvironmentId}</comment>"); } } // Perform the mapping or unmapping. $config = $this->getProjectConfig($projectRoot); $config += ['mapping' => []]; if ($mappedByDefault || $specifiedEnvironmentId == '0') { unset($config['mapping'][$specifiedBranch]); $this->setProjectConfig('mapping', $config['mapping'], $projectRoot); } else { if (isset($config['mapping']) && ($current = array_search($specifiedEnvironmentId, $config['mapping'])) !== false) { unset($config['mapping'][$current]); } $config['mapping'][$specifiedBranch] = $specifiedEnvironmentId; $this->setProjectConfig('mapping', $config['mapping'], $projectRoot); } // Check the success of the operation. if (isset($config['mapping'][$specifiedBranch])) { $actualRemoteEnvironment = $config['mapping'][$specifiedBranch]; $this->stdErr->writeln("The local branch <info>{$specifiedBranch}</info> is mapped to the remote environment <info>{$actualRemoteEnvironment}</info>"); } elseif ($mappedByDefault) { $actualRemoteEnvironment = $specifiedBranch; $this->stdErr->writeln("The local branch <info>{$specifiedBranch}</info> is mapped to the default remote environment, <info>{$specifiedBranch}</info>"); } else { $this->stdErr->writeln("The local branch <info>{$specifiedBranch}</info> is not mapped to a remote environment"); } $success = !empty($actualRemoteEnvironment) ? $actualRemoteEnvironment == $specifiedEnvironmentId : $specifiedEnvironmentId == '0'; return $success ? 0 : 1; }
/** * Ensure there are appropriate Git remotes in the repository. * * @param string $dir * @param string $url */ public function ensureGitRemote($dir, $url) { if (!file_exists("{$dir}/.git")) { throw new \InvalidArgumentException('The directory is not a Git repository'); } $gitHelper = new GitHelper(); $gitHelper->ensureInstalled(); $gitHelper->setDefaultRepositoryDir($dir); $platformUrl = $gitHelper->getConfig("remote.platform.url", $dir); if (!$platformUrl) { $gitHelper->execute(array('remote', 'add', 'platform', $url), $dir, true); } elseif ($platformUrl != $url) { $gitHelper->execute(array('remote', 'set-url', 'platform', $url), $dir, true); } // Add an origin remote too. if (!$gitHelper->getConfig("remote.origin.url", $dir)) { $gitHelper->execute(array('remote', 'add', 'origin', $url)); } }
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; }