Skip to content
Snippets Groups Projects
Commit 937278c7 authored by Jared Hancock's avatar Jared Hancock
Browse files

package: Re-use the deployment system for packaging

parent 09753f67
No related branches found
No related tags found
No related merge requests found
......@@ -180,7 +180,7 @@ class Deployment extends Unpacker {
$X = implode(' --exclude-per-directory=', $patterns);
chdir($source.$local);
if (!($files = proc_open(
"git ls-files -s --full-name --exclude-standard --exclude-per-directory=$X -- .",
"git ls-files -zs --exclude-standard --exclude-per-directory=$X -- .",
array(1 => array('pipe', 'w')),
$pipes
))) {
......@@ -189,9 +189,9 @@ class Deployment extends Unpacker {
$dryrun = $this->getOption('dry-run', false);
$verbose = $this->getOption('verbose') || $dryrun;
while ($line = stream_get_line($pipes[1], 255, PHP_EOL)) {
while ($line = stream_get_line($pipes[1], 255, "\x00")) {
list($mode, $hash, , $path) = preg_split('/\s+/', $line);
$src = $source.$path;
$src = $source.$local.$path;
if ($this->exclude($exclude, $src))
continue;
if (!$this->isChanged($src, $hash))
......
<?php
require_once "deploy.php";
class Packager extends Deployment {
var $prologue = "Creates an osTicket distribution ZIP file";
var $epilog =
"Packaging is based on the `deploy` and `test` cli apps. After
running the tests, the system is deployed into a temporary staging
area using the files tracked in git if supported. Afterwards, the
staging area is packaged as a ZIP file.";
var $options = array(
'format' => array('-F','--format',
'default'=>'zip',
'help'=>'Output the package in this format. Supported formats are
"zip" (the default), and "targz"'
),
'skip-test' => array('-S','--skip-test',
'action'=>'store_true', 'default'=>false,
'help'=>'Skip regression testing (NOT RECOMMENDED)',
),
);
var $arguments = array();
function __construct() {
// Skip options added to the deploy — options and arguments are
// forced in this module
call_user_func_array(array('Module', '__construct'), func_get_args());
}
function run($args, $options) {
// Set some forced args and options
$temp = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$stage_path = $temp . 'osticket'
. substr(md5('osticket-stage'.getmypid().getcwd()), -8);
$args['install-path'] = $stage_path . '/upload';
// Deployment will auto-create the staging area
// Ensure that the staging path is cleaned up on exit
register_shutdown_function(function() use ($stage_path) {
$delTree = function($dir) use (&$delTree) {
$files = array_diff(scandir($dir), array('.','..'));
foreach ($files as $file) {
(is_dir("$dir/$file")) ? $delTree("$dir/$file") : unlink("$dir/$file");
}
return rmdir($dir);
};
return $delTree($stage_path);
});
$options['setup'] = true;
$options['git'] = true;
$options['verbose'] = true;
$options['clean'] = false;
$options['dry-run'] = false;
$options['include'] = false;
$this->_args = $args;
$this->_options = $options;
// TODO: Run the testing applet first
$root = $this->find_root_folder();
if (!$this->getOption('skip-test') && $this->run_tests($root) > 0)
$this->fail("Regression tests failed. Cowardly refusing to package");
// Run the deployment
// NOTE: The deployment will change the working directory
parent::run($args, $options);
// Deploy the `setup/scripts` folder to `/scripts`
$root = $this->source;
Unpacker::unpackage("$root/setup/scripts/{,.}*", "$stage_path/scripts", -1);
// Package up the staging area
$version = exec('git describe');
switch (strtolower($this->getOption('format'))) {
case 'zip':
default:
$this->packageZip("$root/osTicket-$version.zip", $stage_path);
}
}
function run_tests($root) {
return (require "$root/setup/test/run-tests.php");
}
function packageZip($name, $path) {
$zip = new ZipArchive();
if (!$zip->open($name, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true)
return false;
$php56 = version_compare(phpversion(), '5.6.0', '>');
$addFiles = function($dir) use (&$addFiles, $zip, $path, $php56) {
$files = array_diff(scandir($dir), array('.','..'));
$path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
foreach ($files as $file) {
$full = "$dir/$file";
$local = str_replace($path, '', $full);
if (is_dir($full))
$addFiles($full);
else
// XXX: AddFile() will keep the file open and run
// out of OS open file handles
$zip->addFromString($local, file_get_contents($full));
// This only works on PHP >= v5.6
if ($php56) {
// Set the Unix mode of the file
$stat = stat($full);
$zip->setExternalAttributesName($local, ZipArchive::OPSYS_UNIX, $stat['mode']);
}
}
};
$addFiles($path);
return $zip->close();
}
}
Module::register('package', 'Packager');
#!/usr/bin/env php
<?php
if (php_sapi_name() != 'cli')
die("Only command-line packaging is supported");
$stage_folder = "stage";
$stage_path = dirname(__file__) . '/' . $stage_folder;
function get_osticket_root_path() {
# Hop up to the root folder
$start = dirname(__file__);
for (;;) {
if (file_exists($start . '/main.inc.php')) break;
$start .= '/..';
}
return realpath($start);
}
function run_tests($root) {
return (require "$root/setup/test/run-tests.php");
}
# Check PHP syntax across all php files
function glob_recursive($pattern, $flags = 0) {
$files = glob($pattern, $flags);
foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) {
$files = array_merge($files,
glob_recursive($dir.'/'.basename($pattern), $flags));
}
return $files;
}
$root = get_osticket_root_path();
function exclude($pattern, $match) {
if (is_array($pattern)) {
foreach ($pattern as $p)
if (fnmatch($p, $match))
return true;
}
else
return fnmatch($pattern, $match);
return false;
}
function package($pattern, $destination, $recurse=false, $exclude=false) {
global $root, $stage_path;
$search = $root . '/' . $pattern;
echo "Packaging " . $search . "\n";
foreach (glob($search, GLOB_BRACE|GLOB_NOSORT) as $file) {
if (is_file($file)) {
if ($exclude && exclude($exclude, $file))
continue;
if (!is_dir("$stage_path/$destination"))
mkdir("$stage_path/$destination", 0777, true);
copy($file, $stage_path . '/' . $destination . '/' . basename($file));
}
}
if ($recurse) {
foreach (glob(dirname($search).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) {
if ($exclude && exclude($exclude, $dir))
continue;
package(dirname($pattern).'/'.basename($dir).'/'.basename($pattern),
$destination.'/'.basename($dir),
$recurse-1, $exclude);
}
}
}
# Run tests before continuing
if (run_tests($root) > 0)
die("Regression tests failed. Cowardly refusing to package\n");
# Create the stage folder for the install files
if (!is_dir($stage_path))
mkdir($stage_path);
else {
$dirs = array();
foreach (glob_recursive($stage_path . '/*') as $file)
if (is_dir($file))
$dirs[] = $file;
else
unlink($file);
sort($dirs);
foreach (array_reverse($dirs) as $dir)
rmdir($dir);
}
# Source code goes into 'upload'
mkdir($stage_path . '/upload');
# Load the root directory files
package("*.php", 'upload/');
package("web.config", 'upload/');
# Load the client interface
foreach (array('assets','css','images','js') as $dir)
package("$dir/*", "upload/$dir", -1, "*less");
# Load API and pages
package('api/{,.}*', 'upload/api');
package('pages/{,.}*', 'upload/pages');
# Load the knowledgebase
package("kb/*.php", "upload/kb");
# Load the staff interface
package("scp/*.php", "upload/scp/", -1);
foreach (array('css','images','js') as $dir)
package("scp/$dir/*", "upload/scp/$dir", -1);
# Load in the scripts
mkdir("$stage_path/scripts/");
package("setup/scripts/*", "scripts/", -1, "*stage");
# Load the heart of the system
package("include/{,.}*", "upload/include", -1, array('*ost-config.php', '*.sw[a-z]','plugins/*'));
# Include the installer
package("setup/*.{php,txt,html}", "upload/setup", -1, array("*scripts","*test","*stage"));
foreach (array('css','images','js') as $dir)
package("setup/$dir/*", "upload/setup/$dir", -1);
package("setup/inc/streams/*.sql", "upload/setup/inc/streams", -1);
# Load the license and documentation
package("*.{txt,md}", "");
#Rename markdown as text TODO: Do html version before rename.
if(($mds = glob("$stage_path/*.md"))) {
foreach($mds as $md)
rename($md, preg_replace('/\.md$/', '.txt', $md));
}
# Make an archive of the stage folder
$version = exec('git describe');
$hash = exec('git rev-parse HEAD');
$short = substr($hash, 0, 7);
$pwd = getcwd();
chdir($stage_path);
// Replace THIS_VERSION in the stage/ folder
shell_exec("sed -ri -e \"
s/( *)define\('THIS_VERSION'.*/\\1define('THIS_VERSION', '$version');/
s/( *)define\('GIT_VERSION'.*/\\1define('GIT_VERSION', '$short');/
\" upload/bootstrap.php");
shell_exec("find upload -name '*.php' -print0 | xargs -0 sed -i -e '
s:<script\(.*\) src=\"\(.*\).js\"></script>:<script\\1 src=\"\\2.js?$short\"></script>:
s:<link\(.*\) href=\"\(.*\)\.css\"\(.*\)*/*>:<link\\1 href=\"\\2.css?$short\"\\3>:
'");
shell_exec("find upload -name '*.php' -print0 | xargs -0 sed -i -e \"
s/\( *\)ini_set( *'display_errors'[^])]*);/\\1ini_set('display_errors', 0);/
s/\( *\)ini_set( *'display_startup_errors'[^])]*);/\\1ini_set('display_startup_errors', 0);/
\"");
shell_exec("tar cjf '$pwd/osTicket-$version.tar.bz2' *");
shell_exec("zip -r '$pwd/osTicket-$version.zip' *");
chdir($pwd);
?>
Creating an osTicket distribution
=================================
osTicket is packaged using an included script. Access to the packaging
system is provided via the `manage.php` CLI app.
The packaging system extends the deployment system in order to remove
similar code between the two processes. Where possible, the files reported
to be part of the git repository are used in the packaging process, which
removes the possibility of experimental files and those ignored by git from
being added to the distribution.
More information is available via the automated help output.
php setup/cli/manage.php package --help
Creating the ZIP file
---------------------
To package the system using the defaults (as a ZIP file), just invoke the
packager with no other options.
php setup/cli/manage.php package
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment