diff --git a/.gitignore b/.gitignore index 4fcb04e..71e592a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/.idea /vendor /dev composer.lock diff --git a/bin/phploy.phar b/bin/phploy.phar index 4ac25ac..0e5797a 100755 Binary files a/bin/phploy.phar and b/bin/phploy.phar differ diff --git a/phploy.ini b/phploy.ini index 4d51290..711b561 100644 --- a/phploy.ini +++ b/phploy.ini @@ -1,4 +1,4 @@ -; This is a sample deploy.ini file. You can specify as many +; This is a sample phploy.ini file. You can specify as many ; servers as you need and use normal or quickmode configuration. ; ; NOTE: If a value in the .ini file contains any non-alphanumeric @@ -24,7 +24,7 @@ purge[] = "cache/" pre-deploy[] = "wget http://staging-example.com/pre-deploy/test.php --spider --quiet" post-deploy[] = "wget http://staging-example.com/post-deploy/test.php --spider --quiet" - + [production] quickmode = ftp://example:password@production-example.com:21/path/to/installation passive = true @@ -34,6 +34,6 @@ skip[] = 'libs/*' skip[] = 'config/*' skip[] = 'src/*.scss' - purge[] = "cache/" + purge[] = "cache/" pre-deploy[] = "wget http://staging-example.com/pre-deploy/test.php --spider --quiet" post-deploy[] = "wget http://staging-example.com/post-deploy/test.php --spider --quiet" diff --git a/readme.md b/readme.md index af19d68..1c5e404 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,5 @@ # PHPloy -**Version 4.1.3** +**Version 4.2** PHPloy is an incremental Git FTP and SFTP deployment tool. By keeping track of the state of the remote server(s) it deploys only the files that were committed since the last deployment. PHPloy supports submodules, sub-submodules, deploying to multiple servers and rollbacks. PHPloy requires **PHP 5.4+** and **Git 1.8+**. @@ -79,6 +79,17 @@ The `phploy.ini` file holds your project configuration. It should be located in ``` If your password is missing in the `phploy.ini` file, PHPloy will interactively ask you for your password. +There is also an option to store the password in a file called `.phploy`. + +``` +[staging] + password=password + +[production] + password=password +``` + +This feature is especially useful if you would like to share your phploy.ini via Git but hide your password from the public. ## Multiple servers diff --git a/src/Connection.php b/src/Connection.php index f086029..fab48e9 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -6,10 +6,25 @@ use League\Flysystem\Filesystem; use League\Flysystem\Sftp\SftpAdapter as SftpAdapter; +/** + * Class Connection. + */ class Connection { + /** + * @var Filesystem + */ public $server; + /** + * Connection constructor. + * + * @param string $server + * + * @return Connection + * + * @throws \Exception + */ public function __construct($server) { if (!isset($server['scheme'])) { @@ -30,7 +45,9 @@ public function __construct($server) * * @param string $server * - * @throws Exception if it can't connect to FTP server + * @return Filesystem|null + * + * @throws \Exception if it can't connect to FTP server */ protected function connectToFtp($server) { @@ -47,6 +64,8 @@ protected function connectToFtp($server) } catch (\Exception $e) { echo "\r\nOh Snap: {$e->getMessage()}\r\n"; } + + return; } /** @@ -54,7 +73,9 @@ protected function connectToFtp($server) * * @param string $server * - * @throws Exception if it can't connect to FTP server + * @return Filesystem|null + * + * @throws \Exception if it can't connect to FTP server */ protected function connectToSftp($server) { @@ -71,5 +92,7 @@ protected function connectToSftp($server) } catch (\Exception $e) { echo "\r\nOh Snap: {$e->getMessage()}\r\n"; } + + return; } } diff --git a/src/Git.php b/src/Git.php index 7a41fb7..9ef9bbd 100644 --- a/src/Git.php +++ b/src/Git.php @@ -2,6 +2,9 @@ namespace Banago\PHPloy; +/** + * Class Git. + */ class Git { /** @@ -19,6 +22,11 @@ class Git */ protected $repo; + /** + * Git constructor. + * + * @param null $repo + */ public function __construct($repo = null) { $this->repo = $repo; @@ -29,6 +37,8 @@ public function __construct($repo = null) /** * Executes a console command and returns the output (as an array). * + * @param string $command Command to execute + * * @return array of all lines that were output to the console during the command (STDOUT) */ public function exec($command) @@ -60,6 +70,14 @@ public function command($command, $repoPath = null) return $this->exec($command); } + /** + * Diff versions. + * + * @param string $remoteRevision + * @param string $localRevision + * + * @return array + */ public function diff($remoteRevision, $localRevision) { if (empty($remoteRevision)) { @@ -74,6 +92,13 @@ public function diff($remoteRevision, $localRevision) return $this->command($command); } + /** + * Checkout given $branch. + * + * @param string $branch + * + * @return array + */ public function checkout($branch) { $command = 'checkout '.$branch; diff --git a/src/Options.php b/src/Options.php index 59c77d8..d1a59a6 100644 --- a/src/Options.php +++ b/src/Options.php @@ -2,10 +2,23 @@ namespace Banago\PHPloy; +use League\CLImate\CLImate; + +/** + * Class Options. + */ class Options { + /** + * @var CLImate + */ public $cli; + /** + * Options constructor. + * + * @param CLImate $climate + */ public function __construct($climate) { $this->cli = $climate; @@ -14,6 +27,9 @@ public function __construct($climate) $this->parse(); } + /** + * + */ protected function build() { $this->cli->description('PHPloy - Incremental Git FTP/SFTP deployment tool that supports multiple servers, submodules and rollbacks.'); @@ -70,6 +86,9 @@ protected function build() ]); } + /** + * + */ protected function parse() { $this->cli->arguments->parse(); diff --git a/src/PHPloy.php b/src/PHPloy.php index e90a55e..b9e3638 100644 --- a/src/PHPloy.php +++ b/src/PHPloy.php @@ -8,7 +8,7 @@ * @link https://github.com/banago/PHPloy * @licence MIT Licence * - * @version 4.1.3 + * @version 4.2 */ namespace Banago\PHPloy; @@ -18,7 +18,7 @@ class PHPloy /** * @var string */ - protected $version = '4.1'; + protected $version = '4.2'; /** * @var string @@ -26,12 +26,12 @@ class PHPloy public $revision = 'HEAD'; /** - * @var string + * @var \League\CLImate\CLImate */ public $cli; /** - * @var string + * @var Git */ public $git; @@ -135,7 +135,14 @@ class PHPloy public $iniFilename = 'phploy.ini'; /** - * @var bool|resource + * The filename from which to read server password. + * + * @var string + */ + public $passFile = '.phploy'; + + /** + * @var \League\Flysystem\Filesystem; */ protected $connection = null; @@ -297,23 +304,25 @@ public function setup() } /** - * Parse Credentials. + * Parse an ini file and return values as array. * - * @param string $deploy The filename to obtain the list of servers from, normally $this->iniFilename + * @param string $iniFile * - * @return array of servers listed in the file $deploy + * @return array + * + * @throws \Exception */ - public function parseCredentials($iniFile) + public function parseIniFile($iniFile) { if (!file_exists($iniFile)) { throw new \Exception("'$iniFile' does not exist."); } else { - $servers = parse_ini_file($iniFile, true); + $values = parse_ini_file($iniFile, true); - if (!$servers) { + if (!$values) { throw new \Exception("'$iniFile' is not a valid .ini file."); } else { - return $servers; + return $values; } } } @@ -344,7 +353,7 @@ public function prepareServers() $iniFile = $this->repo.DIRECTORY_SEPARATOR.$this->iniFilename; - $servers = $this->parseCredentials($iniFile); + $servers = $this->parseIniFile($iniFile); foreach ($servers as $name => $options) { @@ -393,17 +402,49 @@ public function prepareServers() $this->postDeploy[$name] = $servers[$name]['post-deploy']; } - // Ask for a password if it is empty and a private key is not provided + // Ask for a password if it is empty and a private key is not provided if ($options['pass'] === '' && $options['privkey'] === '') { - fwrite(STDOUT, 'No password has been provided for user "'.$options['user'].'". Please enter a password: '); - $options['pass'] = $this->getPassword(); - $this->cli->lightGreen()->out("\r\n" . 'Password received. Continuing deployment ...'); + // Look for .phploy config file + if (file_exists($this->getPasswordFile())) { + $options['pass'] = $this->getPasswordFromIniFile($name); + } else { + fwrite(STDOUT, 'No password has been provided for user "'.$options['user'].'". Please enter a password: '); + $options['pass'] = $this->getPassword(); + $this->cli->lightGreen()->out("\r\n" . 'Password received. Continuing deployment ...'); + } } $this->servers[$name] = $options; } } + /** + * Returns the full path to password file. + * + * @return string + */ + public function getPasswordFile() + { + return $this->repo.DIRECTORY_SEPARATOR.$this->passFile; + } + + /** + * Try to fetch password from .phploy file if not found, an empty string will be returned. + * + * @param string $servername Server to fetch password for + * + * @return string + */ + public function getPasswordFromIniFile($servername) + { + $values = $this->parseIniFile($this->getPasswordFile()); + if (isset($values[$servername]['password']) === true) { + return $values[$servername]['password']; + } + + return ''; + } + /** * Gets the password from user input, hiding password and replaces it * with stars (*) if user users Unix / Mac. @@ -592,6 +633,8 @@ public function deploy() * * @param int $bytes * @param int $decimals + * + * @return string */ public function humanFilesize($bytes, $decimals = 2) { @@ -606,6 +649,8 @@ public function humanFilesize($bytes, $decimals = 2) * * @param string $pattern * @param string $string + * + * @return string */ public function patternMatch($pattern, $string) { @@ -650,18 +695,15 @@ public function listFiles($files) * * @param string $localRevision * - * @throws Exception if unknown git diff status + * @throws \Exception if unknown git diff status * * @return array */ public function compare($localRevision) { $remoteRevision = null; - $tmpFile = tmpfile(); $filesToUpload = []; $filesToDelete = []; - $filesToSkip = []; - $output = []; if ($this->currentSubmoduleName) { $this->dotRevision = $this->currentSubmoduleName.'/'.$this->dotRevisionFilename; @@ -876,14 +918,14 @@ public function push($files) } // If $this->revision is not HEAD, it means the rollback command was provided - // The working copy was rolled back earlier to run the deployment, and we + // The working copy was rolled back earlier to run the deployment, and we // now want to return the working copy back to its original state. if ($this->revision != 'HEAD') { $this->git->command('checkout '.($initialBranch ?: 'master')); } $this->log('[SHA: '.$this->localRevision.'] Deployment to server: "'.$this->currentlyDeploying.'" from branch "'. - $initialBranch.'". '.count($filesToUpload).' files uploaded; '.count($filesToDelete).' files deleted.'); + $initialBranch.'". '.count($filesToUpload).' files uploaded; '.count($filesToDelete).' files deleted.'); } /** @@ -1034,7 +1076,7 @@ public function purge($purgeDirs) $this->connection->delete($item['path']); $this->cli->out(" × {$item['path']} is removed from directory"); } elseif ($item['type'] === 'dir') { - // Directories need to be stacked to be + // Directories need to be stacked to be // deleted at the end when they are empty $innerDirs[] = $item['path']; } @@ -1059,7 +1101,7 @@ public function purge($purgeDirs) public function copy($copyDirs) { $dirNameTrimFunc = function ($name) { - return rtrim(str_replace('\\', '/', trim($name)), '/'); + return rtrim(str_replace('\\', '/', trim($name)), '/'); }; foreach ($copyDirs as $copyRule) { @@ -1095,7 +1137,7 @@ public function copy($copyDirs) $this->cli->out(" × {$item['path']} is copied to {$newPath}"); } elseif ($item['type'] === 'dir') { $dirParts = explode('/', $item['path']); - $this->copy(array($fromDir.'/'.end($dirParts).'->'.$toDir.'/'.end($dirParts))); + $this->copy([$fromDir.'/'.end($dirParts).'->'.$toDir.'/'.end($dirParts)]); } } @@ -1135,6 +1177,8 @@ public function postDeploy(array $commands) * Checks for deleted directories. Git cares only about files. * * @param array $filesToDelete + * + * @return array */ public function hasDeletedDirectories($filesToDelete) { @@ -1195,11 +1239,13 @@ public function debug($message) * @param bool $recursive Include sub directories * @param bool $listDirs Include directories on listing * @param bool $listFiles Include files on listing - * @param regex $exclude Exclude paths that matches this regex + * @param string $exclude Exclude paths that matches this regex + * + * @return array */ public function directoryToArray($directory, $recursive = true, $listDirs = false, $listFiles = true, $exclude = '') { - $arrayItems = array(); + $arrayItems = []; $skipByExclude = false; $handle = opendir($directory); if ($handle) { @@ -1233,6 +1279,10 @@ public function directoryToArray($directory, $recursive = true, $listDirs = fals /** * Strip Absolute Path. + * + * @param string $el + * + * @return string */ protected function relPath($el) { @@ -1249,7 +1299,7 @@ protected function createSampleIniFile() $data = "; NOTE: If non-alphanumeric characters are present, enclose in value in quotes.\n [staging] quickmode = ftp://example:password@production-example.com:21/path/to/installation\n -[staging] +[production] scheme = sftp user = example pass = password @@ -1264,7 +1314,7 @@ protected function createSampleIniFile() /** * Log a message to file. - * + * * @param string $message The message to write * @param string $type The type of log message (e.g. INFO, DEBUG, ERROR, etc.) */