diff --git a/setup/cli/modules/class.module.php b/setup/cli/modules/class.module.php index 70c2eebebd730d4aa18f901607c6f00c96f28fed..bfe32969b9e891d87e27f99a96aacfe1c64e8bfd 100644 --- a/setup/cli/modules/class.module.php +++ b/setup/cli/modules/class.module.php @@ -34,7 +34,7 @@ class Option { function handleValue(&$destination, $args) { $nargs = 0; - $value = array_shift($args); + $value = ($this->hasArg()) ? array_shift($args) : null; if ($value[0] == '-') $value = null; elseif ($value) @@ -62,7 +62,7 @@ class Option { function toString() { $short = explode(':', $this->short); $long = explode(':', $this->long); - if ($this->nargs == '?') + if ($this->nargs === '?') $switches = sprintf(' %s [%3$s], %s[=%3$s]', $short[0], $long[0], $this->metavar); elseif ($this->hasArg()) @@ -80,6 +80,21 @@ class Option { } } +class OutputStream { + var $stream; + + function OutputStream() { + call_user_func_array(array($this, '__construct'), func_get_args()); + } + function __construct($stream) { + $this->stream = fopen($stream, 'w'); + } + + function write($what) { + fwrite($this->stream, $what); + } +} + class Module { var $options = array(); @@ -89,6 +104,9 @@ class Module { var $usage = '$script [options] $args [arguments]'; var $autohelp = true; + var $stdout; + var $stderr; + var $_options; var $_args; @@ -102,6 +120,8 @@ class Module { 'help'=>"Display this help message"); foreach ($this->options as &$opt) $opt = new Option($opt); + $this->stdout = new OutputStream('php://output'); + $this->stderr = new OutputStream('php://stderr'); } function showHelp() { diff --git a/setup/cli/modules/deploy.php b/setup/cli/modules/deploy.php new file mode 100644 index 0000000000000000000000000000000000000000..585f5314a02f8851f8814bd1bdcae561b48b29ca --- /dev/null +++ b/setup/cli/modules/deploy.php @@ -0,0 +1,59 @@ +<?php +require_once dirname(__file__) . "/class.module.php"; +require_once dirname(__file__) . "/unpack.php"; + +class Deployment extends Unpacker { + var $prologue = "Deploys osTicket into target install path"; + + var $epilog = + "Deployment is used from the continuous development model. If you + are following the upstream git repo, then you can use the deploy + script to deploy changes made by you or upstream development to your + installation target"; + + function find_root_folder() { + # Hop up to the root folder of this repo + $start = dirname(__file__); + for (;;) { + if (is_file($start . '/main.inc.php')) break; + $start .= '/..'; + } + return realpath($start); + } + + function run($args, $options) { + $this->destination = $args['install-path']; + if (!is_dir($this->destination)) + if (!@mkdir($this->destination, 0751, true)) + die("Destination path does not exist and cannot be created"); + $this->destination = realpath($this->destination).'/'; + + # Determine if this is an upgrade, and if so, where the include/ + # folder is currently located + $upgrade = file_exists("{$this->destination}/main.inc.php"); + + # Get the current value of the INCLUDE_DIR before overwriting + # main.inc.php + $include = ($upgrade) ? $this->get_include_dir() + : ($options['include'] ? $options['include'] + : "{$this->destination}/include"); + if (substr($include, -1) !== '/') + $include .= '/'; + + # Locate the upload folder + $root = $this->find_root_folder(); + + # Unpack everything but the include/ folder + $this->unpackage("$root/{,.}*", $this->destination, -1, + array("$root/setup", "$root/include", "$root/.git*", + "*.sw[a-z]","*.md", "*.txt")); + # Unpack the include folder + $this->unpackage("$root/include/{,.}*", $include, -1, + array("*/include/ost-config.php")); + if (!$upgrade && $include != "{$this->destination}/include") + $this->change_include_dir($include); + } +} + +Module::register('deploy', 'Deployment'); +?> diff --git a/setup/cli/modules/unpack.php b/setup/cli/modules/unpack.php index fca8f0ac2405686d72c624786ccefa6a1cac4ebd..3c66703ef90e0f63d23b2e10aa26e84f6948a35a 100644 --- a/setup/cli/modules/unpack.php +++ b/setup/cli/modules/unpack.php @@ -21,6 +21,9 @@ class Unpacker extends Module { code in that folder. The folder will be automatically created if it doesn't already exist." ), + 'verbose' => array('-v','--verbose', 'default'=>false, 'nargs'=>0, + 'action'=>'store_true', 'help'=> + "Move verbose logging to stdout"), ); var $arguments = array( @@ -45,9 +48,9 @@ class Unpacker extends Module { # Read the main.inc.php script $main_inc_php = $this->destination . '/main.inc.php'; $lines = explode("\n", file_get_contents($main_inc_php)); - # Try and use ROOT_PATH + # Try and use ROOT_DIR if (strpos($include_path, $this->destination) === 0) - $include_path = "ROOT_PATH . '" . + $include_path = "ROOT_DIR . '" . str_replace($this->destination, '', $include_path) . "'"; else $include_path = "'$include_path'"; @@ -78,23 +81,49 @@ class Unpacker extends Module { return false; } - function unpackage($folder, $destination, $recurse=true, $exclude=false) { + /** + * Copy from source to desination, perhaps recursing up to n folders. + * Exclusions are also permitted. If any files match an MD5 sum, they + * will be excluded from the copy operation. + * + * Parameters: + * folder - (string) source folder root + * destination - (string) destination folder root + * recurse - (int) recuse up to this many folders. Use 0 or false to + * disable recursion, and -1 to recurse infinite folders. + * exclude - (string | array<string>) patterns that will be matched + * using the PHP `fnmatch` function. If any file or folder matches, + * it will be excluded from the copy procedure. Omit or use false + * to disable exclusions + */ + function unpackage($folder, $destination, $recurse=0, $exclude=false) { + $verbose = $this->getOption('verbose'); + if (substr($destination, -1) !== '/') + $destination .= '/'; foreach (glob($folder, GLOB_BRACE|GLOB_NOSORT) as $file) { if ($this->exclude($exclude, $file)) continue; if (is_file($file)) { if (!is_dir($destination)) mkdir($destination, 0751, true); - copy($file, $destination . '/' . basename($file)); + $target = $destination . basename($file); + if (is_file($target) && md5_file($target) == md5_file($file)) + continue; + if ($verbose) + $this->stdout->write($target."\n"); + copy($file, $target); } } if ($recurse) { - foreach (glob(dirname($folder).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) { - if ($this->exclude($exclude, $dir)) + foreach (glob(dirname($folder).'/'.basename($folder), + GLOB_BRACE|GLOB_ONLYDIR|GLOB_NOSORT) as $dir) { + if (in_array(basename($dir), array('.','..'))) + continue; + elseif ($this->exclude($exclude, $dir)) continue; $this->unpackage( dirname($folder).'/'.basename($dir).'/'.basename($folder), - $destination.'/'.basename($dir), + $destination.basename($dir), $recurse - 1, $exclude); } } @@ -132,7 +161,7 @@ class Unpacker extends Module { # Get the current value of the INCLUDE_DIR before overwriting # main.inc.php $include = $this->get_include_dir(); - $this->unpackage("$upload/*", $this->destination, -1, "*include"); + $this->unpackage("$upload/{,.}*", $this->destination, -1, "*include"); if (!$upgrade) { if ($this->getOption('include')) { @@ -140,14 +169,14 @@ class Unpacker extends Module { if (!is_dir("$location/")) if (!mkdir("$location/", 0751, true)) die("Unable to create folder for include/ files\n"); - $this->unpackage("$upload/include/*", $location, -1); + $this->unpackage("$upload/include/{,.}*", $location, -1); $this->change_include_dir($location); } else - $this->unpackage("$upload/include/*", "{$this->destination}/include", -1); + $this->unpackage("$upload/include/{,.}*", "{$this->destination}/include", -1); } else { - $this->unpackage("$upload/include/*", $include, -1); + $this->unpackage("$upload/include/{,.}*", $include, -1); # Change the new main.inc.php to reflect the location of the # include/ directory $this->change_include_dir($include);