/**
  * Test FilesystemHelper::symlinkDir().
  */
 public function testSymlinkDir()
 {
     $testTarget = $this->tempDir();
     $testLink = $this->tempDir() . '/link';
     $this->filesystemHelper->symlink($testTarget, $testLink);
     $this->assertTrue(is_link($testLink));
     touch($testTarget . '/test-file');
     $this->assertFileExists($testLink . '/test-file');
 }
 /**
  * Process the defined special destinations.
  */
 protected function symLinkSpecialDestinations()
 {
     foreach ($this->specialDestinations as $sourcePattern => $relDestination) {
         $matched = glob($this->appRoot . '/' . $sourcePattern, GLOB_NOSORT);
         if (!$matched) {
             continue;
         }
         // On Platform these replacements would be a bit different.
         $absDestination = str_replace(array('{webroot}', '{approot}'), $this->buildDir, $relDestination);
         foreach ($matched as $source) {
             // Ignore the source if it's in ignoredFiles.
             $relSource = str_replace($this->appRoot . '/', '', $source);
             if (in_array($relSource, $this->ignoredFiles)) {
                 continue;
             }
             $this->output->writeln("Symlinking {$relSource} to {$relDestination}");
             $destination = $absDestination;
             // Do not overwrite directories with files.
             if (!is_dir($source) && is_dir($destination)) {
                 $destination = $destination . '/' . basename($source);
             }
             // Delete existing files, emitting a warning.
             if (file_exists($destination)) {
                 $this->output->writeln(sprintf("Overriding existing path '%s' in destination", str_replace($this->buildDir . '/', '', $destination)));
                 $this->fsHelper->remove($destination);
             }
             $this->fsHelper->symlink($source, $destination);
         }
     }
 }
 /**
  * Create a settings.local.php for a Drupal site.
  *
  * This helps with database setup, etc.
  */
 protected function installDrupalSettingsLocal()
 {
     $sitesDefault = $this->getWebRoot() . '/sites/default';
     $shared = $this->getSharedDir();
     $settingsLocal = $sitesDefault . '/settings.local.php';
     if ($shared !== false && is_dir($sitesDefault) && !file_exists($settingsLocal)) {
         $sharedSettingsLocal = $shared . '/settings.local.php';
         $relative = $this->config->get('local.shared_dir') . '/settings.local.php';
         if (!file_exists($sharedSettingsLocal)) {
             $this->output->writeln("Creating file: <info>{$relative}</info>");
             $this->fsHelper->copy(CLI_ROOT . '/resources/drupal/settings.local.php.dist', $sharedSettingsLocal);
             $this->output->writeln('Edit this file to add your database credentials and other Drupal configuration.');
         } else {
             $this->output->writeln("Symlinking <info>{$relative}</info> into sites/default");
         }
         $this->fsHelper->symlink($sharedSettingsLocal, $settingsLocal);
     }
 }
 /**
  * @param LocalApplication $app
  * @param string           $sourceDir
  * @param string           $destination
  *
  * @return bool
  */
 protected function buildApp($app, $sourceDir, $destination = null)
 {
     $verbose = $this->output->isVerbose();
     $destination = $destination ?: $sourceDir . '/' . $this->config->get('local.web_root');
     $appRoot = $app->getRoot();
     $appConfig = $app->getConfig();
     $multiApp = $appRoot != $sourceDir;
     $appId = $app->getId();
     $toolstack = $app->getToolstack();
     if (!$toolstack) {
         $this->output->writeln("Toolstack not found for application <error>{$appId}</error>");
         return false;
     }
     // Find the right build directory.
     $buildName = $multiApp ? str_replace('/', '-', $appId) : 'default';
     $tmpBuildDir = $sourceDir . '/' . $this->config->get('local.build_dir') . '/' . $buildName . '-tmp';
     if (file_exists($tmpBuildDir)) {
         if (!$this->fsHelper->remove($tmpBuildDir)) {
             $this->output->writeln(sprintf('Failed to remove directory <error>%s</error>', $tmpBuildDir));
             return false;
         }
     }
     // If the destination is inside the source directory, ensure it isn't
     // copied or symlinked into the build.
     if (strpos($destination, $sourceDir) === 0) {
         $toolstack->addIgnoredFiles([ltrim(substr($destination, strlen($sourceDir)), '/')]);
     }
     // Warn about a mismatched PHP version.
     if (isset($appConfig['type']) && strpos($appConfig['type'], ':')) {
         list($stack, $version) = explode(':', $appConfig['type'], 2);
         if ($stack === 'php' && version_compare($version, PHP_VERSION, '>')) {
             $this->output->writeln(sprintf('<comment>Warning:</comment> the application <comment>%s</comment> expects PHP %s, but the system version is %s.', $appId, $version, PHP_VERSION));
         }
     }
     $toolstack->setOutput($this->output);
     $buildSettings = $this->settings + ['multiApp' => $multiApp, 'sourceDir' => $sourceDir];
     $toolstack->prepare($tmpBuildDir, $app, $this->config, $buildSettings);
     $archive = false;
     if (empty($this->settings['no-archive']) && empty($this->settings['no-cache'])) {
         $treeId = $this->getTreeId($appRoot);
         if ($treeId) {
             if ($verbose) {
                 $this->output->writeln("Tree ID: {$treeId}");
             }
             $archive = $sourceDir . '/' . $this->config->get('local.archive_dir') . '/' . $treeId . '.tar.gz';
         }
     }
     if ($archive && file_exists($archive)) {
         $message = "Extracting archive for application <info>{$appId}</info>";
         $this->output->writeln($message);
         $this->fsHelper->extractArchive($archive, $tmpBuildDir);
     } else {
         $message = "Building application <info>{$appId}</info>";
         if (isset($appConfig['type'])) {
             $message .= ' (runtime type: ' . $appConfig['type'] . ')';
         }
         $this->output->writeln($message);
         $toolstack->build();
         if ($this->runPostBuildHooks($appConfig, $toolstack->getAppDir()) === false) {
             // The user may not care if build hooks fail, but we should
             // not archive the result.
             $archive = false;
         }
         if ($archive && $toolstack->canArchive()) {
             $this->output->writeln("Saving build archive");
             if (!is_dir(dirname($archive))) {
                 mkdir(dirname($archive));
             }
             $this->fsHelper->archiveDir($tmpBuildDir, $archive);
         }
     }
     // The build is complete. Move the directory.
     $buildDir = substr($tmpBuildDir, 0, strlen($tmpBuildDir) - 4);
     if (file_exists($buildDir)) {
         if (empty($this->settings['no-backup']) && is_dir($buildDir) && !is_link($buildDir)) {
             $previousBuildArchive = dirname($buildDir) . '/' . basename($buildDir) . '-old.tar.gz';
             $this->output->writeln("Backing up previous build to: " . $previousBuildArchive);
             $this->fsHelper->archiveDir($buildDir, $previousBuildArchive);
         }
         if (!$this->fsHelper->remove($buildDir, true)) {
             $this->output->writeln(sprintf('Failed to remove directory <error>%s</error>', $buildDir));
             return false;
         }
     }
     if (!rename($tmpBuildDir, $buildDir)) {
         $this->output->writeln(sprintf('Failed to move temporary build directory into <error>%s</error>', $buildDir));
         return false;
     }
     $toolstack->setBuildDir($buildDir);
     $toolstack->install();
     $this->runPostDeployHooks($appConfig, $buildDir);
     $webRoot = $toolstack->getWebRoot();
     // Symlink the built web root ($webRoot) into www or www/appId.
     if (!is_dir($webRoot)) {
         $this->output->writeln("\nWeb root not found: <error>{$webRoot}</error>\n");
         return false;
     }
     if ($multiApp) {
         $appDir = str_replace('/', '-', $appId);
         if (is_link($destination)) {
             $this->fsHelper->remove($destination);
         }
         $destination .= "/{$appDir}";
     }
     $this->fsHelper->symlink($webRoot, $destination);
     $message = "\nBuild complete for application <info>{$appId}</info>";
     $this->output->writeln($message);
     $this->output->writeln("Web root: <info>{$destination}</info>\n");
     return true;
 }