Newer
Older
<?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 __construct() {
$this->options['dry-run'] = array('-t','--dry-run',
'action'=>'store_true',
'help'=>'Don\'t actually deploy new code. Just show the files
that would be copied');
$this->options['setup'] = array('-s','--setup',
'action'=>'store_true',
'help'=>'Deploy the setup folder. Useful for deploying for new
installations.');
$this->options['clean'] = array('-C','--clean',
'action'=>'store_true',
'help'=>'Remove files from the destination that are no longer
included in this repository');
# super(*args);
call_user_func_array(array('parent', '__construct'), func_get_args());
}
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 self::realpath($start);
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/**
* Removes files from the deployment location that no longer exist in
* the local repository
*/
function clean($local, $destination, $recurse=0, $exclude=false) {
$dryrun = $this->getOption('dry-run', false);
$verbose = $dryrun || $this->getOption('verbose');
$destination = rtrim($destination, '/') . '/';
$contents = glob($destination.'{,.}*', GLOB_BRACE|GLOB_NOSORT);
foreach ($contents as $i=>$file) {
if ($this->exclude($exclude, $file))
continue;
if (is_file($file)) {
$ltarget = $local . '/' . basename($file);
if (is_file($ltarget))
continue;
if ($verbose)
$this->stdout->write("(delete): $file\n");
if (!$dryrun)
unlink($file);
unset($contents[$i]);
}
elseif (in_array(basename($file), array('.','..'))) {
// Doesn't indicate that the folder has contents
unset($contents[$i]);
}
}
if ($recurse) {
$folders = glob(dirname($destination).'/'.basename($destination).'/*',
GLOB_BRACE|GLOB_ONLYDIR|GLOB_NOSORT);
foreach ($folders as $dir) {
if (in_array(basename($dir), array('.','..')))
continue;
elseif ($this->exclude($exclude, $dir))
continue;
$this->clean(
$local.'/'.basename($dir),
$destination.basename($dir),
$recurse - 1, $exclude);
}
}
if (!$contents || !glob($destination.'{,.}*', GLOB_BRACE|GLOB_NOSORT)) {
if ($verbose)
$this->stdout->write("(delete-folder): $destination\n");
if (!$dryrun)
rmdir($destination);
}
}
function writeManifest($root) {
$lines = array();
foreach ($this->manifest as $F=>$H)
$lines[] = "$H $F";
return file_put_contents($root.'/.MANIFEST', implode("\n", $lines));
}
function hashContents($file) {
$md5 = md5($file);
$sha1 = sha1($file);
return substr($md5, -20) . substr($sha1, -20);
}
function getEditedContents($src) {
static $short = false;
static $version = false;
if (substr($src, -4) != '.php')
return false;
if (!$short) {
$hash = exec('git rev-parse HEAD');
$short = substr($hash, 0, 7);
}
if (!$version)
$version = exec('git describe');
if (!$short || !$version)
return false;
$source = file_get_contents($src);
$source = preg_replace(':<script(.*) src="([^"]+)\.js"></script>:',
'<script$1 src="$2.js?'.$short.'"></script>',
$source);
$source = preg_replace(':<link(.*) href="([^"]+)\.css"([^/>]*)/?>:', # <?php
'<link$1 href="$2.css?'.$short.'"$3/>',
$source);
// Set THIS_VERSION
$source = preg_replace("/^(\s*)define\s*\(\s*'THIS_VERSION'.*$/m",
"$1define('THIS_VERSION', '".$version."'); // Set by installer",
// Set GIT_VERSION
$source = preg_replace("/^(\s*)define\s*\(\s*'GIT_VERSION'.*$/m",
"$1define('GIT_VERSION', '".$short."'); // Set by installer",
$source);
// Disable error display
$source = preg_replace("/^(\s*)ini_set\s*\(\s*'(display_errors|display_startup_errors)'.*$/m",
"$1ini_set('$2', '0'); // Set by installer",
$source);
return $source;
}
function copyFile($source, $dest, $hash=false) {
$contents = $this->getEditedContents($source);
if ($contents === false)
// Regular file
return parent::copyFile($source, $dest, $hash);
if (!file_put_contents($dest, $contents))
$this->fail($dest.": Unable to apply rewrite rules");
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
function unpackage($folder, $destination, $recurse=0, $exclude=false) {
return parent::unpackage($folder, $destination, $recurse, $exclude);
// TODO: Consider using `git ls-files` for deployment
if (substr($destination, -1) !== '/')
$destination .= '/';
$source = $this->source;
if (substr($source, -1) != '/')
$source .= '/';
$pipes = array();
$patterns = array();
foreach ((array) $exclude as $x) {
$patterns[] = str_replace($source, '', $x);
}
$exclude = implode(' --exclude-per-directory=', $patterns);
if (!($files = proc_open(
"git ls-files -s --exclude-standard --exclude-per-directory=$exclude $folder",
array(1 => array('pipe', 'w')),
$pipes
))) {
return parent::unpackage($folder, $destination, $recurse, $exclude);
}
$dryrun = $this->getOption('dry-run', false);
$verbose = $this->getOption('verbose') || $dryrun;
while ($line = stream_get_line($pipes[1], 255, PHP_EOL)) {
list($mode, $hash, , $path) = preg_split('/\s+/', $line);
$src = $source.$path;
$dst = $destination.$path;
if (!$this->isChanged($src, false, $hash))
continue;
if ($verbose)
$this->stdout->write($dst."\n");
if ($dryrun)
continue;
if (!is_dir(dirname($dst)))
mkdir(dirname($dst), 0751, true);
// TODO: Consider the MODE value
$this->copyFile($src, $dst, $hash);
}
}
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 = self::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
$include = ($upgrade) ? $this->get_include_dir()
: ($options['include'] ? $options['include']
: rtrim($this->destination, '/')."/include");
$include = rtrim($include, '/').'/';
$root = $this->source = $this->find_root_folder();
$rootPattern = str_replace("\\","\\\\", $root); //need for windows case
# Prime the manifest system
$this->readManifest($this->destination.'/.MANIFEST');
$exclusions = array("$rootPattern/include", "$rootPattern/.git*",
"*.sw[a-z]","*.md", "*.txt");
if (!$options['setup'])
$this->unpackage("$root/{,.}*", $this->destination, -1,
$this->unpackage("$root/include/{,.}*", $include, -1,
array("*/include/ost-config.php"));
if (!$options['dry-run']) {
if ($include != "{$this->destination}/include/")
$this->change_include_dir($include);
}
if ($options['clean']) {
// Clean everything but include folder first
$this->clean($root, $this->destination, -1,
array($include, "setup/"));
$this->clean("$root/include", $include, -1,
array("ost-config.php","settings.php","plugins/",
"*/.htaccess"));
$this->writeManifest($this->destination);
}
}
Module::register('deploy', 'Deployment');
?>