/** * Converts a property file into a yaml file * @param array $transform an array of transformation rules such as eg. 'sourcetag' => 'desttag' (desttag can be empty for tag removal or an array for tag expansion) * @todo move to a separate class to slim down base class? * @todo make it capable to remove complete $ext.version.alias property */ static function convertPropertyFileToYamlFile($infile, $outfile = '', $transform = array(), $prepend = '') { if ($outfile == '') { $outfile = self::getOptionsDir() . '/options.yaml'; } $current = array(); $out = array(); foreach (file($infile) as $line) { $line = trim($line); if ($line == '') { $out[] = ''; } else { if (strpos($line, '<!--') === 0) { $out[] .= preg_replace('/^<!-- *(.*) *-->$/', '# $1', $line); } else { if (strpos($line, '=') != 0) { $line = explode('=', $line, 2); $path = explode('.', trim($line[0])); foreach ($transform as $src => $dst) { foreach ($path as $i => $element) { if ($element == $src) { if ($dst == '') { unset($path[$i]); } else { if (is_array($dst)) { array_splice($path, $i - 1, 1, $dst); } else { $path[$i] = $dst; } } } } } // elements index can have holes here, cannot trust them => reorder $path = array_values($path); $value = $line[1]; $token = array_pop($path); if ($path != $current) { $skip = 0; foreach ($path as $j => $element) { if ($element == @$current[$j]) { $skip++; } else { break; } } for ($j = $skip; $j < count($path); $j++) { $line = ''; for ($i = 0; $i < $j; $i++) { $line .= ' '; } $line .= $path[$j] . ':'; $out[] = $line; } } $line = ''; for ($i = 0; $i < count($path); $i++) { $line .= ' '; } $line .= $token . ': ' . $value; $out[] = $line; $current = $path; } else { /// @todo log warning? } } } } pake_mkdirs('pake'); // ask confirmation if file exists $ok = !file_exists($outfile) || pake_input("Destionation file {$outfile} exists. Overwrite? [y/n]", 'n') == 'y'; if ($ok) { file_put_contents($outfile, $prepend . implode($out, "\n")); pake_echo_action('file+', $outfile); } }
/** * Demo-task * * @param string $task * @param string $args * @return bool * @author Alexey Zakhlestin */ function run_foo($task, $args) { $age = pake_input('How old are you?'); pake_echo_comment("You are " . $age); // throw new Exception('test'); }
/** * Converts an existing ant properties file in its corresponding yaml version * * Converts the .properties files used to hold configuration settings for old * versions of ezextensionbuilder (the ones based on ant) to a .yaml configuration * file that is suitable for this version of the script. * It is recommended to inspect by hand the generated .yaml file after executing * the conversion. */ static function run_convert_configuration($task = null, $args = array(), $cliopts = array()) { self::setConfigDir($cliopts); $extname = @$args[0]; if ($extname == '') { $extname = dirname(__FILE__); } while (!is_file("ant/{$extname}.properties")) { $extname = pake_input('What is the name of the current extension?'); if (!is_file("ant/{$extname}.properties")) { pake_echo("File ant/{$extname}.properties not found"); } } self::convertPropertyFileToYamlFile("ant/{$extname}.properties", self::getConfigDir() . "/options-{$extname}.yaml", array($extname => '', 'external' => 'dependencies', 'dependency' => 'extensions', 'repository' => array('svn', 'url')), "extension:\n name: {$extname}\n\n"); foreach (array('files.to.parse.txt' => 'to_parse', 'files.to.exclude.txt' => 'to_exclude') as $file => $option) { $src = "ant/{$file}"; if (file_exists($src)) { //$ok = !file_exists( $dst ) || ( pake_input( "Destionation file $dst exists. Overwrite? [y/n]", 'n' ) == 'y' ); //$ok && pake_copy( $src, $dst, array( 'override' => true ) ); if (count($in = file($src, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES))) { $in = "\n\nfiles:\n {$option}: [" . implode(', ', $in) . "]\n"; file_put_contents(self::getConfigDir() . "options-{$extname}.yaml", $in, FILE_APPEND); } } } }
/** * Creates a tarball of the built extension. * * Depending on configuration options, different versions of the extenion tarball * are generated by this task: .tar.gz, .zip, .ezpkg */ static function run_dist($task = null, $args = array(), $cliopts = array()) { $opts = self::getOpts(@$args[0], @$args[1], $cliopts); if ($opts['create']['tarball'] || $opts['create']['zip'] || $opts['create']['ezpackage'] || $opts['create']['pearpackage']) { if (!SharedLock::acquire($opts['extension']['name'], LOCK_SH, $opts)) { throw new PakeException("Source code locked by another process"); } pake_mkdirs($opts['dist']['dir']); $rootpath = self::getBuildDir($opts) . '/' . $opts['extension']['name']; if ($opts['create']['tarball']) { $target = $opts['dist']['dir'] . '/' . $opts['extension']['name'] . '-' . $opts['version']['alias'] . '.' . $opts['version']['release'] . '.tar.gz'; self::archiveDir($rootpath, $target); } if ($opts['create']['zip']) { $target = $opts['dist']['dir'] . '/' . $opts['extension']['name'] . '-' . $opts['version']['alias'] . '.' . $opts['version']['release'] . '.zip'; self::archiveDir($rootpath, $target); } if ($opts['create']['ezpackage'] || $opts['create']['pearpackage']) { $toppath = $opts['build']['dir']; // check if package.xml file is there $file = pakeFinder::type('file')->name('package.xml')->maxdepth(0)->in($toppath); if (!count($file)) { pake_echo_error("File 'package.xml' missing in build dir {$rootpath}. Cannot create package(s)"); return; } // cleanup if extra files/dirs found $dirs = array(); $dirs = pakeFinder::type('directory')->not_name(array('documents', 'ezextension'))->maxdepth(0)->in($toppath); $dirs = array_merge($dirs, pakeFinder::type('directory')->in($toppath . '/documents')); $dirs = array_merge($dirs, pakeFinder::type('directory')->not_name($opts['extension']['name'])->maxdepth(0)->in($toppath . '/ezextension')); $files = pakeFinder::type('file')->not_name('package.xml')->maxdepth(0)->in($toppath); $files = array_merge($files, pakeFinder::type('file')->in($toppath . '/documents')); $files = array_merge($files, pakeFinder::type('file')->not_name('extension-' . $opts['extension']['name'] . '.xml')->maxdepth(0)->in($toppath . '/ezextension')); if (count($dirs) || count($files)) { pake_echo("Extra files/dirs found in build dir. Must remove them to continue:\n " . implode("\n ", $dirs) . " " . implode("\n ", $files)); $ok = pake_input("Do you want to delete them? [y/n]", 'n'); if ($ok != 'y') { return; } foreach ($files as $file) { pake_remove($file, ''); } foreach ($dirs as $dir) { pake_remove_dir($dir); } } // prepare missing folders/files /// @todo we should not blindly copy LICENSE and README, but inspect actual package.xml file /// and copy any files mentioned there pake_copy($rootpath . '/' . $opts['files']['gnu_dir'] . '/LICENSE', $toppath . '/documents/LICENSE'); pake_copy($rootpath . '/' . $opts['files']['gnu_dir'] . '/README', $toppath . '/documents/README'); $target = $opts['dist']['dir'] . '/' . $opts['extension']['name'] . '_extension.ezpkg'; self::archiveDir($toppath, $target, true); if ($opts['create']['pearpackage']) { /// @todo ... pake_echo_error("PEAR package creation not yet implemented"); } } SharedLock::release($opts['extension']['name'], LOCK_SH, $opts); } }
/** * Commits changelog to the "ci" git repo and updates in there other files holding version-related infos; options: skip-update-ci-repo-source * As part of this task, the local copy of the "ci" git repo is updated from upstream. * * The "ci" repo is used by the standard eZ Publish build process, driven by Jenkins. * It holds, amongs other things, patch files that are applied in order to build the * CP version instead of the Enterprise one */ public static function run_update_ci_repo($task = null, $args = array(), $cliopts = array()) { $opts = self::getOpts($args, $cliopts); $rootpath = self::getSourceDir($opts, 'legacy'); // start work on the ci repo: $cipath = self::getSourceDir($opts, 'ci-repo'); $git = self::getTool('git', $opts); // 1. update ci repo - moved to a separate task // dirty, dirty hack $originp = @$GLOBALS['originp']; $repo = new pakeGit($cipath); if ($opts['git']['ci-repo']['path'] != '') { $cipath .= '/' . $opts['git']['ci-repo']['path']; $localcipath = $opts['git']['ci-repo']['path'] . '/'; } else { $localcipath = ''; } // 1b. check that there is no spurious stuff in changelog dir, or step 3 later will create bad patch files. // we do this here to avoid adding ANY file to local copy of CI git repo and abort asap $changelogdir = 'doc/changelogs/Community_Project-' . $opts['version']['major']; $files = pakeFinder::type('file')->maxdepth(0)->relative()->in(self::getSourceDir($opts, 'legacy') . '/' . $changelogdir); if (count($files) != 1) { throw new pakeException("More than one changelog file (or none) found in directory {$changelogdir}, can not generate patch file for CI repo"); } // 2. update 0002_2011_11_patch_fix_version.diff file $files1 = pakeFinder::type('file')->name('0002_2011_11_patch_fix_version.diff')->maxdepth(0)->relative()->in($cipath . '/patches'); $files2 = pakeFinder::type('file')->name('0003_2011_11_patch_fix_package_repository.diff')->maxdepth(0)->relative()->in($cipath . '/patches'); /// @todo what if those files are gone? $patchfile1 = $cipath . '/patches/' . $files1[0]; $patchfile2 = $cipath . '/patches/' . $files2[0]; // if a new major version has been released, the '0002_2011_11_patch_fix_version.diff' patch will not apply, // and the '0003_2011_11_patch_fix_package_repository.diff' file will have to be altered as well // we need thus to regenerate them (more details: https://docs.google.com/a/ez.no/document/d/1h5n3aZdXbyo9_iJoDjoDs9a6GdFZ2G-db9ToK7J1Gck/edit?hl=en_GB) // 1st, we test that the current patch file will apply cleanly (if it does, we assume no new EE release) $patch = self::getTool('patch', $opts); $patcherror = false; try { $patchResult = pake_sh(self::getCdCmd($rootpath) . " && {$patch} --dry-run -p0 < " . $patchfile1); } catch (Exception $e) { $patcherror = $e->getMessage(); } // then, we (try to) recover version info from existing patch files $patch1 = file_get_contents($patchfile1); $patch2 = file_get_contents($patchfile2); if (preg_match('/^\\- +const +VERSION_MAJOR += +(\\d+);/m', $patch1, $m1) && preg_match('/^\\- +const +VERSION_MINOR += +(\\d+);/m', $patch1, $m2) && preg_match('/^\\+RemotePackagesIndexURL=http:\\/\\/packages.ez.no\\/ezpublish\\/[^\\/]+\\/(.+)$/m', $patch2, $m3)) { $oldNextVersionMajor = $m1[1]; $oldNextVersionMinor = $m2[1]; $currentVersion = $m3[1]; // give some information to user pake_echo("\nPackages available during setup wizard execution for this CP build will be the ones from eZP {$currentVersion}"); pake_echo("The next build of eZ Publish EE is expected to be {$oldNextVersionMajor}.{$oldNextVersionMinor}\n"); // last, we try automated fixing or abort if ($patcherror) { // try to gather enough information to fix automatically the patch files $currfile = file_get_contents($rootpath . '/lib/version.php'); if (preg_match('/^\\ +const +VERSION_MAJOR += +(\\d+);/m', $currfile, $m1) && preg_match('/^\\ +const +VERSION_MINOR += +(\\d+);/m', $currfile, $m2)) { $newNextVersionMajor = $m1[1]; $newNextVersionMinor = $m2[1]; if ($newNextVersionMajor == $oldNextVersionMajor && $newNextVersionMinor == $oldNextVersionMinor) { // patch does not apply but version number was not changed. Abort throw new pakeException("The diff file {$patchfile1} does not apply correctly, build will fail. Please fix it, commit and push.\n Also check to fix if needed the url to packages repo in 0003_2011_11_patch_fix_package_repository.diff.\nError details:\n" . $patcherror); } pake_echo("It seems that EE version {$oldNextVersionMajor}.{$oldNextVersionMinor} was released, next expected EE version is currently {$newNextVersionMajor}.{$newNextVersionMinor}"); pake_echo("This means that the diff file {$patchfile1} does not apply correctly, the build will fail."); pake_echo("The script can fix this automatically, or you will have to fix patch files by hand (at least 2 of them)"); pake_echo("Proposed changes:"); pake_echo("Packages available during setup wizard execution for this CP build will be the ones from eZP {$oldNextVersionMajor}.{$oldNextVersionMinor}.0"); $ok = pake_input("Do you want to continue with automatic fixing? [y/n]", 'n'); if ($ok != 'y') { throw new pakeException("Please fix patch file by hand, commit and push.\nAlso remember to fix the url to packages repo in 0003_2011_11_patch_fix_package_repository.diff"); } else { pake_replace_regexp($files1, $cipath . '/patches', array('/^- +const +VERSION_MAJOR += +\\d+;/m' => "+ const VERSION_MAJOR = {$newNextVersionMajor};", '/^- +const +VERSION_MINOR += +\\d+;/m' => "+ const VERSION_MINOR = {$newNextVersionMinor};")); pake_replace_regexp($files2, $cipath . '/patches', array('/^\\+RemotePackagesIndexURL=http:\\/\\/packages.ez.no\\/ezpublish\\/([^\\/]+)\\/.*$/m' => "+RemotePackagesIndexURL=http://packages.ez.no/ezpublish/{$oldNextVersionMajor}.{$oldNextVersionMinor}/{$oldNextVersionMajor}.{$oldNextVersionMinor}.0")); } } else { throw new pakeException("The diff file {$patchfile1} does not apply correctly, build will fail. Please fix it, commit and push.\n Also remember to fix the url to packages repo in 0003_2011_11_patch_fix_package_repository.diff.\nError details:\n" . $patcherror); } } } else { if ($patcherror) { throw new pakeException("The diff file {$patchfile1} does not apply correctly, build will fail. Please fix it, commit and push.\n Also remember to fix the url to packages repo in 0003_2011_11_patch_fix_package_repository.diff.\nError details:\n" . $patcherror); } else { /// @todo waht to do here? warn user and give him a chance to abort... } } // finally, the changes which apply every time pake_replace_regexp($files1, $cipath . '/patches', array('/^\\+ +const +VERSION_MAJOR += +\\d+;/m' => "+ const VERSION_MAJOR = {$opts['version']['major']};", '/^\\+ +const +VERSION_MINOR += +\\d+;/m' => "+ const VERSION_MINOR = {$opts['version']['minor']};")); $repo->add(array($localcipath . 'patches/0002_2011_11_patch_fix_version.diff')); // 3. generate changelog diff // get absolute path to build dir $absrootpath = pakeFinder::type('directory')->name(self::getProjName())->in($opts['build']['dir'] . '/source'); $absrootpath = dirname($absrootpath[0]); $difffile = $absrootpath . '/' . $opts['version']['alias'] . '_patch_fix_changelog.diff'; pake_sh(self::getCdCmd($rootpath) . " && {$git} add " . escapeshellarg($changelogdir)); pake_sh(self::getCdCmd($rootpath) . " && {$git} diff --no-prefix --staged -- " . escapeshellarg($changelogdir) . " > " . escapeshellarg($difffile)); /// unstage the file pake_sh(self::getCdCmd($rootpath) . " && {$git} reset HEAD --"); // 4. add new changelog file /// calculate sequence nr. $max = 0; $files = pakeFinder::type('file')->maxdepth(0)->in($cipath . '/patches'); foreach ($files as $file) { $nr = (int) substr(basename($file), 0, 4); if ($nr > $max) { $max = $nr; } } $seqnr = str_pad($max + 1, 4, '0', STR_PAD_LEFT); $newdifffile = $seqnr . '_' . str_replace('.', '_', $opts['version']['alias']) . '_patch_fix_changelog.diff'; // what if there is already a patch file in the ci repo which creates the changelog we are adding a patch file for? if (preg_match_all('/^\\+\\+\\+ (.*)$/m', file_get_contents($difffile), $matches)) { $added = $matches[1]; //echo "adding: "; var_dump( $added ); $patchfiles = pakeFinder::type('file')->name('*.diff')->maxdepth(0)->in($cipath . '/patches'); foreach ($patchfiles as $patchfile) { if (preg_match_all('/^\\+\\+\\+ (.*)$/m', file_get_contents($patchfile), $matches)) { //echo "already added: "; var_dump( $matches[1] ); if (array_intersect($added, $matches[1])) { $cont = pake_select_input("The patch file\n {$patchfile}\nseems to already create the same files wew want to create using a new patch file:\n {$newdifffile}\n" . "This is a sign of a probable error somewhere. Press '1' to continue the build task anyway. Otherwise press '2' to exit.", array('Continue', 'Stop'), 1); if ($cont != 'Y') { exit; } } } } } pake_copy($difffile, $cipath . '/patches/' . $newdifffile, array('override' => true)); $repo->add(array($localcipath . 'patches/' . $newdifffile)); // 5. update ezpublish-gpl.properties $files = pakeFinder::type('file')->name('ezpublish-gpl.properties')->maxdepth(0)->relative()->in($cipath . '/properties'); pake_replace_regexp($files, $cipath . '/properties', array('/^ezp\\.cp\\.version\\.major += +.+$/m' => "ezp.cp.version.major = {$opts['version']['major']}", '/^ezp\\.cp\\.version\\.minor += +.+$/m' => "ezp.cp.version.minor = {$opts['version']['minor']}")); $repo->add(array($localcipath . 'properties/ezpublish-gpl.properties')); // 5. commit changes and push to upstream $repo->commit('Prepare files for build of CP ' . $opts['version']['alias']); /// @todo allow the user to specify this on the command line if ($originp == '') { pake_echo("WARNING: Using \"origin\" as name of upstream repo as actual name not found"); $originp = "origin"; } pake_sh(self::getCdCmd($cipath) . " && {$git} push {$originp} {$opts['git']['ci-repo']['branch']}:{$opts['git']['ci-repo']['branch']}"); }