/** * 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\Deploy\Domain\Model\Node $node * @param \TYPO3\Deploy\Domain\Model\Application $application * @param \TYPO3\Deploy\Domain\Model\Deployment $deployment * @param array $options * @return void */ public function execute(Node $node, Application $application, Deployment $deployment, array $options = array()) { if (!$application->hasOption('keepReleases')) { $deployment->getLogger()->log(($deployment->isDryRun() ? 'Would keep' : 'Keeping') . ' all releases for "' . $application->getName() . '"', LOG_DEBUG); return; } $keepReleases = $application->getOption('keepReleases'); $releasesPath = $application->getDeploymentPath() . '/releases'; $currentReleaseIdentifier = $deployment->getReleaseIdentifier(); $previousReleasePath = $application->getDeploymentPath() . '/releases/previous'; $previousReleaseIdentifier = trim($this->shell->execute("if [ -h {$previousReleasePath} ]; then basename `readlink {$previousReleasePath}` ; fi", $node, $deployment)); $allReleasesList = $this->shell->execute("find {$releasesPath}/. -maxdepth 1 -type d -exec basename {} \\;", $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()->log(($deployment->isDryRun() ? 'Would remove' : 'Removing') . ' releases ' . implode(', ', $removeReleases)); $this->shell->executeOrSimulate($removeCommand, $node, $deployment); } else { $deployment->getLogger()->log('No releases to remove', LOG_DEBUG); } }
/** * 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\Deploy\Domain\Model\Node $node * @param \TYPO3\Deploy\Domain\Model\Application $application * @param \TYPO3\Deploy\Domain\Model\Deployment $deployment * @param array $callstack * @return void */ protected function executeTask($task, Node $node, Application $application, Deployment $deployment, 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()->log('Task "' . $beforeTask . '" before "' . $task, LOG_DEBUG); $this->executeTask($beforeTask, $node, $application, $deployment, $callstack); } } } if (isset($callstack[$task])) { throw new \Exception('Cycle for task "' . $task . '" detected, aborting.'); } $deployment->getLogger()->log('Execute task "' . $task . '" on "' . $node->getName() . '" for application "' . $application->getName() . '"', LOG_DEBUG); if (isset($this->tasks['defined'][$task])) { $this->taskManager->execute($this->tasks['defined'][$task]['task'], $node, $application, $deployment, $this->tasks['defined'][$task]['options']); } else { $this->taskManager->execute($task, $node, $application, $deployment); } $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()->log('Task "' . $beforeTask . '" after "' . $task . '" ' . $label, LOG_DEBUG); $this->executeTask($beforeTask, $node, $application, $deployment, $callstack); } } } }
/** * Execute a shell command via SSH * * @param mixed $command * @param \TYPO3\Deploy\Domain\Model\Node $node * @param \TYPO3\Deploy\Domain\Model\Deployment $deployment * @return array */ protected function executeRemoteCommand($command, Node $node, Deployment $deployment) { $command = $this->prepareCommand($command); $deployment->getLogger()->log(' $' . $node->getName() . ': "' . $command . '"', LOG_DEBUG); $username = $node->getOption('username'); $hostname = $node->getHostname(); $returnedOutput = ''; // TODO Get SSH options from node or deployment $fp = popen('ssh -A ' . $username . '@' . $hostname . ' ' . escapeshellarg($command) . ' 2>&1', 'r'); while (($line = fgets($fp)) !== FALSE) { $deployment->getLogger()->log(' > ' . rtrim($line)); $returnedOutput .= $line; } $exitCode = pclose($fp); return array($exitCode, $returnedOutput); }