1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
15:
16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31:
32: class Horde_Release
33: {
34: 35: 36: 37: 38:
39: protected $_options = array(
40: 'test' => false,
41: 'nocommit' => false,
42: 'noftp' => false,
43: 'noannounce' => false,
44: 'nofreecode' => false,
45: 'nowhups' => false,
46: );
47:
48: 49:
50: const FOCUS_INITIAL = 'Initial announcement';
51: const FOCUS_MINORFEATURE = 'Minor feature enhancements';
52: const FOCUS_MAJORFEATURE = 'Major feature enhancements';
53: const FOCUS_MINORBUG = 'Minor bugfixes';
54: const FOCUS_MAJORBUG = 'Major bugfixes';
55: const FOCUS_MINORSECURITY = 'Minor security fixes';
56: const FOCUS_MAJORSECURITY = 'Major security fixes';
57: const FOCUS_DOCS = 'Documentation improvements';
58:
59: 60: 61: 62: 63:
64: protected $_sourceVersionString;
65:
66: 67: 68: 69: 70:
71: protected $_oldSourceVersionString;
72:
73: 74: 75: 76: 77:
78: protected $_newSourceVersionString;
79:
80: 81: 82: 83: 84:
85: protected $_newSourceVersionStringPlain;
86:
87: 88: 89: 90: 91:
92: protected $_hordeVersionString;
93:
94: 95: 96: 97: 98:
99: protected $_oldHordeVersionString;
100:
101: 102: 103: 104: 105:
106: protected $_tagVersionString;
107:
108: 109: 110: 111: 112:
113: protected $_oldTagVersionString;
114:
115: 116: 117: 118: 119:
120: protected $_changelogVersion;
121:
122: 123: 124: 125: 126:
127: protected $_oldChangelogVersion;
128:
129: 130: 131: 132: 133:
134: protected $_ticketVersion;
135:
136: 137: 138: 139: 140:
141: protected $_ticketVersionDesc = '';
142:
143: 144: 145: 146: 147:
148: protected $_directoryName;
149:
150: 151: 152: 153: 154:
155: protected $_oldDirectoryName;
156:
157: 158: 159: 160: 161:
162: protected $_tarballName;
163:
164: 165: 166: 167: 168:
169: protected $_tarballMD5;
170:
171: 172: 173: 174: 175:
176: protected $_makeDiff = false;
177:
178: 179: 180: 181: 182:
183: protected $_binaryDiffs = array();
184:
185: 186: 187: 188: 189:
190: protected $_oldVersion = false;
191:
192: 193: 194: 195: 196:
197: protected $_patchName;
198:
199: 200: 201: 202: 203:
204: protected $_patchMD5;
205:
206: 207: 208: 209: 210:
211: protected $_latest = true;
212:
213: 214: 215: 216: 217: 218: 219:
220: public $notes = array();
221:
222: 223: 224:
225: public function __construct($options = array())
226: {
227: $this->_options = array_merge($this->_options, $options);
228: $cvsroot = getenv('CVSROOT');
229: if (empty($cvsroot)) {
230: putenv('CVSROOT=:ext:' . $this->_options['horde']['user'] . '@cvs.horde.org:/repository');
231: }
232: print 'CVSROOT ' . getenv('CVSROOT') . "\n";
233: if (!empty($this->_options['cvs']['cvs_rsh'])) {
234: putenv('CVS_RSH=' . $this->_options['cvs']['cvs_rsh']);
235: }
236: print 'CVS_RSH ' . getenv('CVS_RSH') . "\n";
237: }
238:
239: public function __get($property)
240: {
241: return $this->{'_' . $property};
242: }
243:
244: 245: 246:
247: public function deleteDirectory($directory)
248: {
249: print "Deleting directory $directory\n";
250: system("sudo rm -rf $directory");
251: }
252:
253: 254: 255:
256: public function makeTarball()
257: {
258: print "Setting owner/group to 0/0\n";
259: system("sudo chown -R 0:0 $this->_directoryName");
260:
261: print "Making tarball\n";
262: $this->_tarballName = $this->_directoryName . '.tar.gz';
263: if (file_exists($this->_tarballName)) {
264: unlink($this->_tarballName);
265: }
266: system("tar -zcf $this->_tarballName $this->_directoryName");
267: exec($this->_options['md5'] . ' ' . $this->_tarballName, $this->_tarballMD5);
268: }
269:
270: 271: 272:
273: public function tagSource($directory = null, $version = null)
274: {
275: if (empty($directory)) {
276: $directory = $this->_directoryName;
277: }
278: if (empty($version)) {
279: $version = $this->_tagVersionString;
280: }
281: if (!$this->_options['nocommit']) {
282: print "Tagging source in $directory with tag $version\n";
283: system("cd $directory;cvs tag -F $version > /dev/null 2>&1");
284: } else {
285: print "NOT tagging source in $directory (would have been tag $version)\n";
286: }
287: }
288:
289: 290: 291:
292: public function diff()
293: {
294: $this->_patchName = 'patch-' . $this->_oldDirectoryName . str_replace($this->_options['module'], '', $this->_directoryName);
295: print "Making diff between $this->_oldDirectoryName and $this->_directoryName\n";
296: system("diff -uNr $this->_oldDirectoryName $this->_directoryName > $this->_patchName");
297:
298:
299: $this->_binaryDiffs = array();
300: $handle = fopen($this->_patchName, 'r');
301: if ($handle) {
302: while (!feof($handle)) {
303:
304:
305: if (preg_match("/^Binary files (.+) and (.+) differ$/i", rtrim(fgets($handle)), $matches)) {
306: $this->_binaryDiffs[] = ltrim(str_replace($this->_oldDirectoryName . '/', '', $matches[1]));
307: }
308: }
309: fclose($handle);
310: }
311: system("gzip -9f $this->_patchName");
312: exec($this->_options['md5'] . ' ' . $this->_patchName . '.gz', $this->_patchMD5);
313: }
314:
315: 316: 317: 318:
319: public function updateVersionFile($directory, $version_string)
320: {
321: $module = $this->_options['module'];
322: $all_caps_module = strtoupper($module);
323: print "Updating version file for $module\n";
324:
325:
326: $filename_only = 'version.php';
327: $filename = $directory . '/lib/' . $filename_only;
328: $newfilename = $filename . '.new';
329:
330: $oldfp = fopen($filename, 'r');
331: $newfp = fopen($newfilename, 'w');
332: while ($line = fgets($oldfp)) {
333: if (strstr($line, 'VERSION')) {
334: fwrite($newfp, "<?php define('{$all_caps_module}_VERSION', '$version_string') ?>\n");
335: } else {
336: fwrite($newfp, $line);
337: }
338: }
339: fclose($oldfp);
340: fclose($newfp);
341:
342: system("mv -f $newfilename $filename");
343: if (!$this->_options['nocommit']) {
344: system("cd $directory/lib/; cvs commit -f -m \"Tarball script: building new $module release - $version_string\" $filename_only > /dev/null 2>&1");
345: }
346: }
347:
348: 349: 350:
351: public function updateSentinel()
352: {
353: $module = $this->_options['module'];
354:
355: print "Updating CHANGES file for $module\n";
356:
357: $filename_only = 'CHANGES';
358: $updater = new Horde_Release_Sentinel($this->_directoryName);
359: $updater->updateChanges($this->_newSourceVersionStringPlain);
360:
361: if (!$this->_options['nocommit']) {
362: system("cd {$this->_directoryName}/docs/; cvs commit -f -m \"Tarball script: building new $module release - {$this->_newSourceVersionString}\" $filename_only > /dev/null 2>&1");
363: }
364: }
365:
366: 367: 368:
369: public function saveChangelog($old = false, $directory = null)
370: {
371: if (empty($directory)) {
372: if ($old) {
373: $directory = './' . $this->_oldDirectoryName . '/docs';
374: } else {
375: $directory = './' . $this->_directoryName . '/docs';
376: }
377: }
378: if (!$old) {
379: include "$directory/RELEASE_NOTES";
380: if (strlen($this->notes['fm']['changes']) > 600) {
381: print "WARNING: freecode release notes are longer than 600 characters!\n";
382: }
383: }
384: exec("cd $directory; cvs status CHANGES", $output);
385: foreach ($output as $line) {
386: if (preg_match('/Repository revision:\s+([\d.]+)/', $line, $matches)) {
387: if ($old) {
388: $this->_oldChangelogVersion = $matches[1];
389: } else {
390: $this->_changelogVersion = $matches[1];
391: }
392: break;
393: }
394: }
395: }
396:
397: 398: 399: 400:
401: public function cleanDirectories($directory)
402: {
403: print "Cleaning source tree\n";
404: $directories = explode("\n", `find $directory -type d \\( -name CVS -o -name packaging -o -name framework \\) -print | sort -r`);
405: foreach ($directories as $dir) {
406: system("rm -rf $dir");
407: }
408: $cvsignores = explode("\n", `find $directory -name .cvsignore -print`);
409: foreach ($cvsignores as $file) {
410: if (!empty($file)) {
411: unlink($file);
412: }
413: }
414: }
415:
416: 417: 418: 419:
420: public function checkOutTag($mod_version, $directory, $module = null)
421: {
422: if (empty($module)) {
423: $module = $this->_options['module'];
424: }
425:
426: if (@is_dir($module)) {
427: system("rm -rf $module");
428: }
429:
430:
431: if ($mod_version == 'HEAD') {
432: print "Checking out HEAD for $module\n";
433: $cmd = "cvs -q co -P $module > /dev/null";
434: system($cmd, $status);
435: } else {
436: print "Checking out tag $mod_version for $module\n";
437: $cmd = "cvs -q co -P -r$mod_version $module > /dev/null";
438: system($cmd, $status);
439: }
440: if ($status) {
441: die("\nThere was an error running the command\n$cmd\n");
442: }
443:
444:
445: print "Moving $module to $directory\n";
446: if (@is_dir($directory)) {
447: system("rm -rf $directory");
448: }
449: system("mv -f $module $directory");
450: }
451:
452: 453: 454:
455: public function checkOutFramework($mod_version, $directory)
456: {
457: if ($this->_options['module'] == 'horde') {
458: if ($this->_options['branch'] == 'HEAD') {
459: print "Checking out HEAD for framework\n";
460: } else {
461: print "Checking out tag $mod_version for framework\n";
462: }
463: $cmd = "cd $directory; cvs co -P -r$mod_version framework > /dev/null 2>&1; cd ..";
464: system($cmd, $status);
465: if ($status) {
466: die("\nThere was an error running the command\n$cmd\n");
467: }
468:
469: $bin_path = isset($this->_options['framework_bin'])
470: ? rtrim($this->_options['framework_bin'], '/') . '/'
471: : '';
472:
473: print "Installing framework packages\n";
474: passthru($bin_path . "install_framework --copy --src ./$directory/framework --horde /tmp --dest ./$directory/lib", $result);
475: if ($result) {
476: exit;
477: }
478:
479: print "Setting include path\n";
480: $filename = $directory . '/lib/core.php';
481: $newfilename = $filename . '.new';
482: $oldfp = fopen($filename, 'r');
483: $newfp = fopen($newfilename, 'w');
484: while ($line = fgets($oldfp)) {
485: fwrite($newfp, str_replace('// ini_set(\'include_path\'', 'ini_set(\'include_path\'', $line));
486: }
487: fclose($oldfp);
488: fclose($newfp);
489: system("mv -f $newfilename $filename");
490: }
491: }
492:
493: 494: 495:
496: public function upload()
497: {
498: $module = $this->_options['module'];
499: $user = $this->_options['horde']['user'];
500: $identity = empty($this->_options['ssh']['identity']) ? '' : ' -i ' . $this->_options['ssh']['identity'];
501: $chmod = "chmod 664 /horde/ftp/pub/$module/$this->_tarballName;";
502: if ($this->_makeDiff) {
503: $chmod .= " chmod 664 /horde/ftp/pub/$module/patches/$this->_patchName.gz;";
504: }
505: if ($this->_latest &&
506: strpos($this->_options['branch'], 'RELENG') !== 0) {
507: $chmod .= " ln -sf $this->_tarballName /horde/ftp/pub/$module/$module-latest.tar.gz;";
508: }
509:
510: if (!$this->_options['noftp']) {
511: print "Uploading $this->_tarballName to $user@ftp.horde.org:/horde/ftp/pub/$module/\n";
512: system("scp -P 35$identity $this->_tarballName $user@ftp.horde.org:/horde/ftp/pub/$module/");
513: if ($this->_makeDiff) {
514: print "Uploading $this->_patchName.gz to $user@ftp.horde.org:/horde/ftp/pub/$module/patches/\n";
515: system("scp -P 35$identity $this->_patchName.gz $user@ftp.horde.org:/horde/ftp/pub/$module/patches/");
516: }
517: print "Executing $chmod\n";
518: system("ssh -p 35 -l $user$identity ftp.horde.org '$chmod'");
519: } else {
520: print "NOT uploading $this->_tarballName to ftp.horde.org:/horde/ftp/pub/$module/\n";
521: if ($this->_makeDiff) {
522: print "NOT uploading $this->_patchName.gz to $user@ftp.horde.org:/horde/ftp/pub/$module/patches/\n";
523: }
524: print "NOT executing $chmod\n";
525: }
526: }
527:
528: 529: 530:
531: public function announce($doc_dir = null)
532: {
533: $module = $this->_options['module'];
534: if (!isset($this->notes)) {
535: print "NOT announcing release, RELEASE_NOTES missing.\n";
536: return;
537: }
538: if (!empty($this->_options['noannounce']) ||
539: !empty($this->_options['nofreecode'])) {
540: print "NOT announcing release on freecode.com\n";
541: } else {
542: print "Announcing release on freecode.com\n";
543: }
544:
545: if (empty($doc_dir)) {
546: $doc_dir = $module . '/docs';
547: }
548:
549: $url_changelog = $this->_oldVersion
550: ? "http://cvs.horde.org/diff.php/$doc_dir/CHANGES?rt=horde&r1={$this->_oldChangelogVersion}&r2={$this->_changelogVersion}&ty=h"
551: : '';
552:
553:
554: $version = array('version' => $this->_sourceVersionString,
555: 'changelog' => $this->notes['fm']['changes']);
556:
557: if (is_array($this->notes['fm']['focus'])) {
558: $version['tag_list'] = $this->notes['fm']['focus'];
559: } else {
560: $version['tag_list'] = array($this->notes['fm']['focus']);
561: }
562:
563:
564: $links = array();
565: $links[] = array('label' => 'Tar/GZ',
566: 'location' => "ftp://ftp.horde.org/pub/$module/{$this->_tarballName}");
567: if (!empty($url_changelog)) {
568: $links[] = array('label' => 'Changelog',
569: 'location' => $url_changelog);
570: }
571:
572: if (!empty($this->_options['noannounce']) ||
573: !empty($this->_options['nofreecode'])) {
574:
575: print "Announcement data:\n";
576: print_r($version);
577: print_r($links);
578: } else {
579: try {
580: $fm = $this->_fmPublish($version);
581: $fm = $this->_fmUpdateLinks($links);
582: } catch (Horde_Exception $e) {
583: print "Error publishing to FM:\n";
584: print $e->getMessage();
585: }
586: }
587:
588: $mailer = new Horde_Release_MailingList(
589: $module,
590: $this->notes['name'],
591: $this->_hordeVersionString,
592: $this->_options['ml']['from'],
593: isset($this->notes['list']) ? $this->notes['list'] : null,
594: $this->_ticketVersion,
595: $version['tag_list']
596: );
597:
598: $headers = $mailer->getHeaders();
599:
600: if (!empty($this->_options['noannounce'])) {
601: print "NOT announcing release on " . $headers['To'] . "\n";
602: } else {
603: print "Announcing release to " . $headers['To'] . "\n";
604: }
605:
606:
607: $mailer->append($this->notes['ml']['changes']);
608: if ($this->_oldVersion) {
609: $mailer->append("\n\n" .
610: sprintf('The full list of changes (from version %s) can be viewed here:', $this->_oldSourceVersionString) .
611: "\n\n" .
612: $url_changelog
613: );
614: }
615: $mailer->append("\n\n" .
616: sprintf('The %s %s distribution is available from the following locations:', $this->notes['name'], $this->_sourceVersionString) .
617: "\n\n" .
618: sprintf(' ftp://ftp.horde.org/pub/%s/%s', $module, $this->_tarballName) . "\n" .
619: sprintf(' http://ftp.horde.org/pub/%s/%s', $module, $this->_tarballName)
620: );
621: if ($this->_makeDiff) {
622: $mailer->append("\n\n" .
623: sprintf('Patches against version %s are available at:', $this->_oldSourceVersionString) .
624: "\n\n" .
625: sprintf(' ftp://ftp.horde.org/pub/%s/patches/%s.gz', $module, $this->_patchName) . "\n" .
626: sprintf(' http://ftp.horde.org/pub/%s/patches/%s.gz', $module, $this->_patchName)
627: );
628:
629: if (!empty($this->_binaryDiffs)) {
630: $mailer->append("\n\n" .
631: 'NOTE: Patches do not contain differences between files containing binary data.' . "\n" .
632: 'These files will need to be updated via the distribution files:' . "\n\n " .
633: implode("\n ", $this->_binaryDiffs)
634: );
635: }
636: }
637: $mailer->append("\n\n" .
638: 'MD5 sums for the packages are as follows:' .
639: "\n\n" .
640: ' ' . $this->_tarballMD5[0] . "\n" .
641: ' ' . $this->_patchMD5[0] .
642: "\n\n" .
643: 'Have fun!' .
644: "\n\n" .
645: 'The Horde Team.'
646: );
647:
648: if (!empty($this->_options['noannounce'])) {
649: print "Message headers:\n";
650: print_r($headers);
651: print "Message body:\n" . $mailer->getBody() . "\n";
652: return;
653: }
654:
655:
656: try {
657: $class = 'Horde_Mail_Transport_' . ucfirst($this->_options['mailer']['type']);
658: $mailer->getMail()->send(new $class($this->_options['mailer']['params']));
659: } catch (Horde_Mime_Exception $e) {
660: print $e->getMessage() . "\n";
661: }
662: }
663:
664: 665: 666: 667: 668: 669: 670:
671: protected function _fmPublish($params)
672: {
673: $key = $this->_options['fm']['user_token'];
674: $params['tag_list'] = implode(', ', $params['tag_list']);
675: $fm_params = array('auth_code' => $key,
676: 'release' => $params);
677: $http = new Horde_Http_Client();
678: try {
679: $response = $http->post('http://freecode.com/projects/' . $this->notes['fm']['project'] . '/releases.json',
680: Horde_Serialize::serialize($fm_params, Horde_Serialize::JSON),
681: array('Content-Type' => 'application/json'));
682: } catch (Horde_Http_Exception $e) {
683: if (strpos($e->getMessage(), '201 Created') === false) {
684: throw new Horde_Exception_Wrapped($e);
685: } else {
686: return '';
687: }
688: }
689:
690:
691: return $response->getBody();
692: }
693:
694: 695: 696:
697: public function _fmUpdateLinks($links)
698: {
699:
700:
701: $http = new Horde_Http_Client();
702: try {
703: $response = $http->get('http://freecode.com/projects/' . $this->notes['fm']['project'] . '/urls.json?auth_code=' . $this->_options['fm']['user_token']);
704: } catch (Horde_Http_Exception $e) {
705: throw new Horde_Exception_Wrapped($e);
706: }
707:
708: $url_response = Horde_Serialize::unserialize($response->getBody(), Horde_Serialize::JSON);
709: if (!is_array($url_response)) {
710: $url_response = array();
711: }
712:
713:
714:
715: foreach ($links as $link) {
716: $permalink = '';
717: foreach ($url_response as $url) {
718:
719:
720: $url = $url->url;
721: if ($link['label'] == $url->label) {
722: $permalink = $url->permalink;
723: break;
724: }
725: }
726: $link = array('auth_code' => $this->_options['fm']['user_token'],
727: 'url' => $link);
728: $http = new Horde_Http_Client();
729: if (empty($permalink)) {
730:
731: try {
732: $response = $http->post('http://freecode.com/projects/' . $this->notes['fm']['project'] . '/urls.json',
733: Horde_Serialize::serialize($link, Horde_Serialize::JSON),
734: array('Content-Type' => 'application/json'));
735: $response = $response->getBody();
736: } catch (Horde_Http_Exception $e) {
737: if (strpos($e->getMessage(), '201 Created') === false) {
738: throw new Horde_Exception_Wrapped($e);
739: } else {
740: $response = '';
741: }
742: }
743: } else {
744:
745: try {
746: $response = $http->put('http://freecode.com/projects/' . $this->notes['fm']['project'] . '/urls/' . $permalink . '.json',
747: Horde_Serialize::serialize($link, Horde_Serialize::JSON),
748: array('Content-Type' => 'application/json'));
749: $response = $response->getBody();
750:
751: } catch (Horde_Http_Exception $e) {
752: throw new Horde_Exception_Wrapped($e);
753: }
754: }
755: }
756:
757: return true;
758: }
759:
760: 761: 762:
763: public function test()
764: {
765: if (!$this->_options['test']) {
766: return;
767: }
768:
769: print "options['version']={$this->_options['version']}\n";
770: print "options['oldversion']={$this->_options['oldversion']}\n";
771: print "options['module']={$this->_options['module']}\n";
772: print "options['branch']={$this->_options['branch']}\n";
773:
774: $this->setVersionStrings();
775:
776: print "hordeVersionString={$this->_hordeVersionString}\n";
777: print "oldHordeVersionString={$this->_oldHordeVersionString}\n";
778: print "makeDiff={$this->_makeDiff}\n";
779: print "oldVersion={$this->_oldVersion}\n";
780: print "directoryName={$this->_directoryName}\n";
781: if ($this->_oldVersion) {
782: print "oldDirectoryName={$this->_oldDirectoryName}\n";
783: }
784: print "tagVersionString={$this->_tagVersionString}\n";
785: if ($this->_oldVersion) {
786: print "oldTagVersionString={$this->_oldTagVersionString}\n";
787: }
788: print "sourceVersionString={$this->_sourceVersionString}\n";
789: if ($this->_oldVersion) {
790: print "oldSourceVersionString={$this->_oldSourceVersionString}\n";
791: }
792: print "newSourceVersionString={$this->_newSourceVersionString}\n";
793: print "newSourceVersionStringPlain={$this->_newSourceVersionStringPlain}\n";
794: print "ticketVersion={$this->_ticketVersion}\n";
795: print "ticketVersionDesc=MODULE{$this->_ticketVersionDesc}\n";
796: if ($this->_latest) {
797: print "This is a production release\n";
798: }
799: exit(0);
800: }
801:
802: 803: 804:
805: public function addWhupsVersion()
806: {
807: if (!isset($this->notes)) {
808: print "\nNOT updating bugs.horde.org, RELEASE_NOTES missing.\n";
809: return;
810: }
811: $this->_ticketVersionDesc = $this->notes['name'] . $this->_ticketVersionDesc;
812:
813: $params = array('url' => 'https://dev.horde.org/horde/rpc.php',
814: 'user' => $this->_options['horde']['user'],
815: 'pass' => $this->_options['horde']['pass']);
816: $whups = new Horde_Release_Whups($params);
817:
818: if (!$this->_options['nowhups']) {
819: print "Adding new versions to bugs.horde.org: ";
820:
821: try {
822: $whups->addNewVersion($this->_options['module'], $this->_ticketVersion, $this->_ticketVersionDesc);
823: print "OK\n";
824: } catch (Horde_Exception $e) {
825: print "Failed:\n";
826: print $e->getMessage() . "\n";
827: }
828: } else {
829: print "NOT updating bugs.horde.org:\n";
830: print "New ticket version WOULD have been {$this->_ticketVersion}\n";
831: print "New ticket version description WOULD have been {$this->_ticketVersionDesc}\n";
832:
833:
834: try {
835: $queue = $whups->getQueueId($this->_options['module']);
836:
837: if ($queue === false) {
838: print "Was UNABLE to locate the queue id for {$this->_options['module']}\n";
839: } else {
840: print "The queue id on bugs.horde.org is $queue \n";
841: }
842: } catch (Horde_Exception $e) {
843: print "Will be UNABLE to update bugs.horde.org:\n";
844: print $e->getMessage() . "\n";
845: }
846: }
847: }
848:
849: 850: 851:
852: public function setVersionStrings()
853: {
854: $ver = explode('.', $this->_options['version']);
855: if (preg_match('/(\d+)\-(.*)/', $ver[count($ver) - 1], $matches)) {
856: $ver[count($ver) - 1] = $matches[1];
857: $plus = $matches[2];
858: }
859: if (preg_match('/(H\d)-(\d+)/', $ver[0], $matches)) {
860: $ver[0] = $matches[2];
861: $this->_hordeVersionString = $matches[1];
862: }
863: if (count($ver) > 2 && $ver[count($ver) - 1] == '0') {
864: die("version {$this->_options['version']} should not have the trailing 3rd-level .0\n");
865: }
866:
867:
868: if (!empty($this->_options['oldversion'])) {
869: $this->_oldVersion = true;
870: }
871: $oldver = explode('.', $this->_options['oldversion']);
872: if (preg_match('/(\d+)\-(.*)/', $oldver[count($oldver) - 1], $matches)) {
873: $oldver[count($oldver) - 1] = $matches[1];
874: $oldplus = $matches[2];
875: }
876: if (preg_match('/(H\d)-(\d+)/', $oldver[0], $matches)) {
877: $oldver[0] = $matches[2];
878: $this->_oldHordeVersionString = $matches[1];
879: }
880:
881:
882: $this->_tagVersionString = strtoupper($this->_options['module'] . '_' . preg_replace('/\W/', '_', implode('_', $ver)));
883: if (isset($plus)) {
884: $this->_tagVersionString .= '_' . $plus;
885: }
886:
887:
888: if ($this->_options['oldversion'] && $ver[0] == $oldver[0]) {
889: $this->_makeDiff = true;
890: }
891:
892:
893: if (isset($plus) && !preg_match('/^pl\d/', $plus)) {
894: $this->_latest = false;
895: }
896:
897:
898: $this->_sourceVersionString = implode('.', $ver);
899: if (isset($plus)) {
900: $this->_sourceVersionString .= '-' . $plus;
901: }
902:
903:
904: $this->_directoryName = $this->_options['module'] . '-';
905: if (!empty($this->_hordeVersionString)) {
906: $this->_directoryName .= $this->_hordeVersionString . '-';
907: }
908: $this->_directoryName = strtolower($this->_directoryName . $this->_sourceVersionString);
909:
910: if (!empty($this->_hordeVersionString)) {
911: $this->_sourceVersionString = $this->_hordeVersionString . ' (' . $this->_sourceVersionString . ')';
912: }
913:
914: if ($this->_oldVersion) {
915: $this->_oldSourceVersionString = implode('.', $oldver);
916: if (isset($oldplus)) {
917: $this->_oldSourceVersionString .= '-' . $oldplus;
918: }
919: $this->_oldTagVersionString = strtoupper($this->_options['module'] . '_' . implode('_', $oldver));
920: if (isset($oldplus)) {
921: $this->_oldTagVersionString .= '_' . $oldplus;
922: }
923: $this->_oldDirectoryName = strtolower($this->_options['module'] . '-' . $this->_oldHordeVersionString . $this->_oldSourceVersionString);
924: $this->_oldDirectoryName = $this->_options['module'] . '-';
925: if (!empty($this->_oldHordeVersionString)) {
926: $this->_oldDirectoryName .= $this->_oldHordeVersionString . '-';
927: }
928: $this->_oldDirectoryName = strtolower($this->_oldDirectoryName . $this->_oldSourceVersionString);
929:
930: if (!empty($this->_oldHordeVersionString)) {
931: $this->_oldSourceVersionString = $this->_oldHordeVersionString . ' (' . $this->_oldSourceVersionString . ')';
932: }
933: }
934:
935:
936: $this->_ticketVersion = implode('.', $ver);
937: if (!empty($plus)) {
938: $this->_ticketVersion .= '-' . $plus;
939: }
940:
941: if (!empty($this->_hordeVersionString)) {
942: $this->_ticketVersionDesc .= ' ' . $this->_hordeVersionString;
943: }
944:
945:
946: if ($this->_options['module'] == 'horde') {
947: $this->_ticketVersionDesc .= ' ' . implode('.', $ver);
948: } else {
949: $this->_ticketVersionDesc .= ' (' . implode('.', $ver) . ')';
950: }
951:
952:
953: if ($this->_latest) {
954: $this->_ticketVersionDesc .= ' Final';
955: } elseif (!empty($plus) &&
956: preg_match('/^RC(\d+)/', $plus, $matches)) {
957: $this->_ticketVersionDesc .= ' Release Candidate ' . $matches[1];
958:
959: } elseif (!empty($plus) && strtolower($plus) == 'alpha') {
960: $this->_ticketVersionDesc .= ' Alpha';
961: }
962:
963:
964:
965: if (!isset($plus)) {
966: while (count($ver) < 3) {
967: $ver[] = '0';
968: }
969: $ver[count($ver) - 1] += 1;
970: }
971: $this->_newSourceVersionString = implode('.', $ver) . '-cvs';
972: $this->_newSourceVersionStringPlain = $this->_newSourceVersionString;
973:
974: if (!empty($this->_hordeVersionString)) {
975: $this->_newSourceVersionString = $this->_hordeVersionString .
976: ' (' . $this->_newSourceVersionString . ')';
977: }
978:
979: }
980:
981: 982: 983:
984: public function getArguments()
985: {
986: global $argv;
987:
988:
989: array_shift($argv);
990: foreach ($argv as $arg) {
991: if (preg_match('/--module=(.*)/', $arg, $matches)) {
992:
993: $this->_options['module'] = $matches[1];
994:
995: } elseif (preg_match('/--version=(.*)/', $arg, $matches)) {
996:
997: $this->_options['version']= $matches[1];
998:
999: } elseif (preg_match('/--oldversion=(.*)/', $arg, $matches)) {
1000:
1001: $this->_options['oldversion']= $matches[1];
1002:
1003: } elseif (preg_match('/--branch=(.*)/', $arg, $matches)) {
1004:
1005: $this->_options['branch']= $matches[1];
1006:
1007: } elseif (strstr($arg, '--nocommit')) {
1008:
1009: $this->_options['nocommit']= true;
1010:
1011: } elseif (strstr($arg, '--noftp')) {
1012:
1013: $this->_options['noftp']= true;
1014:
1015: } elseif (strstr($arg, '--noannounce')) {
1016:
1017: $this->_options['noannounce']= true;
1018:
1019: } elseif (strstr($arg, '--nofreecode')) {
1020:
1021: $this->_options['nofreecode']= true;
1022:
1023: } elseif (strstr($arg, '--noticketversion')) {
1024:
1025: $this->_options['nowhups'] = true;
1026:
1027: } elseif (strstr($arg, '--dryrun')) {
1028:
1029: $this->_options['nocommit'] = true;
1030: $this->_options['noftp'] = true;
1031: $this->_options['noannounce'] = true;
1032: $this->_options['nowhups'] = true;
1033: $this->_options['nofreecode']= true;
1034:
1035: } elseif (strstr($arg, '--test')) {
1036:
1037: $this->_options['test']= true;
1038:
1039: $this->_options['nocommit'] = true;
1040: $this->_options['noftp'] = true;
1041: $this->_options['noannounce'] = true;
1042: $this->_options['nowhups'] = true;
1043: $this->_options['nofreecode']= true;
1044:
1045: } elseif (strstr($arg, '--help')) {
1046:
1047: $this->print_usage();
1048: exit;
1049:
1050: } else {
1051:
1052: $this->print_usage('You have used unknown arguments: ' . $arg);
1053: exit;
1054: }
1055: }
1056: }
1057:
1058: 1059: 1060:
1061: public function checkArguments()
1062: {
1063:
1064: if (!isset($this->_options['module'])) {
1065: $this->print_usage('You must define which module to package.');
1066: exit;
1067: }
1068:
1069:
1070: if (!isset($this->_options['version'])) {
1071: $this->print_usage('You must define which version to package.');
1072: exit;
1073: }
1074: if (!preg_match('/\d+\.\d+.*/', $this->_options['version'])) {
1075: $this->print_usage('Incorrect version string.');
1076: exit;
1077: }
1078: if (!isset($this->_options['oldversion'])) {
1079: $this->print_usage('You must define last release\'s version.');
1080: exit;
1081: }
1082: if (!preg_match('/\d+(\.\d+.*)?/', $this->_options['oldversion'])) {
1083: $this->print_usage('Incorrect old version string.');
1084: exit;
1085: }
1086:
1087:
1088: if (empty($this->_options['horde']['user'])) {
1089: $this->print_usage('You must define a horde.org user.');
1090: exit;
1091: }
1092:
1093:
1094:
1095: if (!isset($this->_options['branch'])) {
1096: $this->_options['branch'] = 'HEAD';
1097: }
1098: }
1099:
1100: 1101: 1102:
1103: public function checkSetSystem()
1104: {
1105:
1106: umask(022);
1107: }
1108:
1109: 1110: 1111:
1112: public function print_usage($message = null)
1113: {
1114: if (!is_null($message)) {
1115: print "\n*** ERROR: $message ***\n";
1116: }
1117:
1118: print <<<USAGE
1119:
1120: make-release.php: Horde release generator.
1121:
1122: This script takes as arguments the module to make a release of, the
1123: version of the release, and the branch:
1124:
1125: horde-make-release.php --module=<name>
1126: --version=[Hn-]xx.yy[.zz[-<string>]]
1127: --oldversion=[Hn-]xx[.yy[.zz[-<string>]]]
1128: [--branch=<branchname>] [--nocommit] [--noftp]
1129: [--noannounce] [--nofreecode] [--noticketversion]
1130: [--test] [--dryrun] [--help]
1131:
1132: If you omit the branch, it will implicitly work with the HEAD branch.
1133: If you release a new major version use the --oldversion=0 option.
1134: Use the --nocommit option to do a test build (without touching the CVS
1135: repository).
1136: Use the --noftp option to not upload any files on the FTP server.
1137: Use the --noannounce option to not send any release announcements.
1138: Use the --nofreecode option to not send any freecode announcements.
1139: Use the --noticketversion option to not update the version information on
1140: bugs.horde.org.
1141: The --dryrun option is an alias for:
1142: --nocommit --noftp --noannounce --nofreecode --noticketversion.
1143: The --test option is for debugging purposes only.
1144:
1145: EXAMPLES:
1146:
1147: To make a new development release of Horde:
1148: horde-make-release.php --module=horde --version=2.1-dev --oldversion=2.0
1149:
1150: To make a new stable release of Turba:
1151: horde-make-release.php --module=turba --version=H3-2.0.2 \
1152: --oldversion=H3-2.0.1 --branch=FRAMEWORK_3
1153:
1154: To make a new stable release of IMP 3:
1155: horde-make-release.php --module=imp --version=3.0 --oldversion=2.3.7 \
1156: --branch=RELENG_3
1157:
1158: To make a brand new Alpha/Beta/RC release of Luxor:
1159: horde-make-release.php --module=luxor --version=H3-1.0-ALPHA \
1160: --oldversion=0
1161:
1162: USAGE;
1163: }
1164:
1165: }
1166: