├── .gitignore ├── BeSimpleDeploymentBundle.php ├── Command ├── DeploymentCommand.php ├── LaunchDeploymentCommand.php └── TestDeploymentCommand.php ├── DependencyInjection ├── BeSimpleDeploymentExtension.php └── Configuration.php ├── Deployer ├── Config.php ├── Deployer.php ├── Logger.php ├── Rsync.php └── Ssh.php ├── Event ├── CommandEvent.php ├── DeployerEvent.php └── FeedbackEvent.php ├── Events.php ├── README.md └── Resources └── config └── deployment.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .*.un~ 2 | .*.swp -------------------------------------------------------------------------------- /BeSimpleDeploymentBundle.php: -------------------------------------------------------------------------------- 1 | setDefinition(array( 29 | new InputArgument('server', InputArgument::OPTIONAL, 'The target server name', null), 30 | )) 31 | ; 32 | } 33 | 34 | /** 35 | * @param \Symfony\Component\Console\Input\InputInterface $input 36 | * @param \Symfony\Component\Console\Output\OutputInterface $output 37 | * @return void 38 | */ 39 | protected function execute(InputInterface $input, OutputInterface $output) 40 | { 41 | $deployer = $this->getContainer()->get('be_simple_deployment.deployer'); 42 | $eventDispatcher = $this->getContainer()->get('event_dispatcher'); 43 | 44 | $this->output = $output; 45 | $this->output->setDecorated(true); 46 | 47 | $self = $this; 48 | 49 | if ($output->getVerbosity() > Output::VERBOSITY_QUIET) { 50 | 51 | // Start event 52 | 53 | $eventDispatcher->addListener(Events::onDeploymentStart, function (DeployerEvent $event) use ($self) { 54 | $self->write(sprintf( 55 | 'Starting %s deployment on server "%s"', 56 | $event->isTest() ? 'test' : 'real', 57 | $event->getServer() 58 | )); 59 | }); 60 | 61 | // Feedback events 62 | 63 | if ($output->getVerbosity() > Output::VERBOSITY_NORMAL) { 64 | $eventDispatcher->addListener(Events::onDeploymentRsyncFeedback, function (FeedbackEvent $event) use ($self) { 65 | $self->write(sprintf( 66 | '[Rsync] %s', 67 | $event->getFeedback() 68 | ), $event->isError() ? 'error' : 'comment'); 69 | }); 70 | 71 | $eventDispatcher->addListener(Events::onDeploymentSshFeedback, function (FeedbackEvent $event) use ($self) { 72 | $self->write(sprintf( 73 | '[SSH] %s', 74 | $event->getFeedback() 75 | ), $event->isError() ? 'error' : 'comment'); 76 | }); 77 | } 78 | 79 | // Success events 80 | 81 | $eventDispatcher->addListener(Events::onDeploymentSuccess, function (DeployerEvent $event) use ($self) { 82 | $self->write(sprintf( 83 | '%s deployment on server "%s" succeded', 84 | $event->isTest() ? 'Test' : 'Real', 85 | $event->getServer() 86 | ), 'info'); 87 | }); 88 | 89 | $eventDispatcher->addListener(Events::onDeploymentRsyncSuccess, function (CommandEvent $event) use ($self) { 90 | $self->write(sprintf( 91 | '[Rsync success] %s', 92 | $event->getCommand() 93 | )); 94 | }, 'info'); 95 | 96 | $eventDispatcher->addListener(Events::onDeploymentSshSuccess, function (CommandEvent $event) use ($self) { 97 | $self->write(sprintf( 98 | '[SSH success] %s', 99 | $event->getCommand() 100 | )); 101 | }, 'info'); 102 | } 103 | 104 | $this->executeDeployment($deployer, $input->getArgument('server')); 105 | } 106 | 107 | /** 108 | * @param string $message 109 | * @param string $style 110 | * @return void 111 | */ 112 | public function write($message, $style = 'comment') 113 | { 114 | $this->output->writeln(sprintf('<%s>%s', $style, $message, $style)); 115 | } 116 | 117 | /** 118 | * @abstract 119 | * @param \BeSimple\DeploymentBundle\Deployer\Deployer $deployer 120 | * @param string $server 121 | * @return void 122 | */ 123 | abstract protected function executeDeployment(Deployer $deployer, $server); 124 | } 125 | -------------------------------------------------------------------------------- /Command/LaunchDeploymentCommand.php: -------------------------------------------------------------------------------- 1 | setName('deployment:launch') 19 | ->setDescription('Launch deployment') 20 | ; 21 | } 22 | 23 | /** 24 | * @param \BeSimple\DeploymentBundle\Deployer\Deployer $deployer 25 | * @param string $server 26 | * @return void 27 | */ 28 | protected function executeDeployment(Deployer $deployer, $server) 29 | { 30 | $deployer->launch($server); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Command/TestDeploymentCommand.php: -------------------------------------------------------------------------------- 1 | setName('deployment:test') 19 | ->setDescription('Test deployment') 20 | ; 21 | } 22 | 23 | /** 24 | * @param \BeSimple\DeploymentBundle\Deployer\Deployer $deployer 25 | * @param string $server 26 | * @return void 27 | */ 28 | protected function executeDeployment(Deployer $deployer, $server) 29 | { 30 | $deployer->test($server); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /DependencyInjection/BeSimpleDeploymentExtension.php: -------------------------------------------------------------------------------- 1 | process($configuration->getConfigTree(), $configs); 23 | 24 | $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 25 | $loader->load('deployment.xml'); 26 | 27 | $container->setParameter('be_simple_deployment.config.rsync', $config['rsync']); 28 | $container->setParameter('be_simple_deployment.config.ssh', $config['rsync']); 29 | $container->setParameter('be_simple_deployment.config.rules', $config['rules']); 30 | $container->setParameter('be_simple_deployment.config.commands', $config['commands']); 31 | $container->setParameter('be_simple_deployment.config.servers', $config['servers']); 32 | } 33 | 34 | /** 35 | * @return string 36 | */ 37 | public function getAlias() 38 | { 39 | return 'be_simple_deployment'; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | root('be_simple_deployment', 'array'); 19 | 20 | $this->addRsyncSection($rootNode); 21 | $this->addSshSection($rootNode); 22 | $this->addRulesSection($rootNode); 23 | $this->addCommandsSection($rootNode); 24 | $this->addServersSection($rootNode); 25 | 26 | return $treeBuilder->buildTree(); 27 | } 28 | 29 | /** 30 | * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $node 31 | * @return void 32 | */ 33 | protected function addRsyncSection(ArrayNodeDefinition $node) 34 | { 35 | $node->children() 36 | ->arrayNode('rsync') 37 | ->addDefaultsIfNotSet() 38 | ->children() 39 | ->scalarNode('command')->defaultValue('rsync')->cannotBeEmpty()->end() 40 | ->booleanNode('delete')->defaultFalse()->end() 41 | ->scalarNode('options')->defaultValue('-Cva')->end() 42 | ->scalarNode('root')->defaultValue('%kernel.root_dir%/..')->cannotBeEmpty()->end() 43 | ->end() 44 | ; 45 | } 46 | 47 | /** 48 | * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $node 49 | * @return void 50 | */ 51 | protected function addSshSection(ArrayNodeDefinition $node) 52 | { 53 | $node->children() 54 | ->arrayNode('ssh')->children() 55 | ->scalarNode('pubkey_file')->defaultNull()->end() 56 | ->scalarNode('privkey_file')->defaultNull()->end() 57 | ->scalarNode('passphrase')->defaultNull()->end() 58 | ->end() 59 | ; 60 | } 61 | 62 | /** 63 | * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $node 64 | * @return void 65 | */ 66 | protected function addRulesSection(ArrayNodeDefinition $node) 67 | { 68 | $node->children() 69 | ->arrayNode('rules') 70 | ->useAttributeAsKey('name') 71 | ->prototype('array') 72 | ->children() 73 | ->arrayNode('ignore') 74 | ->useAttributeAsKey('ignore')->prototype('scalar')->end() 75 | ->end() 76 | ->arrayNode('force') 77 | ->useAttributeAsKey('force')->prototype('scalar')->end() 78 | ->end() 79 | ->end() 80 | ->end() 81 | ; 82 | } 83 | 84 | /** 85 | * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $node 86 | * @return void 87 | */ 88 | protected function addCommandsSection(ArrayNodeDefinition $node) 89 | { 90 | $node->children() 91 | ->arrayNode('commands') 92 | ->useAttributeAsKey('name') 93 | ->children() 94 | ->scalarNode('command')->defaultValue('./app/console')->cannotBeEmpty()->end() 95 | ->end() 96 | ->prototype('array')->children() 97 | ->scalarNode('type')->defaultValue('symfony')->end() 98 | ->scalarNode('command')->isRequired()->cannotBeEmpty()->end() 99 | ->scalarNode('env')->defaultNull()->end() 100 | ->end() 101 | ; 102 | } 103 | 104 | /** 105 | * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $node 106 | * @return void 107 | */ 108 | protected function addServersSection(ArrayNodeDefinition $node) 109 | { 110 | $node->children() 111 | ->arrayNode('servers') 112 | ->useAttributeAsKey('name') 113 | ->prototype('array')->children() 114 | ->scalarNode('host')->defaultValue('localhost')->end() 115 | ->scalarNode('rsync_port')->defaultNull()->end() 116 | ->scalarNode('ssh_port')->defaultValue(22)->cannotBeEmpty()->end() 117 | ->scalarNode('username')->defaultNull()->end() 118 | ->scalarNode('password')->defaultNull()->end() 119 | ->scalarNode('path')->isRequired()->cannotBeEmpty()->end() 120 | ->arrayNode('rules') 121 | ->useAttributeAsKey('rules')->prototype('scalar')->end() 122 | ->end() 123 | ->arrayNode('commands') 124 | ->useAttributeAsKey('commands')->prototype('scalar')->end() 125 | ->end() 126 | ->end() 127 | ->end() 128 | ; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /Deployer/Config.php: -------------------------------------------------------------------------------- 1 | rules = $rules; 35 | $this->commands = $commands; 36 | $this->servers = $servers; 37 | $this->config = array(); 38 | } 39 | 40 | /** 41 | * @return array 42 | */ 43 | public function getServerNames() 44 | { 45 | return array_keys($this->servers); 46 | } 47 | 48 | /** 49 | * @throws \InvalidArgumentException 50 | * @param string $server 51 | * @return array 52 | */ 53 | public function getServerConfig($server) 54 | { 55 | if (!isset($this->servers[$server])) { 56 | throw new \InvalidArgumentException(sprintf('Server "%s" not configured', $server)); 57 | } 58 | 59 | if (!isset($this->config[$server])) { 60 | $this->config[$server] = array( 61 | 'connection' => $this->getConnectionConfig($server), 62 | 'rules' => $this->getRulesConfig($server), 63 | 'commands' => $this->getCommandsConfig($server), 64 | ); 65 | } 66 | 67 | return $this->config[$server]; 68 | } 69 | 70 | /** 71 | * @param string $server 72 | * @return array 73 | */ 74 | protected function getConnectionConfig($server) 75 | { 76 | $config = $this->servers[$server]; 77 | unset($config['rules'], $config['commands']); 78 | 79 | return $config; 80 | } 81 | 82 | /** 83 | * @throws \InvalidArgumentException 84 | * @param string $server 85 | * @return array 86 | */ 87 | protected function getRulesConfig($server) 88 | { 89 | $config = array( 90 | 'ignore' => array(), 91 | 'force' => array() 92 | ); 93 | 94 | $parameters = array_keys($config); 95 | 96 | foreach ($this->servers[$server]['rules'] as $name) { 97 | if (!isset($this->rules[$name])) { 98 | throw new \InvalidArgumentException(sprintf('Rule "%s" declared in server "%s" is not configured', $name, $server)); 99 | } 100 | 101 | foreach ($parameters as $parameter) { 102 | $config[$parameter] = array_merge($config[$parameter], $this->rules[$name][$parameter]); 103 | } 104 | } 105 | 106 | return $config; 107 | } 108 | 109 | /** 110 | * @throws \InvalidArgumentException 111 | * @param string $server 112 | * @return array 113 | */ 114 | protected function getCommandsConfig($server) 115 | { 116 | $config = array(); 117 | 118 | foreach ($this->servers[$server]['commands'] as $name) { 119 | if (!isset($this->commands[$name])) { 120 | throw new \InvalidArgumentException(sprintf('Command "%s" declared in server "%s" is not configured', $name, $server)); 121 | } 122 | 123 | $config[] = $this->commands[$name]; 124 | } 125 | 126 | return $config; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Deployer/Deployer.php: -------------------------------------------------------------------------------- 1 | rsync = $rsync; 40 | $this->ssh = $ssh; 41 | $this->config = $config; 42 | $this->dispatcher = $eventDispatcher; 43 | } 44 | 45 | /** 46 | * @param null|string $server 47 | * @return void 48 | */ 49 | public function launch($server = null) 50 | { 51 | $this->deploy($server, true); 52 | } 53 | 54 | /** 55 | * @param null|string $server 56 | * @return void 57 | */ 58 | public function test($server = null) 59 | { 60 | $this->deploy($server, false); 61 | } 62 | 63 | /** 64 | * @param null|string $server 65 | * @param bool $real 66 | * @return 67 | */ 68 | protected function deploy($server = null, $real = false) 69 | { 70 | if(is_null($server)) { 71 | foreach($this->config->getServerNames() as $server) { 72 | $this->deploy($server, $real); 73 | } 74 | 75 | return; 76 | } 77 | 78 | $this->dispatcher->dispatch(Events::onDeploymentStart, new DeployerEvent($server, $real)); 79 | 80 | $config = $this->config->getServerConfig($server); 81 | 82 | $this->rsync->run($config['connection'], $config['rules'], $real); 83 | if(false === empty($config['commands'])){ 84 | $this->ssh->run($config['connection'], $config['commands'], $real); 85 | } 86 | 87 | $this->dispatcher->dispatch(Events::onDeploymentSuccess, new DeployerEvent($server, $real)); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Deployer/Logger.php: -------------------------------------------------------------------------------- 1 | logger = $logger; 25 | } 26 | 27 | /** 28 | * @param string $type 29 | * @param string $server 30 | * @param int $count 31 | * @return void 32 | */ 33 | public function logDeployment($type, $server, $count) 34 | { 35 | $this->log(self::INFO, sprintf('%s files deployed via %s on %s', $count, $type, $server)); 36 | } 37 | 38 | /** 39 | * @param \Exception $exception 40 | * @return void 41 | */ 42 | public function logException(\Exception $exception) 43 | { 44 | $this->log(self::ERROR, $exception->getMessage()); 45 | } 46 | 47 | /** 48 | * @param string $type 49 | * @param string $message 50 | * @return void 51 | */ 52 | protected function log($type, $message) 53 | { 54 | if ($this->logger instanceof LoggerInterface) { 55 | $this->logger->$type($message); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Deployer/Rsync.php: -------------------------------------------------------------------------------- 1 | logger = $logger; 52 | $this->dispatcher = $eventDispatcher; 53 | $this->config = $config; 54 | $this->stdout = array(); 55 | $this->stderr = array(); 56 | $this->callback = null; 57 | } 58 | 59 | /** 60 | * @param string $glue 61 | * @return array|string 62 | */ 63 | public function getStdout($glue = "\n") 64 | { 65 | if (!$glue) { 66 | return $this->stdout; 67 | } 68 | 69 | return implode($glue, $this->stdout); 70 | } 71 | 72 | /** 73 | * @param string $glue 74 | * @return array|string 75 | */ 76 | public function getStderr($glue = "\n") 77 | { 78 | if (!$glue) { 79 | return $this->stderr; 80 | } 81 | 82 | return implode($glue, $this->stderr); 83 | } 84 | 85 | /** 86 | * @throws \Exception|\InvalidArgumentException 87 | * @param array $connection 88 | * @param array $rules 89 | * @param bool $real 90 | * @return array 91 | */ 92 | public function run(array $connection, array $rules, $real = false) 93 | { 94 | $root = realpath($this->config['root']); 95 | 96 | if (!$root) { 97 | throw new \InvalidArgumentException(sprintf('Invalid "root" option : "%s" is not a valid path', $this->config['root'])); 98 | } 99 | 100 | $command = $this->buildCommand($connection, $rules, $real); 101 | $process = new Process($command, $root); 102 | 103 | $this->stderr = array(); 104 | $this->stdout = array(); 105 | 106 | $this->dispatcher->dispatch(Events::onDeploymentRsyncStart, new CommandEvent($command)); 107 | 108 | $code = $process->run(array($this, 'onStdLine')); 109 | 110 | if ($code !== 0) { 111 | throw new \Exception($this->stderr, $process->getExitCode()); 112 | } 113 | 114 | $this->dispatcher->dispatch(Events::onDeploymentRsyncSuccess, new CommandEvent($command)); 115 | 116 | return $this->stdout; 117 | } 118 | 119 | /** 120 | * @param string $type 121 | * @param string $line 122 | * @return void 123 | */ 124 | public function onStdLine($type, $line) 125 | { 126 | if ('out' == $type) { 127 | $this->stdout[] = $line; 128 | } else { 129 | $this->stderr = $line; 130 | } 131 | 132 | $this->dispatcher->dispatch(Events::onDeploymentRsyncFeedback, new FeedbackEvent($type, $line)); 133 | } 134 | 135 | /** 136 | * @param array $connection 137 | * @param array $rules 138 | * @param bool $real 139 | * @return string 140 | */ 141 | protected function buildCommand(array $connection, array $rules, $real = false) 142 | { 143 | $source = '.'; 144 | $user = $connection['username'] ? $connection['username'].'@' : ''; 145 | $destination = sprintf('%s%s:%s', $user, $connection['host'], $connection['path']); 146 | $options = array(); 147 | $options[] = $this->config['options']; 148 | 149 | if (!empty($connection['port'])) { 150 | $options[] = '-p '.$connection['port']; 151 | } 152 | 153 | if ($this->config['delete']) { 154 | $options[] = '--delete'; 155 | } 156 | 157 | if (count($rules)) { 158 | foreach ($rules['ignore'] as $mask) { 159 | $options[] = sprintf('--exclude="%s"', $mask); 160 | } 161 | 162 | foreach ($rules['force'] as $mask) { 163 | $options[] = sprintf('--include="%s"', $mask); 164 | } 165 | } 166 | 167 | $strReal = $real ? '' : '--dry-run'; 168 | 169 | return sprintf('%s -e ssh %s %s %s %s', $this->config['command'], $strReal, implode(' ', $options), $source, $destination); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /Deployer/Ssh.php: -------------------------------------------------------------------------------- 1 | logger = $logger; 55 | $this->dispatcher = $eventDispatcher; 56 | $this->config = $config; 57 | $this->session = null; 58 | $this->shell = null; 59 | $this->stdout = array(); 60 | $this->stdin = array(); 61 | $this->stderr = array(); 62 | } 63 | 64 | /** 65 | * @param string $glue 66 | * @return array|string 67 | */ 68 | public function getStdout($glue = "\n") 69 | { 70 | if (!$glue) { 71 | return $this->stdout; 72 | } 73 | 74 | return implode($glue, $this->stdout); 75 | } 76 | 77 | /** 78 | * @param string $glue 79 | * @return array|string 80 | */ 81 | public function getStderr($glue = "\n") 82 | { 83 | if (!$glue) { 84 | return $this->stderr; 85 | } 86 | 87 | return implode($glue, $this->stderr); 88 | } 89 | 90 | /** 91 | * @param array $connection 92 | * @param array $commands 93 | * @param bool $real 94 | * @return array 95 | */ 96 | public function run(array $connection, array $commands, $real = false) 97 | { 98 | $this->connect($connection); 99 | $this->execute(array('type' => 'shell', 'command' => sprintf('cd %s', $connection['path']))); 100 | 101 | if ($real) { 102 | foreach ($commands as $command) { 103 | $this->execute($command); 104 | } 105 | } 106 | 107 | $this->disconnect(); 108 | 109 | return $this->stdout; 110 | } 111 | 112 | /** 113 | * @throws \InvalidArgumentException|\RuntimeException 114 | * @param array $connection 115 | * @return void 116 | */ 117 | protected function connect(array $connection) 118 | { 119 | $this->session = ssh2_connect($connection['host'], $connection['ssh_port']); 120 | 121 | if (!$this->session) { 122 | throw new \InvalidArgumentException(sprintf('SSH connection failed on "%s:%s"', $connection['host'], $connection['ssh_port'])); 123 | } 124 | 125 | if (isset($connection['username']) && isset($connection['pubkey_file']) && isset($connection['privkey_file'])) { 126 | if (!ssh2_auth_pubkey_file($connection['username'], $connection['pubkey_file'], $connection['privkey_file'], $connection['passphrase'])) { 127 | throw new \InvalidArgumentException(sprintf('SSH authentication failed for user "%s" with public key "%s"', $connection['username'], $connection['pubkey_file'])); 128 | } 129 | } else if ($connection['username'] && $connection['password']) { 130 | if (!ssh2_auth_password($this->session, $connection['username'], $connection['password'])) { 131 | throw new \InvalidArgumentException(sprintf('SSH authentication failed for user "%s"', $connection['username'])); 132 | } 133 | } 134 | 135 | $this->shell = ssh2_shell($this->session); 136 | 137 | if (!$this->shell) { 138 | throw new \RuntimeException(sprintf('Failed opening "%s" shell', $this->config['shell'])); 139 | } 140 | 141 | $this->stdout = array(); 142 | $this->stdin = array(); 143 | } 144 | 145 | /** 146 | * @return void 147 | */ 148 | protected function disconnect() 149 | { 150 | fclose($this->shell); 151 | } 152 | 153 | /** 154 | * @param array $command 155 | * @return void 156 | */ 157 | protected function execute(array $command) 158 | { 159 | $command = $this->buildCommand($command); 160 | 161 | $this->dispatcher->dispatch(Events::onDeploymentSshStart, new CommandEvent($command)); 162 | 163 | $outStream = ssh2_exec($this->session, $command); 164 | $errStream = ssh2_fetch_stream($outStream, SSH2_STREAM_STDERR); 165 | 166 | stream_set_blocking($outStream, true); 167 | stream_set_blocking($errStream, true); 168 | 169 | $stdout = explode("\n", stream_get_contents($outStream)); 170 | $stderr = explode("\n", stream_get_contents($errStream)); 171 | 172 | if (count($stdout)) { 173 | $this->dispatcher->dispatch(Events::onDeploymentRsyncFeedback, new FeedbackEvent('out', implode("\n", $stdout))); 174 | } 175 | 176 | if (count($stdout)) { 177 | $this->dispatcher->dispatch(Events::onDeploymentRsyncFeedback, new FeedbackEvent('err', implode("\n", $stderr))); 178 | } 179 | 180 | $this->stdout = array_merge($this->stdout, $stdout); 181 | 182 | if (is_array($stderr)) { 183 | $this->stderr = array_merge($this->stderr, $stderr); 184 | } else { 185 | $this->dispatcher->dispatch(Events::onDeploymentSshSuccess, new CommandEvent($command)); 186 | } 187 | 188 | fclose($outStream); 189 | fclose($errStream); 190 | } 191 | 192 | /** 193 | * @param array $command 194 | * @return string 195 | */ 196 | protected function buildCommand(array $command) 197 | { 198 | if ($command['type'] === 'shell') { 199 | return $command['command']; 200 | } 201 | 202 | $symfony = $this->config['symfony_command']; 203 | $env = $command['env'] ?: $this->env; 204 | 205 | return sprintf('%s %s --env="%s"', $symfony, $command['command'], $env); 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /Event/CommandEvent.php: -------------------------------------------------------------------------------- 1 | command = $command; 21 | } 22 | 23 | /** 24 | * @return string 25 | */ 26 | public function getCommand() 27 | { 28 | return $this->command; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Event/DeployerEvent.php: -------------------------------------------------------------------------------- 1 | server = $server; 27 | $this->real = $real; 28 | } 29 | 30 | /** 31 | * @return string 32 | */ 33 | public function getServer() 34 | { 35 | return $this->server; 36 | } 37 | 38 | /** 39 | * @return boolean 40 | */ 41 | public function isTest() 42 | { 43 | return !$this->real; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Event/FeedbackEvent.php: -------------------------------------------------------------------------------- 1 | type = $type; 27 | $this->line = $line; 28 | } 29 | 30 | /** 31 | * @return boolean 32 | */ 33 | public function isError() 34 | { 35 | return $this->type === 'err'; 36 | } 37 | 38 | /** 39 | * @return string 40 | */ 41 | public function getFeedback() 42 | { 43 | return $this->line; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Events.php: -------------------------------------------------------------------------------- 1 | registerNamespaces(array( 76 | // ... 77 | 'BeSimple' => __DIR__.'/../vendor', 78 | // ... 79 | )); 80 | 81 | 82 | How to configure 83 | ---------------- 84 | 85 | 86 | ###An example 87 | 88 | be_simple_deployment: 89 | 90 | rsync: 91 | delete: true 92 | 93 | ssh: 94 | pubkey_file: /home/me/.ssh/id_rsa.pub 95 | privkey_file: /home/me/.ssh/id_rsa 96 | passwphrase: secret 97 | 98 | rules: 99 | eclipse: 100 | ignore: [.settings, .buildpath, .project] 101 | git: 102 | ignore: [.git, .git*, .svn] 103 | symfony: 104 | ignore: [/app/logs/*, /app/cache/*, /web/uploads/*, /web/*_dev.php] 105 | 106 | commands: 107 | cache_warmup: 108 | type: symfony 109 | command: cache:warmup 110 | fix_perms: 111 | type: shell 112 | command: ./bin/fix_perms.sh 113 | 114 | servers: 115 | staging: 116 | host: localhost 117 | username: login 118 | password: passwd 119 | path: /path/to/project 120 | rules: [eclipse, symfony] 121 | commands: [cache_warmup, fix_perms] 122 | production: 123 | # ... 124 | 125 | 126 | ###Rsync configuration 127 | 128 | To be continued. 129 | 130 | 131 | ###SSH configuration 132 | 133 | To be continued. 134 | 135 | 136 | ###Rules configuration 137 | 138 | Rules can be declared as templates for reuse in your servers configuration. 139 | Some templates are already bundled by default. The following parameters can be used : 140 | 141 | - ignore : masks for the files to be ignored 142 | - force : ignored files can be forced this way 143 | 144 | 145 | ###Servers configuration 146 | 147 | Here is the full list of parameters : 148 | 149 | - host : 150 | - rsync_port : 151 | - ssh_port : 152 | - username 153 | - password : 154 | - path : the path for your application root on the remote server 155 | - rules : list of rules templates to apply 156 | - commands : list of commands to trigger on destination server 157 | 158 | 159 | How to use 160 | ---------- 161 | 162 | 163 | ###Using the commands 164 | 165 | The simpliest way to deploy your application is to use the command line, 166 | go into your project root folder and type the following commands : 167 | 168 | # Test your deployment : 169 | ./app/console deployment:test [server] 170 | 171 | # Launch your deployment : 172 | ./app/console deployment:launch [server] 173 | 174 | You can use the verbose option (`-v`) to get all feedback from rsync and 175 | remote ssh commands. 176 | 177 | 178 | ###Using the service 179 | 180 | You can also use the deployment feature within your controller 181 | by invoking the 'deployment' service : 182 | 183 | // Test your deployment : 184 | $this->get('besimple_deployment')->test([$server]); 185 | 186 | // Launch your deployment : 187 | $this->get('besimple_deployment')->launch([$server]); 188 | 189 | You can connect many events to know what's happening. 190 | 191 | ###Deployer events 192 | 193 | - **onDeploymentStart** : fired on deployment start. 194 | - **onDeploymentSuccess** : fired on deployment success. 195 | 196 | 197 | ###Rsync events 198 | 199 | - **onDeploymentRsyncStart** : fired when rsync is started. 200 | - **onDeploymentRsyncFeedback** : fired on each rsync `stdout` or `stderr` line. 201 | - **onDeploymentRsyncSuccess** : fired on rsync success. 202 | 203 | 204 | ###SSH events 205 | 206 | - **onDeploymentSshStart** : fired when SSH command run. 207 | - **onDeploymentSshFeedback** : fired on each SSH `stdout` or `stderr` line. 208 | - **onDeploymentSshSuccess** : fired on SSH command success. 209 | -------------------------------------------------------------------------------- /Resources/config/deployment.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | BeSimple\DeploymentBundle\Deployer\Rsync 9 | BeSimple\DeploymentBundle\Deployer\Ssh 10 | BeSimple\DeploymentBundle\Deployer\Config 11 | BeSimple\DeploymentBundle\Deployer\Logger 12 | BeSimple\DeploymentBundle\Deployer\Deployer 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | %be_simple_deployment.config.rsync% 21 | 22 | 23 | 24 | 25 | 26 | %be_simple_deployment.config.ssh% 27 | 28 | 29 | 30 | %be_simple_deployment.config.rules% 31 | %be_simple_deployment.config.commands% 32 | %be_simple_deployment.config.servers% 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | --------------------------------------------------------------------------------