PageRenderTime 92ms CodeModel.GetById 69ms app.highlight 18ms RepoModel.GetById 1ms app.codeStats 0ms

/app/vendors/fbollon/commands.php

http://fredistrano.googlecode.com/
PHP | 545 lines | 396 code | 79 blank | 70 comment | 100 complexity | b2837d3bc66d39a664416095f549abc7 MD5 | raw file
  1<?php 
  2/* SVN FILE: $Id: commands.php 374 2009-10-12 21:49:11Z fbollon $ */
  3/**
  4 * Wrap all used shell commands in a static PHP function
  5 * 
  6 * PHP 5
  7 *
  8 * Licensed under The MIT License
  9 * Redistributions of files must retain the above copyright notice.
 10 *
 11 * @filesource
 12 * @link			http://code.google.com/p/fredistrano
 13 * @package			app
 14 * @subpackage		app.vendors.fbollon
 15 * @version			$Revision: 374 $
 16 * @modifiedby		$Author: fbollon $
 17 * @lastmodified	$Date: 2009-10-12 23:49:11 +0200 (Mon, 12 Oct 2009) $
 18 * @license			http://www.opensource.org/licenses/mit-license.php The MIT License
 19 */
 20/**
 21 * Wrap all used shell commands in a static PHP function
 22 *
 23 * @package		app
 24 * @subpackage	app.vendors.fbollon
 25 */
 26class Utils {
 27	
 28	public static function computeDirMode( $fileMode, $options = array() ) {
 29		$fileMode = str_split($fileMode);
 30		$dirMode = '';
 31	
 32		for ($i=0; $i < 3; $i++) { 
 33			if ($fileMode[$i] > 7) {
 34				$fileMode[$i] = 7;
 35			} elseif (!in_array($fileMode[$i], array(0,7))) {
 36				$fileMode[$i]++;
 37			} 
 38			$dirMode .= $fileMode[$i];
 39		}
 40		return $dirMode; 
 41	}// computeDirMode
 42	
 43	public static function getSvnRevisionFromOutput($output, $options = array() ) {
 44		preg_match('/ ([0-9]+)\.$/', $output, $matches);
 45		if (isset($matches[1])) {
 46			return $matches[1];
 47		} else {
 48			return false;
 49		}
 50	}// getRevisionFromOutput
 51	
 52	/**
 53	 * Convert if necessary a path to a cygwin/linux format 
 54	 * @param string $path 		Path to be converted
 55	 * @return string 			Converted path
 56	 */ 
 57	public static function formatPath($path) {
 58		$pathForRsync = $path;
 59		if ( F_OS == 'WIN') {
 60			$pattern = '/^([A-Za-z]):/';
 61			preg_match($pattern, $path, $matches, PREG_OFFSET_CAPTURE);
 62			if (!empty ($matches[1][0])) {
 63				$windowsLetter = strtolower($matches[1][0]);
 64				$pathForRsync = strtr( Configure::read('Cygwin.rootDir') . $windowsLetter . substr($path, 2), "\\", "/");
 65			}	
 66		}
 67		return $pathForRsync;
 68	}// formatPath
 69	
 70}// FileSystemCommand
 71
 72class Action {
 73	
 74	public function initAction($name=null, $comment=null, $type=null, $options = array() ) {
 75		if ( isset($options['stepLog']) && get_class($options['stepLog']) == 'StepLog' ) {
 76			$actionLog =  $options['stepLog']->addNewAction($name, $comment , $type);
 77		} else if ( isset($options['actionLog']) && get_class($options['actionLog']) == 'ActionLog' ) {
 78			$actionLog =  $options['actionLog'];
 79		} else {
 80			$actionLog =  new ActionLog($name, $comment , $type);
 81		}
 82		return $actionLog;
 83	}// initAction
 84	
 85}// Action
 86
 87class PhpAction extends Action {
 88	
 89	// Create files list and directories list for chmod step
 90	public function createFilesListToChmod($output=null, $projectTmpDir=null, $target=null, $options = array()) {
 91		$actionLog = self::initAction('createFilesListToChmod', 'files_to_chmod.txt and dir_to_chmod.txt', 'PhpAction', $options);
 92		
 93		if (empty($output) || empty($projectTmpDir) || empty($target)) {
 94			$actionLog->error( sprintf(__('Missing working data', true)) );
 95		}
 96		
 97		$list = array_reverse(explode("\n", $output));
 98		$size = count($list);
 99		if ($size > 0) {
100			$files_to_chmod = $projectTmpDir."files_to_chmod.txt";
101			$dir_to_chmod = $projectTmpDir."dir_to_chmod.txt";
102			$handle_f = fopen($files_to_chmod, "w");
103			$handle_d = fopen($dir_to_chmod, "w");
104
105			for ($i = 4; $i < $size ; $i++) {
106				if (empty($list[$i]) || strpos($list[$i],'deleting ') !== false) {
107					break;
108				}
109		
110				if (is_file($target . $list[$i])) {
111					$tmp_str = $list[$i];
112					fwrite($handle_f, $target.str_replace(".".Configure::read('FileSystem.renameExt').".", ".", $list[$i]) . "\n");
113				} else if (is_dir($target . $list[$i])){
114					fwrite($handle_d, $target.$list[$i] . "\n");
115				}
116			}
117			fclose($handle_f);
118			fclose($handle_d);
119		}
120		// End action
121		$actionLog->end();
122		
123		return $actionLog;
124	}// createFilesListToChmod
125	
126	public static function loadConfig(& $config, $projectName = null, $options = array()) {
127		$actionLog = self::initAction('loadConfig', null, 'PhpAction', $options);
128		
129		if (empty($projectName)) {
130			$actionLog->error(__('Invalid project',true));
131		}
132		
133		// Check new path
134		$path = F_DEPLOYTMPDIR.$projectName.DS.'tmpDir'.DS.'.fredistrano'.DS.'deploy.php';
135		if ( !file_exists( $path ) ) {
136			$path = F_DEPLOYTMPDIR.$projectName.DS.'tmpDir'.DS.'deploy.php';
137			if (!file_exists( $path )) {
138				$actionLog->error( sprintf(__('Unable to find deploy.php', true)) );
139			}
140		} 
141		include_once($path);
142		$config = new DEPLOY_CONFIG();
143		
144		// End action
145		$actionLog->end();
146		
147		return $actionLog;
148	}// loadConfig
149	
150}// PhpAction
151
152class ShellAction extends Action {
153
154	public static function changePermissions( $path=null, $mode=array(), $options = array() ) {
155		$actionLog = self::initAction('resetPermission', null, 'ShellAction', $options);	
156		if (!is_array($mode)) {
157			$mode = array(
158				'file' 	=> $mode,
159				'dir' 	=> Utils::computeDirMode($mode)
160			);
161		}
162		
163		// Check
164		if ( is_null($path) || empty($mode) ) {
165			$actionLog->error(sprintf(__('Forbidden NULL value for input parameter [%s;%s]',true),$path,$mode));	
166		}
167		if (!file_exists($path)) {
168			$actionLog->error(sprintf(__('Path [%s] not found',true),$path));	
169		}
170		$path = Utils::formatPath($path);
171
172		if (isset($mode['file'])) {
173			// Change file mode
174			$desc = sprintf(__('Reseting permissions files on %s',true),$path);
175			$actionLog->description = $desc;
176			$command = "find ".$path." -type f -exec chmod ".$mode['file']." {} \;";
177			ShellAction::executeCommand( $command,
178				array(
179			        'directory'	=> $path,
180					'actionLog' => $actionLog
181				)
182			);
183		}
184
185		if (isset($mode['dir'])) {
186			// Change directory mode
187			$desc = sprintf(__('Resetting directories permissions on %s',true),$path);
188			$actionLog->description = $desc;
189			$command = "find ".$path." -type d -exec chmod ".$mode['dir']." {} \;";
190			ShellAction::executeCommand( $command,
191				array(
192					'directory'	=> $path,
193					'actionLog' => $actionLog
194				)
195			);
196		}
197		
198		// Terminate action
199		$actionLog->end();
200		
201		return $actionLog;
202	}// changePermissions
203	
204	public static function createDirectory( $path=null, $mode=null, $options = array() ) {
205		$comment = sprintf(__('Creating %s with mode %s',true),$path,$mode);
206		$actionLog = self::initAction('createDirectory', $comment, 'ShellAction', $options);
207		
208		// Check
209		if ( is_null($path) || is_null($mode) ) {
210			$actionLog->error(sprintf(__('Forbidden NULL value for input parameter [%s;%s]',true),$path,$mode));	
211		} else if (is_dir($path)) {
212			$actionLog->error(sprintf(__('Directory %s already exists',true),$path));
213		}
214		
215		// Execute
216		// $path = Utils::formatPath($path);
217		if (!@mkdir($path, octdec( $mode ), TRUE)) {
218			$actionLog->error( sprintf(__('Unable to create directory %s', true), $path) );
219		}
220		
221		// Terminate action
222		$actionLog->end();
223		
224		return $actionLog;
225	}// createDirectory
226
227	public static function executeCommand( $command = null, $options = array() ){
228		$defaultOptions = array(
229			'comment' 		=> null,
230			'directory'		=> null,
231			'commandForLog'	=> null
232		);
233		$options = array_merge($defaultOptions, $options);
234
235		// Log management
236		$actionLog = self::initAction('executeCommand', $options['comment'], 'ShellAction', $options);
237		if ( !isset($options['actionLog']) || get_class($options['actionLog']) != 'ActionLog' ) {
238			// Terminate log if passed in params
239			$terminate = false;
240		} else {
241			$terminate = true;
242		}
243		
244		// Check
245		if ( is_null($command) ) {
246			$actionLog->error(sprintf(__('Forbidden NULL value for input parameter [%s]',true),$command));	
247		} 
248
249		// Prepare command
250		if ( F_OS == 'WIN' ) {
251			if (!is_null($options['directory'])) {
252				$cd = 'cd '.Utils::formatPath( $options['directory'] ).'; ';
253			} else {
254				$cd = '';
255			}
256			$prefix = "bash.exe --login -c \"".$cd;
257			$suffix = "\"";
258		} else {
259			if (!is_null($options['directory'])) {
260				chdir($options['directory']);
261			}
262			$prefix = "";
263			$suffix = "";
264		}
265		
266		// Execute command 
267		$output = utf8_encode(shell_exec($prefix.$command.$suffix));
268		
269		if (!is_null($options['commandForLog'])) {
270			$commandForLog = $options['commandForLog'];
271		}else{
272			$commandForLog = $prefix.$command.$suffix;
273		}
274		$actionLog->saveCommand($commandForLog, $output);
275		 
276		// End action
277		if ($terminate) {
278			$actionLog->end($output);
279		}
280		
281		return $actionLog;
282	}// executeCommand
283	
284	public static function remove( $path, $recursive = false , $options = array() ) {
285		$comment = sprintf(__('Deleting content under %s (recursivity=%d)',true), $path, $recursive);
286		$actionLog = self::initAction('remove', $comment, 'ShellAction', $options);
287		
288		// Check
289		if (!is_dir($path)) {
290			$actionLog->error( sprintf(__('Nothing to delete since no temporary files have been found', true)) );
291		} 
292
293		// Execute command 
294		$path = Utils::formatPath($path);
295		$recStr = $recursive?'r':'';
296		$command = "rm -f".$recStr." ".$path;
297		ShellAction::executeCommand( $command, array('actionLog' => $actionLog) );
298
299		// End action
300		$actionLog->end();
301		
302		return $actionLog;
303	}// remove
304	
305	public static function runScript( $type, $projectTmpDir, $scriptPath, $options ) {
306		$comment = sprintf(__('Executing script %s',true), $scriptPath);
307		$actionLog = self::initAction('runScript', $comment, 'ShellAction', $options);
308		
309		if (!isset($scriptPath) || !$scriptPath) {
310			$actionLog->error( sprintf(__('Script not found', true)) );
311		}
312		
313		// Run script
314		if ($options['run'.ucfirst($type).'Script']) {
315			if (!file_exists($scriptPath) && file_exists($projectTmpDir.'tmpDir'.DS.'.fredistrano'.DS.$scriptPath)) {
316				$scriptPath = $projectTmpDir.'tmpDir'.DS.'.fredistrano'.DS.$scriptPath;
317			} else if (!file_exists($scriptPath)){
318				$actionLog->error( __('Script not found', true) );
319			}
320			
321			$formatedScriptPath = Utils::formatPath($scriptPath);
322			if (!is_executable($scriptPath)) {
323				$log = ShellAction::executeCommand( "chmod u+x $formatedScriptPath", 
324					array(
325						'comment'	=> __('Execution privileges to script',true),
326						'actionLog' => $actionLog
327					)
328				);	
329			}
330			
331			ShellAction::executeCommand( $formatedScriptPath,
332				array(
333					'comment'	=> sprintf(__('%s script',true), $type),
334					'actionLog' => $actionLog
335				)
336			);
337		}
338		// End action
339		$actionLog->end();
340		
341		return $actionLog;
342	}// runScript
343	
344	public static function synchronizeContent( $source = null, $target = null, $options = array ()) {
345		$comment = sprintf(__('Synchronizing %s with %s',true),$source,$target);
346		$actionLog = self::initAction('synchronizeContent', $comment, 'ShellAction', $options);
347
348		// Setting up Rsync options
349		if ($options['simulation'] === true) {
350			// Simulation mode
351			$option = 'rtvn';
352		} else {
353			// Live mode
354			$option = 'rtv';
355		}
356		
357		$optionalOptions = Configure::read('Rsync.optionalOptions');
358		if (!empty($optionalOptions)) {
359			$option .= $optionalOptions;
360		}
361		
362		// Execute command		
363		$excludeFileName = Utils::formatPath( $options['exclude'] );
364		$source = Utils::formatPath( $source );
365		$target = Utils::formatPath( $target );
366	
367		$command = "rsync -$option --delete --exclude-from=$excludeFileName $source $target 2>&1";	
368		ShellAction::executeCommand( $command, array('actionLog' => $actionLog));
369		
370		// End action
371		$actionLog->end();
372		
373		return $actionLog;
374	}// synchronizeContent
375	
376}//
377
378class SvnAction extends Action {
379	
380	public static function checkout( $svnUrl = null, $path = null, $targetDir = null, $options = array() ) {
381		$actionLog = self::initAction('checkout',$svnUrl,'SvnAction',$options);
382		$configDirectory = '';
383		// Check
384		if ( is_null($svnUrl) || is_null($path) || is_null($targetDir)) {
385			$actionLog->error(sprintf(__('Forbidden NULL value for input parameter [%s;%s;%s]',true),$svnUrl,$path,$targetDir));	
386		} else if (!is_dir($path)) {
387			$actionLog->error(sprintf(__('Directory %s not found',true),$path));
388		} else if (is_dir($path.DS.$targetDir))  {
389			$actionLog->error(sprintf(__('Delete target %s directory first',true), $path.DS.$targetDir));			
390		}
391			
392		// Define step options
393		$default_options = array(
394			'revision' 			=> null,
395			'user_svn' 			=> null,
396			'password_svn' 		=> null,
397			'configDirectory'	=> null,
398			'parseResponse' 	=> false
399		);
400		$options = array_merge($default_options, $options);
401		
402		// Execute command 
403		$path = Utils::formatPath($path);
404		$revision = !is_null($options['revision'])?' -r' . $options['revision']:'';
405		$authentication = '';
406		if (!is_null($options['user_svn'])) {
407			$authentication .= '--username '.$options['user_svn'];
408			if (!empty($options['password_svn'])) {
409				$authentication .= ' --password '.$options['password_svn'];
410			}
411		}
412		if (!is_null($options['configDirectory'])) {
413			$configDirectory = '--config-dir '.Utils::formatPath( $options['configDirectory'] );
414		}
415		$command = "svn checkout --non-interactive $configDirectory $revision $authentication $svnUrl $targetDir 2>&1";	
416		$commandForLog = "svn checkout --non-interactive $configDirectory $revision XXXX $svnUrl $targetDir 2>&1";		
417		ShellAction::executeCommand( $command,
418			array(
419		        'directory'		=> $path,
420				'actionLog' 	=> $actionLog,
421				'commandForLog'	=> $commandForLog
422			)
423		);
424		// Parse output
425		if ( !(strpos( $actionLog->getResult() , 'PROPFIND request failed on' ) === false) && $options['parseResponse'] ) {
426			$actionLog->error(__('An error has been detected in the SVN output',true));			
427		}
428		
429		// End action
430		$actionLog->end();
431		
432		return $actionLog;
433	}// checkout
434	
435	public static function export( $svnUrl = null, $path = null, $targetDir = null, $options = array() ) {
436		$actionLog = self::initAction('export',$svnUrl,'SvnAction',$options);
437		$configDirectory = '';
438		
439		if ( is_null($svnUrl) || is_null($path) || is_null($targetDir)) {
440			$actionLog->error(sprintf(__('Forbidden NULL value for input parameter [%s;%s;%s]',true),$svnUrl,$path,$targetDir));	
441		} else if (!is_dir($path)) {
442			$actionLog->error(sprintf(__('Directory %s not found',true),$path));
443		} else if (is_dir($path.DS.$targetDir))  {
444			$actionLog->error(sprintf(__('Delete target %s directory first',true), $path.DS.$targetDir));			
445		}
446		
447		// Define step options
448		$default_options = array(
449			'revision' 			=> null,
450			'user_svn' 			=> null,
451			'password_svn' 		=> null,
452			'configDirectory'	=> null,
453			'parseResponse' 	=> false
454		);
455		$options = array_merge($default_options, $options);
456		
457		// Execute command 
458		$path = Utils::formatPath($path);
459		$revision = !is_null($options['revision'])?' -r' . $options['revision']:'';
460		$authentication = '';
461		if (!is_null($options['user_svn'])) {
462			$authentication .= '--username '.$options['user_svn'];
463			if (!empty($options['password_svn'])) {
464				$authentication .= ' --password '.$options['password_svn'];
465			}
466		}
467		if (!is_null($options['configDirectory'])) {
468			$configDirectory = '--config-dir '.Utils::formatPath( $options['configDirectory'] );
469		}
470		$command = "svn export --non-interactive $configDirectory $revision $authentication $svnUrl $targetDir 2>&1";
471		$commandForLog = "svn export --non-interactive $configDirectory $revision XXXX $svnUrl $targetDir 2>&1";		
472		ShellAction::executeCommand( $command,
473			array(
474		        'directory'	=> $path,
475				'actionLog' => $actionLog,
476				'commandForLog'	=> $commandForLog
477			)
478		);
479		// Parse output
480		if ( !(strpos( $actionLog->getResult() , 'PROPFIND request failed on' ) === false) && $options['parseResponse'] ) {
481			$actionLog->error(__('An error has been detected in the SVN output',true));
482		}
483		
484		// End action
485		$actionLog->end();
486		
487		return $actionLog;
488	}// export
489	
490	public static function update( $projectDir, $options = array() ) {
491		$actionLog = self::initAction('update',$projectDir,'SvnAction',$options);
492		$configDirectory = '';
493		if ( is_null($projectDir) ) {
494			$actionLog->error(sprintf(__('Forbidden NULL value for input parameter [%s]',true),$projectDir));	
495		} else if (!is_dir($projectDir) ) {
496			$actionLog->error(sprintf(__('Directory %s not found',true), $projectDir));
497		} else if ( !is_dir($projectDir.DS.'.svn') ) {
498			$actionLog->error(sprintf(__('Specified directory %s is not a working copy',true), $projectDir.DS.'.svn'));
499		} else if ( !is_writeable($projectDir) ) {
500			$actionLog->error(sprintf(__('Specified directory %s is not writeable',true), $projectDir));
501		}
502 			
503		// Define step options
504		$default_options = array(
505			'revision' 			=> null,
506			'configDirectory'	=> null,
507			'parseResponse' 	=> false
508		);
509		$options = array_merge($default_options, $options);
510		
511		// Execute command 
512		$projectDir = Utils::formatPath($projectDir);
513		$revision = ($options['revision']!=null)?' -r' . $options['revision']:'';
514		$authentication = '';
515		if (!is_null($options['user_svn'])) {
516			$authentication .= '--username '.$options['user_svn'];
517			if (!empty($options['password_svn'])) {
518				$authentication .= ' --password '.$options['password_svn'];
519			}
520		}
521		if (!is_null($options['configDirectory'])) {
522			$configDirectory = '--config-dir '.Utils::formatPath( $options['configDirectory'] );
523		}
524		$command = "svn update --non-interactive $configDirectory $revision $authentication 2>&1";
525		$commandForLog = "svn update --non-interactive $configDirectory $revision XXXX 2>&1";		
526		ShellAction::executeCommand( $command, array(
527				'directory'	=> $projectDir,
528				'actionLog' => $actionLog,
529				'commandForLog'	=> $commandForLog
530			) 
531		);
532		
533		// Parse response
534		if ( !(strpos( $actionLog->getResult() , 'PROPFIND request failed on' ) === false) && $options['parseResponse'] ) {
535			$actionLog->error(__('An error has been detected in the SVN output',true));
536		}
537		
538		// End action
539		$actionLog->end();
540		return $actionLog;
541	}// update
542	
543}// SvnAction
544
545?>