Merge branch 'master' into native_ssh_multiplexing

This commit is contained in:
Anton Medvedev 2017-01-19 10:33:20 +07:00 committed by GitHub
commit 4535676139
39 changed files with 250 additions and 104 deletions

View File

@ -5,3 +5,5 @@
| BC breaks? | Yes or No
| Deprecations? | Yes or No
| Fixed tickets | N/A or xx
> Do not forget to add notes about your changes to [CHANGELOG.md](https://github.com/deployphp/deployer/blob/master/CHANGELOG.md)

View File

@ -2,3 +2,4 @@ preset: psr2
enabled:
- short_array_syntax
- no_whitespace_in_blank_line

55
CHANGELOG.md Normal file
View File

@ -0,0 +1,55 @@
# Changelog
## master
[v4.0.2...master](https://github.com/deployphp/deployer/compare/v4.0.2...master)
### Added
- Added `testLocally` function (analog `test` fn)
- Added `ConfigurationException`
- Show message on file download
### Changed
- Server config `setPty` renamed to `pty` [#953](https://github.com/deployphp/deployer/pull/953)
- Raised timeout for runLocally to 300 seconds [#955](https://github.com/deployphp/deployer/pull/955)
- `deploy:unlock` now always successful [#950](https://github.com/deployphp/deployer/pull/950)
- Added option `-L` to `setfacl` [#956](https://github.com/deployphp/deployer/pull/956)
- Run `deploy:unlock` when deploy is fail [#958](https://github.com/deployphp/deployer/pull/958)
- Now throw exception on duplicates in `shared_dirs`
### Fixed
- Fixed native ssh scp option
- Fixed bug with `$httpGroup` guard clause [#948](https://github.com/deployphp/deployer/pull/948)
## v4.0.2
[v4.0.1...v4.0.2](https://github.com/deployphp/deployer/compare/v4.0.1...v4.0.2)
### Fixed
- Fixed bug with copy shared files
- Fixed recursive upload in native ssh
- Improved Laravel recipe
- Improved exceptions in runLocally
## v4.0.1
[v4.0.0...v4.0.1](https://github.com/deployphp/deployer/compare/v4.0.0...v4.0.1)
### Added
- Added more writable modes
### Changed
- Allowed init command overriding
- Returned ACL as default writable mode
### Fixed
- Fixed SilverStripe recipe
- Fixed release sorting
- Fixed release cleanup
- Improved Symfony recipe
- Fixed `DotArray` syntax in `Collection`
- Fixed typo3 recipe
- Fixed remove of shared dir on first deploy
## v4.0.0
🙄

View File

@ -1,6 +1,6 @@
# Contributing to Deployer
Thank you for considering contributing to `Deployer`. Please make sure to read the following sections if you plan on submitting new issues or pull requests.
Thank you for considering contributing to Deployer. Please make sure to read the following sections if you plan on submitting new issues or pull requests.
# Bug
@ -43,3 +43,4 @@ possible:
The whole table must be included (do **not** remove lines that you think are
not relevant).
Add notes about your changes to [CHANGELOG.md](https://github.com/deployphp/deployer/blob/master/CHANGELOG.md).

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright © 2013-2016 Medvedev Anton
Copyright © 2013-2017 Medvedev Anton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -51,31 +51,17 @@ composer require deployer/deployer
## Documentation
Documentation source can be found in [deployphp/docs](https://github.com/deployphp/docs) repository.
## Community
Discuss Deployer here [deployer.org/discuss](https://deployer.org/discuss).
## Roadmap
* Better documentation.
* Better DX with intelligible errors.
* Better parallel task runner based on https://github.com/icicleio/icicle
* Task grouping for parallel execution.
* Task combining for less ssh calls.
* Implement `dep status` command with health-check of running application and deployment log.
* Task combining for less ssh calls.
* Implement `dep status` command with health-check of running application and deployment log.
* More deploy strategies.
* More integration with third-party services.
* Web-based client.
## Contributing
All code contributions must go through a pull request and approved by a core developer before being merged.
This is to ensure proper review of all the code.
Fork the project, create a feature branch, and send a pull request.
To ensure a consistent code base, you should make sure the code follows
the [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md).
If you would like to help take a look at the [list of issues](https://github.com/deployphp/deployer/issues).
## Maintainers
* Anton Medvedev [@elfet](https://github.com/elfet)
* Oanh Nguyen [@oanhnn](https://github.com/oanhnn)

View File

@ -3,47 +3,47 @@
1. Namespace for functions
Add to beginning of *deploy.php* next line:
```php
use function Deployer\{server, task, run, set, get, add};
use function Deployer\{server, task, run, set, get, add, before, after};
```
If you are using PHP version less than 5.6, you can use this:
```php
namespace Deployer;
```
2. `env()` to `set()`/`get()`
Rename all calls `env($name, $value)` to `set($name, $value)`.
Rename all rvalue `env($name)` to `get($name)`.
Rename all `server(...)->env(...)` to `server(...)->set(...)`.
3. Moved *NonFatalException*
Rename `Deployer\Task\NonFatalException` to `Deployer\Exception\NonFatalException`.
4. Prior release cleanup
Due to changes in release management, the new cleanup task will ignore any prior releases deployed with 3.x. These will need to be manually removed after migrating to and successfully releasing via 4.x.
# Upgrade from 2.x to 3.x
1. ### `->path('...')`
1. ### `->path('...')`
Replace your server paths configuration:
```php
server(...)
->path(...);
```
to:
```php
```php
server(...)
->env('deploy_path', '...');
```

6
build
View File

@ -1,8 +1,8 @@
#!/usr/bin/env php
<?php
/* __ ___ __
* | \ |__ |__)
* |__/ |___ |
/* __ ___ __
* | \ |__ |__)
* |__/ |___ |
*/
require __DIR__ . '/vendor/autoload.php';

View File

@ -1,11 +1,11 @@
# Deployer Recipes
`recipe` contains officially supported Deployer recipes.
All of them based of `common.php` recipe which contains tasks for deployment environment preparation,
`recipe` contains officially supported Deployer recipes.
All of them based of `common.php` recipe which contains tasks for deployment environment preparation,
loading code, changing files permissions, and much more.
Other recipes can be found in [github.com/deployphp/recipes](https://github.com/deployphp/recipes).
To add support for framework or app create new file, require `recipe/common.php`, and describe `deploy` task.
To add support for framework or app create new file, require `recipe/common.php`, and describe `deploy` task.
Take a look of example of `composer.php` recipe.

View File

@ -141,4 +141,6 @@ task('success', function () {
*/
task('deploy:failed', function () {
})->setPrivate();
onFailure('deploy', 'deploy:failed');
before('deploy:failed', 'deploy:unlock');

View File

@ -23,5 +23,5 @@ task('deploy:lock', function () {
desc('Unlock deploy');
task('deploy:unlock', function () {
run("rm {{deploy_path}}/.dep/deploy.lock");
run("rm -f {{deploy_path}}/.dep/deploy.lock");//always success
});

View File

@ -7,10 +7,21 @@
namespace Deployer;
use Deployer\Exception\ConfigurationException;
desc('Creating symlinks for shared files and dirs');
task('deploy:shared', function () {
$sharedPath = "{{deploy_path}}/shared";
// Validate shared_dir, find duplicates
foreach (get('shared_dirs') as $a) {
foreach (get('shared_dirs') as $b) {
if ($a !== $b && strpos($a, $b) === 0) {
throw new ConfigurationException("Can not share same dirs `$a` and `$b`.");
}
}
}
foreach (get('shared_dirs') as $dir) {
// Check if shared dir does not exists.
if (!test("[ -d $sharedPath/$dir ]")) {

View File

@ -43,7 +43,7 @@ task('deploy:writable', function () {
// -R operate on files and directories recursively
// -L if a command line argument is a symbolic link to a directory, traverse it
$httpGroup = get('http_group', false);
if ($httpUser === false) {
if ($httpGroup === false) {
throw new \RuntimeException("Please setup `http_group` config parameter.");
}
run("$sudo chgrp -RH $httpGroup $dirs");
@ -57,8 +57,8 @@ task('deploy:writable', function () {
run("$sudo chmod +a \"`whoami` allow delete,write,append,file_inherit,directory_inherit\" $dirs");
} elseif (commandExist('setfacl')) {
if (!empty($sudo)) {
run("$sudo setfacl -R -m u:\"$httpUser\":rwX -m u:`whoami`:rwX $dirs");
run("$sudo setfacl -dR -m u:\"$httpUser\":rwX -m u:`whoami`:rwX $dirs");
run("$sudo setfacl -RL -m u:\"$httpUser\":rwX -m u:`whoami`:rwX $dirs");
run("$sudo setfacl -dRL -m u:\"$httpUser\":rwX -m u:`whoami`:rwX $dirs");
} else {
// When running without sudo, exception may be thrown
// if executing setfacl on files created by http user (in directory that has been setfacl before).
@ -70,8 +70,8 @@ task('deploy:writable', function () {
$hasfacl = run("getfacl -p $dir | grep \"^user:$httpUser:.*w\" | wc -l")->toString();
// Set ACL for directory if it has not been set before
if (!$hasfacl) {
run("setfacl -R -m u:\"$httpUser\":rwX -m u:`whoami`:rwX $dir");
run("setfacl -dR -m u:\"$httpUser\":rwX -m u:`whoami`:rwX $dir");
run("setfacl -RL -m u:\"$httpUser\":rwX -m u:`whoami`:rwX $dir");
run("setfacl -dRL -m u:\"$httpUser\":rwX -m u:`whoami`:rwX $dir");
}
}
}

View File

@ -49,7 +49,7 @@ class BootstrapByConfigFile
* @var BuilderInterface[] $clusterBuilders
*/
public $clusterBuilders = [];
/**
* @var BuilderInterface[] $serverBuilders
*/

View File

@ -72,4 +72,12 @@ interface BuilderInterface
* @return BuilderInterface
*/
public function set($name, $value);
/**
* Use pty connection
*
* @param $pty
* @return BuilderInterface
*/
public function pty($pty);
}

View File

@ -17,17 +17,17 @@ use Deployer\Deployer;
*/
class Cluster implements ClusterInterface
{
/**
* @var string $name
*/
protected $name = null;
/**
* @var array $nodes
*/
protected $nodes = null;
/**
* @var string|int $port
*/
@ -54,16 +54,16 @@ class Cluster implements ClusterInterface
$this->name = $name;
$this->port = $port;
foreach ($nodes as $key => $host) {
$nName = $name . '_' . $key;
$node = new Node();
$node->setDeployer($deployer)
->setName($nName)
->setHost($host)
->setPort($port);
$node->initialize();
$this->nodes[] = $node;
}
@ -77,7 +77,7 @@ class Cluster implements ClusterInterface
{
return $this->clusterBuilder;
}
/**
* @return array|\Deployer\Cluster\Node[]
*/

View File

@ -120,4 +120,15 @@ class ClusterBuilder implements BuilderInterface
}
return $this;
}
/**
* {@inheritdoc}
*/
public function pty($stages)
{
foreach ($this->nodes as $node) {
$node->builder->pty($stages);
}
return $this;
}
}

View File

@ -59,14 +59,14 @@ class Node implements NodeInterface
{
$env = new Environment();
$config = new Configuration($this->name, $this->host, $this->port);
$this->server = new PhpSecLib($config);
if (\Deployer\has('ssh_type') && \Deployer\get('ssh_type') === 'ext-ssh2') {
$this->server = new SshExtension($config);
}
$this->builder = new Builder($config, $env);
$this->deployer->servers->set($this->name, $this->server);
$this->deployer->environments->set($this->name, $env);

View File

@ -21,7 +21,7 @@ class Application extends Console
* @var InputDefinition
*/
private $userDefinition;
/**
* {@inheritdoc}
*/
@ -77,7 +77,7 @@ class Application extends Console
if (null === $this->userDefinition) {
$this->userDefinition = new InputDefinition();
}
return $this->userDefinition;
}

View File

@ -16,7 +16,7 @@ class OutputWatcher implements OutputInterface
* @var OutputInterface
*/
private $output;
/**
* @var bool
*/

View File

@ -0,0 +1,12 @@
<?php
/* (c) Anton Medvedev <anton@medv.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Deployer\Exception;
class ConfigurationException extends \RuntimeException
{
}

View File

@ -141,7 +141,7 @@ class ParallelExecutor implements ExecutorInterface
{
$this->userDefinition = $userDefinition;
}
/**
* {@inheritdoc}
*/
@ -227,7 +227,7 @@ class ParallelExecutor implements ExecutorInterface
$input .= " --{$option->getName()} $value";
}
}
foreach ($this->servers as $serverName => $server) {
$process = new Process(
"php " . DEPLOYER_BIN .
@ -279,7 +279,7 @@ class ParallelExecutor implements ExecutorInterface
// We got some exception, so not.
$this->isSuccessfullyFinished = false;
if ($exceptionClass == 'Deployer\Exception\NonFatalException') {
// If we got NonFatalException, continue other tasks.
@ -365,7 +365,7 @@ class ParallelExecutor implements ExecutorInterface
} else {
$this->informer->taskError($this->hasNonFatalException);
}
// We waited all workers to finish their tasks.
// Wait no more!
$this->wait = false;

View File

@ -33,7 +33,8 @@ add('writable_dirs', []);
server('production', 'domain.com')
->user('username')
->identityFile()
->set('deploy_path', '/var/www/domain.com');
->set('deploy_path', '/var/www/domain.com')
->pty(true);
// Tasks

View File

@ -27,7 +27,7 @@ class SymfonyTemplate extends FrameworkTemplate
return <<<PHP
// Migrate database before symlink new release.
before('deploy:symlink', 'database:migrate');
PHP;

View File

@ -224,10 +224,24 @@ class Builder implements BuilderInterface
));
}
/**
* Use pty connection
*
* @param $pty
* @return BuilderInterface
*/
public function pty($pty)
{
$this->config->setPty($pty);
return $this;
}
/**
* Use pty in ssh2 connection
*
* @param $ssh2Pty
* @deprecated
* @return BuilderInterface
*/
public function ssh2Pty($ssh2Pty)

View File

@ -102,11 +102,11 @@ class Configuration
private $pemFile;
/**
* Pty configuration for ssh2 connection
* Pty configuration
*
* @var mixed
*/
private $ssh2Pty = null;
private $pty = null;
/**
@ -422,22 +422,44 @@ class Configuration
}
/**
* Set pty for ssh2 connection
* Set pty
*
* @param $ssh2Pty
* @param $pty
*/
public function setSsh2Pty($ssh2Pty)
public function setPty($pty)
{
$this->ssh2Pty = $ssh2Pty;
$this->pty = $pty;
}
/**
* Get pty option for ssh2 connection
* Get pty option
*
* @return mixed
*/
public function getPty()
{
return $this->pty;
}
/**
* Set pty for ssh2 connection. For retro compatibility
*
* @param $ssh2Pty
* @deprecated
*/
public function setSsh2Pty($ssh2Pty)
{
$this->setPty($ssh2Pty);
}
/**
* Get pty option for ssh2 connection. For retro compatibility
*
* @deprecated
* @return mixed
*/
public function getSsh2Pty()
{
return $this->ssh2Pty;
return $this->getPty();
}
}

View File

@ -74,7 +74,7 @@ class Environment
private function checkIfNameIsProtected($name)
{
$length = strlen($name);
foreach ($this->protectedNames as $protectedName) {
$len = strlen($protectedName);
if ($name === $protectedName) {

View File

@ -73,10 +73,15 @@ class NativeSsh implements ServerInterface
$sshOptions[] = '-i ' . escapeshellarg($serverConfig->getPrivateKey());
}
if ($serverConfig->getPty()) {
$sshOptions[] = '-t';
}
$sshCommand = 'ssh ' . implode(' ', $sshOptions) . ' ' . escapeshellarg($username . $hostname) . ' ' . escapeshellarg($command);
$process = new Process($sshCommand);
$process
->setPty($serverConfig->getPty())
->setTimeout(null)
->setIdleTimeout(null)
->mustRun();

View File

@ -113,7 +113,7 @@ class SshExtension implements ServerInterface
{
$this->checkConnection();
$pty = $this->getConfiguration()->getSsh2Pty();
$pty = $this->getConfiguration()->getPty();
return $this->session->getExec()->run($command, $pty);
}

View File

@ -68,7 +68,7 @@ class Context
{
return end(self::$contexts);
}
/**
* @return Context
*/

View File

@ -331,7 +331,7 @@ function run($command)
* @return Result Output of command.
* @throws \RuntimeException
*/
function runLocally($command, $timeout = 60)
function runLocally($command, $timeout = 300)
{
$command = parse($command);
@ -378,6 +378,20 @@ function test($command)
return run("if $command; then echo 'true'; fi")->toBool();
}
/**
* Run test command locally.
* Example:
*
* testLocally('[ -d {{local_release_path}} ]')
*
* @param string $command
* @return bool
*/
function testLocally($command)
{
return runLocally("if $command; then echo 'true'; fi")->toBool();
}
/**
* Upload file or directory to current server.
* @param string $local
@ -433,6 +447,7 @@ function download($local, $remote)
$local = parse($local);
$remote = parse($remote);
writeln("Download file <info>$remote</info> to <info>$local</info>");
$server->download($local, $remote);
}

View File

@ -22,7 +22,7 @@ test:
deploy_path: /home
run_test: true
istanbul_dc_cluster:
istanbul_dc_cluster:
nodes: [192.168.0.1, 192.168.0.2, 192.168.0.3]
user: test
password: qwerty

View File

@ -14,7 +14,7 @@ class BootstrapByConfigFileTest extends \PHPUnit_Framework_TestCase
* @var string|null $configFile;
*/
protected $configFile = null;
/**
* @var BootstrapByConfigFile | null $bootstrap
*/
@ -37,7 +37,7 @@ class BootstrapByConfigFileTest extends \PHPUnit_Framework_TestCase
unset($this->configFile);
unset($this->bootstrap);
}
/**
* tests BootstrapByConfigfile::setConfig()
*/
@ -86,7 +86,7 @@ class BootstrapByConfigFileTest extends \PHPUnit_Framework_TestCase
$this->assertInstanceOf('Deployer\Bootstrap\BootstrapByConfigFile', $bootstrap);
$this->assertContainsOnlyInstancesOf('Deployer\Builder\BuilderInterface', $bootstrap->serverBuilders);
}
/**
* tests BootstrapByConfigFile::initServers()
*/

View File

@ -29,16 +29,16 @@ class ClusterFactoryTest extends \PHPUnit_Framework_TestCase
$this->deployer = new Deployer($app, $input, $output);
}
public function tearDown()
{
unset($this->deployer);
}
/**
* test create function of the factory
*/
public function testCreate()
{
$instance = ClusterFactory::create(

View File

@ -12,7 +12,7 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
public function testApplication()
{
$app = new Application();
$this->assertTrue($app->getDefinition()->hasOption('file'));
}
}

View File

@ -15,7 +15,7 @@ class OutputWatcherTest extends \PHPUnit_Framework_TestCase
public function testOutputWatcher()
{
$output = $this->createMock('Symfony\Component\Console\Output\OutputInterface');
$output->expects($this->any())
->method('write');
@ -37,28 +37,28 @@ class OutputWatcherTest extends \PHPUnit_Framework_TestCase
$output->expects($this->once())
->method('getFormatter');
$ow = new OutputWatcher($output);
$ow->write('test');
$this->assertTrue($ow->getWasWritten());
$ow->writeln('test');
$ow->setVerbosity(OutputInterface::VERBOSITY_NORMAL);
$this->assertEquals(OutputInterface::VERBOSITY_NORMAL, $ow->getVerbosity());
$ow->setDecorated(true);
$ow->isDecorated();
$ow->setFormatter($this->createMock('Symfony\Component\Console\Formatter\OutputFormatterInterface'));
$ow->getFormatter();
$ow->setWasWritten(false);
$this->assertFalse($ow->getWasWritten());

View File

@ -12,7 +12,7 @@ use Deployer\Console\Application;
class DeployerTest extends \PHPUnit_Framework_TestCase
{
private $deployer;
protected function setUp()
{
$console = new Application();

View File

@ -94,16 +94,16 @@ class FunctionsTest extends \PHPUnit_Framework_TestCase
$deployer = $this->deployer;
cluster('myIstanbulDCCluster', ['192.168.0.1', '192.168.0.2'], 22);
$server0 = $deployer->servers->get('myIstanbulDCCluster_0');
$env0 = $deployer->environments->get('myIstanbulDCCluster_0');
$server1 = $deployer->servers->get('myIstanbulDCCluster_1');
$env1 = $deployer->environments->get('myIstanbulDCCluster_1');
$this->assertInstanceOf('Deployer\Server\ServerInterface', $server0);
$this->assertInstanceOf('Deployer\Server\Environment', $env0);
$this->assertInstanceOf('Deployer\Server\ServerInterface', $server1);
$this->assertInstanceOf('Deployer\Server\Environment', $env1);
}

View File

@ -17,14 +17,14 @@ class ContextTest extends \PHPUnit_Framework_TestCase
$output = $this->getMockBuilder('Symfony\Component\Console\Output\OutputInterface')->disableOriginalConstructor()->getMock();
$context = new Context($server, $env, $input, $output);
$this->assertInstanceOf('Deployer\Server\ServerInterface', $context->getServer());
$this->assertInstanceOf('Deployer\Server\Environment', $context->getEnvironment());
$this->assertInstanceOf('Symfony\Component\Console\Input\InputInterface', $context->getInput());
$this->assertInstanceOf('Symfony\Component\Console\Output\OutputInterface', $context->getOutput());
Context::push($context);
$this->assertEquals($context, Context::get());
$this->assertEquals($context, Context::pop());
}