/** * Cleanup old releases by listing all releases and keeping a configurable * number of old releases (application option "keepReleases"). The current * and previous release (if one exists) are protected from removal. * * Example configuration: * * $application->setOption('keepReleases', 2); * * Note: There is no rollback for this cleanup, so we have to be sure not to delete any * live or referenced releases. * * @param \TYPO3\Surf\Domain\Model\Node $node * @param \TYPO3\Surf\Domain\Model\Application $application * @param \TYPO3\Surf\Domain\Model\Deployment $deployment * @param array $options * @return void */ public function execute(Node $node, Application $application, Deployment $deployment, array $options = array()) { if (!isset($options['keepReleases'])) { $deployment->getLogger()->debug(($deployment->isDryRun() ? 'Would keep' : 'Keeping') . ' all releases for "' . $application->getName() . '"'); return; } $keepReleases = $options['keepReleases']; $releasesPath = $application->getReleasesPath(); $currentReleaseIdentifier = $deployment->getReleaseIdentifier(); $previousReleasePath = $application->getReleasesPath() . '/previous'; $previousReleaseIdentifier = trim($this->shell->execute("if [ -h {$previousReleasePath} ]; then basename `readlink {$previousReleasePath}` ; fi", $node, $deployment)); $allReleasesList = $this->shell->execute("if [ -d {$releasesPath}/. ]; then find {$releasesPath}/. -maxdepth 1 -type d -exec basename {} \\; ; fi", $node, $deployment); $allReleases = preg_split('/\\s+/', $allReleasesList, -1, PREG_SPLIT_NO_EMPTY); $removableReleases = array(); foreach ($allReleases as $release) { if ($release !== '.' && $release !== $currentReleaseIdentifier && $release !== $previousReleaseIdentifier && $release !== 'current' && $release !== 'previous') { $removableReleases[] = trim($release); } } sort($removableReleases); $removeReleases = array_slice($removableReleases, 0, count($removableReleases) - $keepReleases); $removeCommand = ''; foreach ($removeReleases as $removeRelease) { $removeCommand .= "rm -rf {$releasesPath}/{$removeRelease};rm -f {$releasesPath}/{$removeRelease}REVISION;"; } if (count($removeReleases) > 0) { $deployment->getLogger()->info(($deployment->isDryRun() ? 'Would remove' : 'Removing') . ' releases ' . implode(', ', $removeReleases)); $this->shell->executeOrSimulate($removeCommand, $node, $deployment); } else { $deployment->getLogger()->info('No releases to remove'); } }
/** * Execute this task * * @param \TYPO3\Surf\Domain\Model\Node $node * @param \TYPO3\Surf\Domain\Model\Application $application * @param \TYPO3\Surf\Domain\Model\Deployment $deployment * @param array $options * @return void * @throws \TYPO3\Surf\Exception\InvalidConfigurationException * @throws \TYPO3\Surf\Exception\TaskExecutionException */ public function execute(Node $node, Application $application, Deployment $deployment, array $options = array()) { if (!isset($options['repositoryUrl'])) { throw new \TYPO3\Surf\Exception\InvalidConfigurationException(sprintf('Missing "repositoryUrl" option for application "%s"', $application->getName()), 1374074052); } $localCheckoutPath = $deployment->getWorkspacePath($application); $node = $deployment->getNode('localhost'); $sha1 = $this->executeOrSimulateGitCloneOrUpdate($localCheckoutPath, $node, $deployment, $options); $this->executeOrSimulatePostGitCheckoutCommands($localCheckoutPath, $sha1, $node, $deployment, $options); }
/** * Execute a task * * @param string $taskName * @param \TYPO3\Surf\Domain\Model\Node $node * @param \TYPO3\Surf\Domain\Model\Application $application * @param \TYPO3\Surf\Domain\Model\Deployment $deployment * @param string $stage * @param array $options Local task options * @return void * @throws \TYPO3\Surf\Exception\InvalidConfigurationException */ public function execute($taskName, Node $node, Application $application, Deployment $deployment, $stage, array $options = array()) { $deployment->getLogger()->info($node->getName() . ' (' . $application->getName() . ') ' . $taskName); $task = $this->createTaskInstance($taskName); $globalOptions = $this->overrideOptions($taskName, $deployment, $node, $application, $options); if (!$deployment->isDryRun()) { $task->execute($node, $application, $deployment, $globalOptions); } else { $task->simulate($node, $application, $deployment, $globalOptions); } $this->taskHistory[] = array('task' => $task, 'node' => $node, 'application' => $application, 'deployment' => $deployment, 'stage' => $stage, 'options' => $globalOptions); }
/** * @param Application $application * @return string */ public function getWorkspacePath(Application $application) { $workspacePath = FLOW_PATH_DATA . 'Surf/'; if ($application->hasOption('repositoryUrl')) { $urlParts = GeneralUtility::getUrlPartsFromRepositoryUrl($application->getOption('repositoryUrl')); $workspacePath .= preg_replace('/[^a-zA-Z0-9]/', '-', $urlParts['path']); $workspacePath .= '_' . substr(sha1($application->getOption('repositoryUrl')), 0, 5); } else { // Default $workspacePath .= $this->getName() . '/' . $application->getName(); } return $workspacePath; }
/** * Executes this task * * @param \TYPO3\Surf\Domain\Model\Node $node * @param \TYPO3\Surf\Domain\Model\Application $application * @param \TYPO3\Surf\Domain\Model\Deployment $deployment * @param array $options * @return void * @throws TaskExecutionException */ public function execute(Node $node, Application $application, Deployment $deployment, array $options = array()) { $targetPath = isset($options['deploymentLogTargetPath']) ? $options['deploymentLogTargetPath'] : '.'; $fileName = !empty($options['deploymentLogFileName']) ? $options['deploymentLogFileName'] : 'deployment.log'; $optionsToLog = !empty($options['deploymentLogOptions']) ? $options['deploymentLogOptions'] : array('tag', 'branch', 'sha1'); $logContent = array(date('Y-m-d H:i:s (D)'), 'Application: ' . $application->getName(), 'Deployment: ' . $deployment->getName(), 'Status: ' . $deployment->getStatus()); foreach ($optionsToLog as $key) { if (!empty($options[$key])) { $logContent[] = $key . ' = ' . $options[$key]; } } $commands = array('cd ' . escapeshellarg($application->getReleasesPath()), 'echo ' . escapeshellarg(implode(' | ', $logContent)) . ' >> ' . rtrim($targetPath, '/') . '/' . $fileName); $this->shell->executeOrSimulate($commands, $node, $deployment); }
/** * Execute this task * * @param \TYPO3\Surf\Domain\Model\Node $node * @param \TYPO3\Surf\Domain\Model\Application $application * @param \TYPO3\Surf\Domain\Model\Deployment $deployment * @param array $options * @return void * @throws \TYPO3\Surf\Exception\InvalidConfigurationException * @throws \TYPO3\Surf\Exception\TaskExecutionException */ public function execute(Node $node, Application $application, Deployment $deployment, array $options = array()) { if (!isset($options['repositoryUrl'])) { throw new \TYPO3\Surf\Exception\InvalidConfigurationException(sprintf('Missing "repositoryUrl" option for application "%s"', $application->getName()), 1335974764); } $releasePath = $deployment->getApplicationReleasePath($application); $checkoutPath = $application->getDeploymentPath() . '/cache/transfer'; if (!isset($options['hardClean'])) { $options['hardClean'] = true; } $sha1 = $this->executeOrSimulateGitCloneOrUpdate($checkoutPath, $node, $deployment, $options); $command = strtr("\n\t\t\tcp -RPp {$checkoutPath}/. {$releasePath}\n\t\t\t\t&& (echo {$sha1} > {$releasePath}" . 'REVISION) ', "\t\n", ' '); $this->shell->executeOrSimulate($command, $node, $deployment); $this->executeOrSimulatePostGitCheckoutCommands($releasePath, $sha1, $node, $deployment, $options); }
/** * Get a local workspace directory for the application */ public function getWorkspacePath(Application $application) { return $this->workspacesBasePath . '/' . $this->getName() . '/' . $application->getName(); }
/** * Execute a task and consider configured before / after "hooks" * * Will also execute tasks that are registered to run before or after this task. * * @param string $task * @param \TYPO3\Surf\Domain\Model\Node $node * @param \TYPO3\Surf\Domain\Model\Application $application * @param \TYPO3\Surf\Domain\Model\Deployment $deployment * @param string $stage * @param array $callstack * @return void * @throws \TYPO3\Surf\Exception\TaskExecutionException */ protected function executeTask($task, Node $node, Application $application, Deployment $deployment, $stage, array &$callstack = array()) { foreach (array('_', $application->getName()) as $applicationName) { if (isset($this->tasks['before'][$applicationName][$task])) { foreach ($this->tasks['before'][$applicationName][$task] as $beforeTask) { $deployment->getLogger()->debug('Task "' . $beforeTask . '" before "' . $task); $this->executeTask($beforeTask, $node, $application, $deployment, $stage, $callstack); } } } if (isset($callstack[$task])) { throw new \TYPO3\Surf\Exception\TaskExecutionException('Cycle for task "' . $task . '" detected, aborting.', 1335976544); } if (isset($this->tasks['defined'][$task])) { $this->taskManager->execute($this->tasks['defined'][$task]['task'], $node, $application, $deployment, $stage, $this->tasks['defined'][$task]['options'], $task); } else { $this->taskManager->execute($task, $node, $application, $deployment, $stage); } $callstack[$task] = true; foreach (array('_', $application->getName()) as $applicationName) { $label = $applicationName === '_' ? 'for all' : 'for application ' . $applicationName; if (isset($this->tasks['after'][$applicationName][$task])) { foreach ($this->tasks['after'][$applicationName][$task] as $beforeTask) { $deployment->getLogger()->debug('Task "' . $beforeTask . '" after "' . $task . '" ' . $label); $this->executeTask($beforeTask, $node, $application, $deployment, $stage, $callstack); } } } }
/** * @param \TYPO3\Surf\Domain\Model\Application $application * * @return void * * @throws TaskExecutionException */ protected function setRemoteNode(Application $application) { $this->requireApplication(__METHOD__); $nodes = $application->getNodes(); if (count($nodes) < 1) { throw new TaskExecutionException("There is no node is set for application '{$application->getName()}'. Please check your TYPO3/Surf configuration."); } $this->remoteNode = array_shift($nodes); }
/** * Prints stages and contained tasks for given application * * @param Application $application * @param array $stages * @param array $tasks */ protected function printStages(Application $application, array $stages, array $tasks) { foreach ($stages as $stage) { $this->output->writeln(' <comment>' . $stage . ':</comment>'); foreach (['before', 'tasks', 'after'] as $stageStep) { $output = ''; foreach (['_', $application->getName()] as $applicationName) { $label = $applicationName === '_' ? 'for all applications' : 'for application ' . $applicationName; if (isset($tasks['stage'][$applicationName][$stage][$stageStep])) { foreach ($tasks['stage'][$applicationName][$stage][$stageStep] as $task) { $output .= ' <success>' . $task . '</success> <info>(' . $label . ')</info>' . PHP_EOL; } } } if (strlen($output) > 0) { $this->output->writeln(' <info>' . $stageStep . ':</info>'); } $this->output->write($output); } } }