5 Patch maker for Gforge.
10 /**** START PROGRAM ****/
12 $STDIN = fopen('php://stdin','r');
13 define ("RED", "\033[01;31m" );
14 define ("NORMAL", "\033[00m" );
15 define ("GREEN", "\033[01;32m" );
16 define ("BLUE", "\033[00;36m" );
17 define ("BLINK", "\033[05m" );
18 define ("YELL", "\033[01;33m" );
19 define('FILE_APPEND', 1);
23 $cvspath = "/cvsroot/gforge";
24 $cvsmodule = "gforge"; //the name of the module from cvs repository
25 $authentication = "pserver"; //ext for it to ask you for your password
26 $cvsuser = "anonymous";
27 $cvsserver = "cvs.gforge.org";
28 $cvsroot = ":" . $authentication . ":" . $cvsuser . "@" . $cvsserver . ":" . $cvspath;
29 //this is the branch config for GFORGE (not the plugins)
30 $old_branch = "2005-11-08";
31 $old_branch_is_date = true; // if the old_branch is a date instead of a branch or tag, the old_branch should be something like 2005-07-10
33 $new_branch_is_date = false;
35 $only_parse_diffs = false; // leave false to generate the diff first
36 $only_test_patch = false; // only tests applyting the patch. the diffs must be generated and parsed already
37 $debug_patch_output = true;
41 $plugins[0]['name'] = "scmcvs";
42 $plugins[0]['old_branch'] = "upstream_version_4_5_2";
43 $plugins[0]['new_branch'] = "HEAD";
44 $plugins[0]['cvsmodule'] = "gforge-plugin-scmcvs";
45 $plugins[0]['new_branch_is_date'] = false;
46 $plugins[0]['old_branch_is_date'] = false;
48 $plugins[1]['name'] = "scmsvn";
49 $plugins[1]['old_branch'] = "2005-08-08";
50 $plugins[1]['new_branch'] = "HEAD";
51 $plugins[1]['cvsmodule'] = "gforge-plugin-scmsvn";
52 $plugins[1]['new_branch_is_date'] = false;
53 $plugins[1]['old_branch_is_date'] = true;
55 $plugins[2]['name'] = "cvstracker";
56 $plugins[2]['old_branch'] = "upstream_version_4_5_2";
57 $plugins[2]['new_branch'] = "v4_5_3";
58 $plugins[2]['cvsmodule'] = "gforge-plugin-cvstracker";
59 $plugins[2]['new_branch_is_date'] = false;
60 $plugins[2]['old_branch_is_date'] = false;
62 // The next one is an example of a plugin that requires different cvspath and must be checked out and worked with ext auth
64 $plugins[3]['name'] = "svntracker";
65 $plugins[3]['old_branch'] = "Branch_4_5";
66 $plugins[3]['new_branch'] = "HEAD";
67 $plugins[3]['cvsmodule'] = "gforge-plugin-svntracker";
68 $plugins[3]['new_branch_is_date'] = false;
69 $plugins[3]['old_branch_is_date'] = false;
70 $plugins[3]['cvspath'] = "/cvsroot/scmplugins"; // only set this var if it´s different than the main one
71 $plugins[3]['authentication'] = "ext"; // only set this var if it´s different than the main one
72 $plugins[3]['cvsuser'] = "danper"; // only set this var if it´s different than the main one
75 /* END OF CONFIG VARS */
77 // php.net function workaround because this is only on Php5
78 function file_put_contents($filename, $data, $flags = 0, $f = FALSE) {
79 if(($f===FALSE) && (($flags%2)==1)) $f=fopen($filename, 'a'); else if($f===FALSE) $f=fopen($filename, 'w');
80 if(round($flags/2)==1) while(!flock($f, LOCK_EX)) { /* lock */ }
81 if(is_array($data)) $data=implode('', $data);
83 if(round($flags/2)==1) flock($f, LOCK_UN);
89 * searchNextIndex -> Searchs for the next "Index:" line
91 * @param &array The file lines array
92 * @param int position in the array where this index is found
93 * @param int position where to begin searching from
94 * @param boolean Whether it´s a plugin diff or the main gforge plugin
95 * @param string The module name
96 * @param boolean true on success, false on EOF
99 function searchNextIndex(&$lines,&$position,$beginfrom,$isplugin,$module) {
101 while ( ($i <count($lines)) ) {
102 if (preg_match('/^Index:/',$lines[$i])) {
104 //if it´s a plugin, add the plugin relative path to the file path
105 $lines[$i] = "Index: " . "plugins/" . $module . "/" . substr($lines[$i],7);
116 * searchFirstModification -> Searchs for the next "Index:" line
118 * @param &array The file lines array
119 * @param int position in the array where the first "@@" line is found
120 * @param int position where to begin searching from
121 * @param boolean Whether it´s a plugin diff or the main gforge plugin
122 * @param string The module name
123 * @param boolean whether EOF was found instead of "@@" or "Index:" lines
124 * @param boolean true on success
128 function searchFirstModification(&$lines,&$firstmodposition,$beginfrom,$isplugin,$module,&$foundEOF) {
131 while ( ( $i < count($lines) ) ) {
134 //if it´s a plugin, add the plugin relative path to the file path
135 if (preg_match('/^--- /',$lines[$i])) {
136 $lines[$i] = "--- " . "plugins/" . $module . "/" . substr($lines[$i],4);
138 if (preg_match('/^\+\+\+ /',$lines[$i])) {
139 $lines[$i] = "+++ " . "plugins/" . $module . "/" . substr($lines[$i],4);
143 if (preg_match('/^@@/',$lines[$i])) {
144 $firstmodposition = $i;
154 * searchSecondModification -> Searchs for the next "Index:" line
156 * @param &array The file lines array
157 * @param int position in the array where the second "@@" line is found (if it is)
158 * @param int position in the array where the first "@@" line was found
159 * @param boolean whether a new index was found instead of a second "@@" line
160 * @param if $foundnewindex is true, this indicates the position where this new index has been found
161 * @param boolean whether EOF was found instead of "@@" or "Index:" lines
162 * @param boolean whether a "version" line was found in the first "@@" section
163 * @param int position where to begin searching from
164 * @param boolean Whether it´s a plugin diff or the main gforge plugin
165 * @param string The module name
166 * @param boolean true on success
169 function searchSecondModification(&$lines,&$secondmodposition,$firstmodposition,&$foundnewindex,&$new_indexposition,&$foundEOF,&$foundversion,$isplugin,$module) {
170 $i = $firstmodposition + 1;
171 $foundversion = false;
173 $foundnewindex = false;
174 while ( ($i <count($lines)) ) {
175 if (preg_match('/^@@/',$lines[$i])) {
176 $secondmodposition = $i;
179 $j = $i+1; //this doesn´t work... -> $lines[($i+1)]
180 if ( (preg_match('/@version[\s|\t]+\$Id$lines[$i])) && (preg_match('/@version[\s|\t]+\$Id$lines[$j])) ) { // only if there´s a - version and a + version (changed version)
181 $foundversion = true;
184 if (preg_match('/^Index:/',$lines[$i])) {
185 $new_indexposition = $i;
186 $foundnewindex = true;
188 //if it´s a plugin, add the plugin relative path to the file path
189 $lines[$i] = "Index: " . "plugins/" . $module . "/" . substr($lines[$i],7);
200 * parseDiffs -> read the files in the current dir and parse the .diff files
204 function parseDiffs() {
206 //open current directory for reading
207 $handle=opendir(".");
208 while ($filename = readdir($handle)) {
209 //Don't add special directories '..' or '.' to the list
210 if (($filename!='..') && ($filename!='.') && (strstr($filename,".diff") && (filesize($filename)>0))) { // only get the diff files
211 if (!strstr($filename,"gforge")) {
216 echo "Parsing file $filename ...\n";
217 parseDiff($filename,$isplugin);
224 * testPatch -> simulates a patching process
226 * @param array the plugins info
227 * @param boolean Output the patching process?
229 * @return boolean true on success
232 function testPatch($plugins,$debug) {
233 global $old_branch_is_date,$old_branch,$cvsmodule;
235 echo YELL . "Performing test of the created patch. Proceeding to update CVS to old versions...\n" . NORMAL;
236 //now we change the plugins to update to the old branch instead.
237 for ($i=0;$i<count($plugins);$i++) {
238 $plugins[$i]['new_branch'] = $plugins[$i]['old_branch'];
239 $plugins[$i]['new_branch_is_date'] = $plugins[$i]['old_branch_is_date'];
241 updateAll($old_branch,$old_branch_is_date,$plugins,1);
242 exec("patch --dry-run -p0 < ../GFORGEPATCH",$output,$return_value);
244 foreach ($output as $out) {
245 echo BLUE . $out . "\n" . NORMAL;
248 if ($return_value!=0) {
257 * joinDiffs -> just grabs the parsed diffs and joins them into 1 big file
261 function joinDiffs() {
263 //open current directory for reading
264 $handle=opendir(".");
265 echo "Creating joined file GFORGEPATCH ...\n";
266 while ($filename = readdir($handle)) {
267 //Don't add special directories '..' or '.' to the list
268 if (($filename!='..') && ($filename!='.') && (strstr($filename,".diff") && (strstr($filename,"parsed-")) && (filesize($filename)>0))) { // only get the diff files
269 file_put_contents("GFORGEPATCH",file_get_contents($filename),FILE_APPEND);
273 if (is_file("GFORGEPATCH")) {
274 echo GREEN . "File GFORGEPATCH created...\n" . NORMAL;
276 echo RED . "File GFORGEPATCH couldn´t be created successfully\n" . NORMAL;
282 * parseDiff -> update the plugins from cvs
284 * @param string The file name
285 * @param boolean Whether it´s a plugin diff or the main gforge plugin
286 * @param boolean true on success, false on failure
289 function parseDiff($filename,$isplugin) {
290 if (! ($vals = file_get_contents($filename)) ){
291 echo RED . "Failed to open file $filename \n" . NORMAL;
294 $lines = explode("\n",$vals); //now we have the lines in an array
295 for ($i=0;$i<count($lines);$i++) {
296 $linestowrite[$i] = 1; // write ALL lines first...
299 $arr = explode(".diff",$filename);
300 $module = $arr[0]; // get the module name (in case it´s a plugin)
304 // we are going to suppose the files are WELL FORMED (not index without @@ lines, has at least 1 Index, etc)
305 searchNextIndex($lines,$indexposition,$beginfrom,$isplugin,$module);
306 if ($indexposition>0) {
307 //remove the " ? filename" lines
308 for ($i=0;$i<$indexposition;$i++) {
309 $linestowrite[$i] = 0;
313 searchFirstModification($lines,$firstmodposition,$indexposition,$isplugin,$module,$foundEOF); //search for first @@ line
314 //weird condition -> i found one diff that had an index and no @@... the file hadn´t been modified. the program crashed. this fixes it
316 //ignore this last index and finish
317 for ($i=$indexposition;$i<count($lines);$i++) {
318 //mark this section as allowed
319 $linestowrite[$i] = 0;
323 //echo $firstmodposition . " ";
324 searchSecondModification($lines,$secondmodposition,$firstmodposition,$foundnewindex,$new_indexposition,$foundEOF,$foundversion,$isplugin,$module); //search for next @@ line (maybe we just find the next index or EOF)
325 //echo $new_indexposition . " ";
326 $foundversion?$value=0:$value=1; // if we found the "version" CVS stuff change we set it at not to be written
327 if ($foundnewindex) {
328 for ($i=$indexposition;$i<$firstmodposition;$i++) {
329 //mark this section as allowed
330 $linestowrite[$i] = $value;
332 $indexposition = $new_indexposition;
333 for ($i=$firstmodposition;$i<$indexposition;$i++) {
334 //mark this section as allowed
335 $linestowrite[$i] = $value;
337 } else { //didn´t find new index
338 if ($foundEOF) { //we have reached EOF, write all from secondmodposition to the end
339 for ($i=$firstmodposition;$i<count($lines);$i++) {
340 //mark this section as allowed
341 $linestowrite[$i] = $value;
344 } else { //found next set of @@
345 for ($i=$indexposition;$i<$firstmodposition;$i++) {
346 //mark this section as allowed
347 $linestowrite[$i] = 1;
349 for ($i=$firstmodposition;$i<$secondmodposition;$i++) {
350 //mark this section as forbidden or allowed
351 $linestowrite[$i] = $value;
353 $found = searchNextIndex($lines,$indexposition,$secondmodposition,$isplugin,$module); //search for the next index
355 //we have reached EOF, write all from secondmodposition to the end
356 for ($i=$secondmodposition;$i<count($lines);$i++) {
357 //mark this section as allowed
358 $linestowrite[$i] = 1;
362 //we have reached the next "Index:" line, write all from secondmodposition to this point
363 for ($i=$secondmodposition;$i<$indexposition;$i++) {
364 //mark this section as allowed
365 $linestowrite[$i] = 1;
374 $fd = fopen("parsed-" . $filename,"w+");
376 echo RED . "Failed to open file parsed-$filename \n" . NORMAL;
380 for ($i=0;$i<count($lines);$i++) {
381 if ($linestowrite[$i] == 1) {
382 fwrite($fd,$lines[$i] . "\n");
387 echo GREEN. "File parsed-" . $filename . " wrote successfully\n" . NORMAL;
391 * updateAll -> updates gforge and the plugins from cvs
393 * @param string Branch to update to
394 * @param boolean Whether the branch is a date
395 * @param array of plugins
396 * @param boolean is this a test?
397 * @return boolean true on success, exits on failure
400 function updateAll($branch,$branch_is_date,$plugins,$test) {
401 global $cvsmodule,$cvsroot;
403 echo "Updating to " . $branch . " branch...\n";
407 if ($branch_is_date) {
408 exec("cvs -Q -d " . $cvsroot . " update -dP -D $branch 2>>/tmp/patchmaker-errorlog",$out,$return_value); // returns 0 on success... that´s why the return_value var
410 exec("cvs -Q -d " . $cvsroot . " update -dP -r $branch 2>>/tmp/patchmaker-errorlog",$out,$return_value); // returns 0 on success... that´s why the return_value var
413 if ($return_value != 0) {
414 die(RED . "Could not update from cvs, exiting...\n" . NORMAL);
417 echo GREEN . "Module " . $cvsmodule . " updated successfully.\n" . NORMAL;
419 if (!empty($plugins)) {
420 if (!is_dir('plugins')) {
421 if (!mkdir('plugins')) {
422 die(RED . "Could not create dir plugins, exiting...\n" . NORMAL);
424 echo "Dir plugins Created.\n";
426 echo "Dir plugins already exists.\n";
429 if (!updatePlugins($plugins,!$test)) {
437 * updatePlugins -> update the plugins from cvs
439 * @param array Plugins that will be updated
440 * @param boolean Whether to create the diffs or just update. True by default
441 * @return boolean true on success, false on failure
444 function updatePlugins($plugins,$creatediffs=true) {
445 global $authentication,$cvsuser,$cvsserver,$cvspath;
448 foreach ($plugins as $plugin) {
449 //if the plugin has different cvspath use it, else use the original one
450 ($plugin['cvspath'])?$path=$plugin['cvspath']:$path=$cvspath;
451 ($plugin['authentication'])?$auth=$plugin['authentication']:$auth=$authentication;
452 ($plugin['cvsuser'])?$user=$plugin['cvsuser']:$user=$cvsuser;
453 $cvsroot = ":" . $auth . ":" . $user . "@" . $cvsserver . ":" . $path;
455 if (!(is_dir($plugin['name']))) {
456 //only checkout if there´s nothing in here
457 echo "Checking out " . $plugin['cvsmodule'] . "...\n";
458 exec("cvs -Q -d " . $cvsroot . " checkout " . $plugin['cvsmodule'] . " 2>>/tmp/patchmaker-errorlog",$out,$return_value);
459 if ($return_value != 0) {
460 die(RED . "Could not checkout module " . $plugin['cvsmodule'] . " from cvs, exiting...\n" . NORMAL);
462 echo GREEN . "Module " . $plugin['cvsmodule'] . " checked out successfully.\n" . NORMAL;
464 if (exec("mv " . $plugin['cvsmodule'] . " " . $plugin['name'])) { //mv returns 0 on succes...
465 die(RED . "Could not rename the dir " . $plugin['cvsmodule'] . ", exiting...\n");
467 echo "Dir " . $plugin['cvsmodule'] . " renamed to " . $plugin['name'] . "\n";
469 echo BLUE . "Module " . $plugin['cvsmodule'] . " has already been checked out before.\n" . NORMAL;
471 echo "Updating " . $plugin['name'] . " to " . $plugin['new_branch'] . " branch...\n";
472 chdir ($plugin['name']);
473 if ($plugin['new_branch_is_date']) {
474 exec("cvs -Q -d " . $cvsroot . " update -dP -D " . $plugin['new_branch'] . " 2>>/tmp/patchmaker-errorlog",$out,$return_value); // returns 0 on success... that´s why the return_value var
476 exec("cvs -Q -d " . $cvsroot . " update -dP -r " . $plugin['new_branch'] . " 2>>/tmp/patchmaker-errorlog",$out,$return_value); // returns 0 on success... that´s why the return_value var
478 if ($return_value != 0) {
479 die("Could not update" . $plugin['name'] . "from cvs, exiting...\n");
481 echo GREEN . "Module " . $plugin['name'] . " updated successfully.\n" . NORMAL;
483 echo "Creating the diff for module " . $plugin['name'] . "...\n";
484 if ($plugin['new_branch_is_date']) {
485 if ($plugin['old_branch_is_date']) {
486 exec("cvs -Q -d " . $cvsroot . " diff -BbuN -D " . $plugin['old_branch'] . " -D " . $plugin['new_branch'] . " > ../../../" . $plugin['name'] . ".diff 2>>/tmp/patchmaker-errorlog");
488 exec("cvs -Q -d " . $cvsroot . " diff -BbuNr " . $plugin['old_branch'] . " -D " . $plugin['new_branch'] . " > ../../../" . $plugin['name'] . ".diff 2>>/tmp/patchmaker-errorlog");
491 if ($plugin['old_branch_is_date']) {
492 exec("cvs -Q -d " . $cvsroot . " diff -BbuN -D " . $plugin['old_branch'] . " -r " . $plugin['new_branch'] . " > ../../../" . $plugin['name'] . ".diff 2>>/tmp/patchmaker-errorlog");
494 exec("cvs -Q -d " . $cvsroot . " diff -BbuNr " . $plugin['old_branch'] . " -r " . $plugin['new_branch'] . " > ../../../" . $plugin['name'] . ".diff 2>>/tmp/patchmaker-errorlog");
497 if ((!(file_exists("../../../".$plugin['name'].".diff"))) || ((filesize("../../../".$plugin['name'].".diff")) < 1) ){
498 if ((filesize("../../../".$plugin['name'].".diff")) < 1) {
499 echo BLUE . "The diff for " . $plugin['name'] . " is empty... ---> " . $plugin['name'] . ".diff" . "\n" . NORMAL;
500 unlink("../../../" . $plugin['name'] . ".diff");
502 die("Could not create the diff, exiting...\n");
505 echo GREEN . "Diff created successfully ---> " . $plugin['name'] . ".diff" . ".\n" . NORMAL;
508 chdir (".."); // return to plugins dir
510 chdir (".."); //return to main module dir
516 if ($only_parse_diffs) {
523 if ($only_test_patch) {
525 if (testPatch($plugins,$debug_patch_output)) {
526 echo GREEN . "GFORGEPATCH file applied successfully, you can distribute the file\n" . NORMAL ;
528 echo RED . "GFORGEPATCH file couldn´t be applied correctly, please check the errors and correct manually\n" . NORMAL ;
533 // get the cvs version of new branch
535 echo "Creating Dir " . $new_branch . " ...\n";
538 if (is_dir(getcwd() . "/" . $new_branch)) {
539 echo BLUE . "Dir " . $new_branch . " already exists.\n" . NORMAL;
540 echo BLUE . "The files are going to be changed. Continue? (Y/N) :" . NORMAL;
541 $sure = fread($STDIN,1);
542 if ( ($sure!='y') && ($sure!='Y') ) {
543 die(RED . "Exiting...\n");
550 if (!mkdir(getcwd() . "/" . $new_branch)) {
551 die(RED . "Could not create dir $new_branch, exiting...\n");
553 echo "Dir " . $new_branch . " Created.\n";
558 if (!(is_dir($cvsmodule))) {
559 //only checkout if there´s nothing in here
560 echo "Checking out " . $cvsmodule . "...\n";
561 exec("cvs -Q -d " . $cvsroot . " checkout $cvsmodule 2>>/tmp/patchmaker-errorlog",$out,$return_value);
562 if ($return_value != 0) {
563 die("Could not checkout from cvs, exiting...\n");
565 echo GREEN . "Module " . $cvsmodule . " checked out successfully.\n" . NORMAL;
567 echo BLUE . "Module " . $cvsmodule . " has already been checked out before.\n" . NORMAL;
570 updateAll($new_branch,$new_branch_is_date,$plugins,0);
572 echo "Creating the diff for module " . $cvsmodule . "...\n";
574 if ($new_branch_is_date) {
575 if ($old_branch_is_date) {
576 exec("cvs -Q -d " . $cvsroot . " diff -BbuN -D $old_branch -D $new_branch > ../$cvsmodule.diff");
578 exec("cvs -Q -d " . $cvsroot . " diff -BbuNr $old_branch -D $new_branch > ../$cvsmodule.diff");
581 if ($old_branch_is_date) {
582 exec("cvs -Q -d " . $cvsroot . " diff -BbuN -D $old_branch -r $new_branch > ../$cvsmodule.diff");
584 exec("cvs -Q -d " . $cvsroot . " diff -BbuNr $old_branch -r $new_branch > ../$cvsmodule.diff");
589 if ((!(file_exists("$cvsmodule.diff"))) || ((filesize("$cvsmodule.diff")) < 1) ){
590 if ((filesize("$cvsmodule.diff")) < 1) {
591 echo BLUE . "The diff for " . $cvsmodule . " is empty...in " . getcwd() . $cvsmodule . ".diff" . ".\n" . NORMAL;
593 die(RED . "Could not create the diff, exiting...\n" . NORMAL);
596 echo GREEN . "Diff created successfully ---> " . $cvsmodule . ".diff" . ".\n" . NORMAL;
601 if (testPatch($plugins,$debug_patch_output)) {
602 echo GREEN . "GFORGEPATCH file applied successfully, you can distribute the file\n" . NORMAL ;
604 echo RED . "GFORGEPATCH file couldn´t be applied correctly, please check the errors and correct manually\n" . NORMAL ;
609 /**** END PROGRAM ****/