/** * 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); } }
/** * Executes this task * * @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()) { $releaseIdentifier = $deployment->getReleaseIdentifier(); $releasesPath = $application->getDeploymentPath() . '/releases'; $this->shell->executeOrSimulate('cd ' . $releasesPath . ' && rm -f ./previous && if [ -e ./current ]; then mv ./current ./previous; fi && ln -s ./' . $releaseIdentifier . ' ./current', $node, $deployment); $deployment->getLogger()->log('Node "' . $node->getName() . '" ' . ($deployment->isDryRun() ? 'would be' : 'is') . ' live!'); }
/** * Executes this task * * @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()) { $releaseIdentifier = $deployment->getReleaseIdentifier(); $releasesPath = $application->getDeploymentPath() . '/releases'; $commands = array("mkdir -p {$releasesPath}/{$releaseIdentifier}/Data", "cd {$releasesPath}/{$releaseIdentifier}", "ln -sf ../../../shared/Data/Logs ./Data/Logs", "ln -sf ../../../shared/Data/Persistent ./Data/Persistent"); $this->shell->executeOrSimulate($commands, $node, $deployment); }
/** * Execute this task * * @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()) { $targetPath = $deployment->getApplicationReleasePath($application); $arguments = isset($options['shellUsername']) ? $options['shellUsername'] : '******'; $arguments .= ' ' . (isset($options['webserverUsername']) ? $options['webserverUsername'] : '******'); $arguments .= ' ' . (isset($options['webserverGroupname']) ? $options['webserverGroupname'] : 'www-data'); $this->shell->executeOrSimulate('cd ' . $targetPath . ' && FLOW3_CONTEXT=Production ./flow3 typo3.flow3:core:setfilepermissions ' . $arguments, $node, $deployment); }
/** * Execute this task * * @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('sitePackageKey')) { throw new \Exception('Missing site package key.', 1312312646); } $targetPath = $deployment->getApplicationReleasePath($application); $sitePackageKey = $application->getOption('sitePackageKey'); $this->shell->executeOrSimulate('cd ' . $targetPath . ' && FLOW3_CONTEXT=Production ./flow3 typo3.typo3:site:import --package-key ' . $sitePackageKey, $node, $deployment); }
/** * Execute this task * * @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()) { $targetPath = $deployment->getApplicationReleasePath($application); if (!isset($options['tagName'])) { throw new \Exception('tagName not set', 1314186541); } if (!isset($options['description'])) { throw new \Exception('description not set', 1314186553); } $targetPath = $deployment->getApplicationReleasePath($application); $this->shell->executeOrSimulate(sprintf('cd ' . $targetPath . '; git tag -f -a -m "%s" %s', $options['description'], $options['tagName']), $node, $deployment); $this->shell->executeOrSimulate(sprintf('cd ' . $targetPath . '; git submodule foreach \'git tag -f -a -m "%s" %s\'', $options['description'], $options['tagName']), $node, $deployment); }
/** * Rollback this task * * @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 rollback(Node $node, Application $application, Deployment $deployment, array $options = array()) { $deploymentPath = $application->getDeploymentPath(); $sharedPath = $application->getSharedPath(); $releasePath = $deployment->getApplicationReleasePath($application); $currentPath = $application->getDeploymentPath() . '/releases/current'; $previousPath = $application->getDeploymentPath() . '/releases/previous'; if (!isset($options['rollbackCommand'])) { return; } $command = $options['rollbackCommand']; $command = str_replace(array('{deploymentPath}', '{sharedPath}', '{releasePath}', '{currentPath}', '{previousPath}'), array($deploymentPath, $sharedPath, $releasePath, $currentPath, $previousPath), $command); $this->shell->execute($command, $node, $deployment, TRUE); }
/** * Execute a 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 $options * @return void */ public function execute($task, \TYPO3\Deploy\Domain\Model\Node $node, \TYPO3\Deploy\Domain\Model\Application $application, \TYPO3\Deploy\Domain\Model\Deployment $deployment, array $options = array()) { list($packageKey, $taskName) = explode(':', $task, 2); $taskClassName = strtr($packageKey, '.', '\\') . '\\Task\\' . strtr($taskName, ':', '\\') . 'Task'; $taskObjectName = $this->objectManager->getCaseSensitiveObjectName($taskClassName); if (!$this->objectManager->isRegistered($taskObjectName)) { throw new \Exception('Task "' . $task . '" not registered ' . $taskClassName); } $task = $this->objectManager->create($taskObjectName); if (!$deployment->isDryRun()) { $task->execute($node, $application, $deployment, $options); } else { $task->simulate($node, $application, $deployment, $options); } $this->taskHistory[] = array('task' => $task, 'node' => $node, 'application' => $application, 'deployment' => $deployment, 'options' => $options); }
/** * Executes this task * * @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()) { $targetReleasePath = $deployment->getApplicationReleasePath($application); $username = $node->getOption('username'); $hostname = $node->getHostname(); $configurationPath = $this->getDeploymentConfigurationPath() . '/Configuration/' . $deployment->getName() . '/'; $encryptedConfiguration = \TYPO3\FLOW3\Utility\Files::readDirectoryRecursively($configurationPath, 'yaml.encrypted'); if (count($encryptedConfiguration) > 0) { throw new \Exception('You have sealed configuration files, please open the configuration for "' . $deployment->getName() . '"', 1317229449); } $configurations = \TYPO3\FLOW3\Utility\Files::readDirectoryRecursively($configurationPath, 'yaml'); $commands = array(); foreach ($configurations as $configuration) { $targetConfigurationPath = dirname(str_replace($configurationPath, '', $configuration)); $commands[] = "scp {$configuration} {$username}@{$hostname}:{$targetReleasePath}/Configuration/{$targetConfigurationPath}/"; } $localhost = new Node('localhost'); $localhost->setHostname('localhost'); $this->shell->executeOrSimulate($commands, $localhost, $deployment); }
/** * Executes this task * * @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()) { $targetReleasePath = $deployment->getApplicationReleasePath($application); $commands = array("cd {$targetReleasePath}/Configuration", "mkdir -p ../../../shared/Configuration/Production", "ln -snf ../../../shared/Configuration/Production Production"); $this->shell->executeOrSimulate($commands, $node, $deployment); }
/** * Rollback this task * * @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 rollback(Node $node, Application $application, Deployment $deployment, array $options = array()) { $releasePath = $deployment->getApplicationReleasePath($application); $this->shell->execute('rm -f ' . $releasePath . 'REVISION', $node, $deployment, TRUE); }
/** * 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 this task * * @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()) { $targetPath = $deployment->getApplicationReleasePath($application); $this->shell->executeOrSimulate('cd ' . $targetPath . ' && phpunit -c Build/Common/PhpUnit/FunctionalTests.xml', $node, $deployment); }
/** * Configure tasks * * @param Workflow $workflow * @param Deployment $deployment */ protected function defineTasks(Workflow $workflow, Deployment $deployment) { $excludePatterns = array('.git*', 'Data/*', 'Web/_Resources/*', 'Build/Reports', './Cache', 'Configuration/PackageStates.php'); $baseArchiveConfiguration = array('sourceDirectory' => $deployment->getApplicationReleasePath($this), 'baseDirectory' => $this->configuration['versionAndProjectName'], 'exclude' => $excludePatterns); $workflow->defineTask('createZipDistribution', 'typo3.deploy:createArchive', array_merge($baseArchiveConfiguration, array('targetFile' => $this->configuration['zipFile']))); $workflow->defineTask('createTarGzDistribution', 'typo3.deploy:createArchive', array_merge($baseArchiveConfiguration, array('targetFile' => $this->configuration['tarGzFile']))); $workflow->defineTask('createTarBz2Distribution', 'typo3.deploy:createArchive', array_merge($baseArchiveConfiguration, array('targetFile' => $this->configuration['tarBz2File']))); if ($this->hasOption('enableSourceforgeUpload') && $this->getOption('enableSourceforgeUpload') === TRUE) { $workflow->defineTask('typo3.deploy:sourceforgeupload', 'typo3.deploy:sourceforgeupload', array('sourceforgeProjectName' => $this->getOption('sourceforgeProjectName'), 'sourceforgePackageName' => $this->getOption('sourceforgePackageName'), 'sourceforgeUserName' => $this->getOption('sourceforgeUserName'), 'version' => $this->getOption('version'), 'files' => array($this->configuration['zipFile'], $this->configuration['tarGzFile'], $this->configuration['tarBz2File']))); } $workflow->defineTask('typo3.deploy:git:tag', 'typo3.deploy:git:tag', array('tagName' => $this->getOption('version'), 'description' => 'Tag distribution with tag ' . $this->getOption('version'))); }
/** * 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); }
/** * Execute this task * * @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()) { $targetPath = $deployment->getApplicationReleasePath($application); $this->shell->executeOrSimulate('cd ' . $targetPath . ' && FLOW3_CONTEXT=Production ./flow3 typo3.flow3:doctrine:migrate', $node, $deployment); }