Browse Source

Safe compilation (works in a temporary directory, on success then move it into env-production)

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@2835 a333f486-631f-4898-b8df-5754b55c2be0
romainq 11 years ago
parent
commit
4d0409bc01
2 changed files with 95 additions and 17 deletions
  1. 57 17
      setup/compiler.class.inc.php
  2. 38 0
      setup/setuputils.class.inc.php

+ 57 - 17
setup/compiler.class.inc.php

@@ -58,8 +58,48 @@ class MFCompiler
 		return $this->aLog;
 	}
 	
+
 	public function Compile($sTargetDir, $oP = null, $bUseSymbolicLinks = false)
 	{
+		$sFinalTargetDir = $sTargetDir;
+		if ($bUseSymbolicLinks)
+		{
+			// Skip the creation of a temporary dictionary, not compatible with symbolic links
+			$sTempTargetDir = $sFinalTargetDir;
+		}
+		else
+		{
+			// Create a temporary directory
+			// Once the compilation is 100% successful, then move the results into the target directory
+			$sTempTargetDir = tempnam(SetupUtils::GetTmpDir(), 'itop-');
+			unlink($sTempTargetDir); // I need a directory, not a file...
+			SetupUtils::builddir($sTempTargetDir); // Here is the directory
+		}
+
+		try
+		{
+			$this->DoCompile($sTempTargetDir, $sFinalTargetDir, $oP = null, $bUseSymbolicLinks);
+		}
+		catch (Exception $e)
+		{
+			if ($sTempTargetDir != $sFinalTargetDir)
+			{
+				// Cleanup the temporary directory
+				SetupUtils::rrmdir($sTempTargetDir);
+			}
+			throw $e;
+		}
+
+		if ($sTempTargetDir != $sFinalTargetDir)
+		{
+			// Move the results to the target directory
+			SetupUtils::movedir($sTempTargetDir, $sFinalTargetDir);
+		}
+	}
+	
+
+	protected function DoCompile($sTempTargetDir, $sFinalTargetDir, $oP = null, $bUseSymbolicLinks = false)
+	{
 		$aAllClasses = array(); // flat list of classes
 
 		// Determine the target modules for the MENUS
@@ -108,7 +148,7 @@ class MFCompiler
 				$sModuleRootDir = realpath($sModuleRootDir);
 				$sRelativeDir = basename($sModuleRootDir);
 				// Push the other module files
-				SetupUtils::copydir($sModuleRootDir, $sTargetDir.'/'.$sRelativeDir, $bUseSymbolicLinks);
+				SetupUtils::copydir($sModuleRootDir, $sTempTargetDir.'/'.$sRelativeDir, $bUseSymbolicLinks);
 			}
 
 			$sCompiledCode = '';
@@ -127,7 +167,7 @@ class MFCompiler
 					$aAllClasses[] = $sClass;
 					try
 					{
-						$sCompiledCode .= $this->CompileClass($oClass, $sTargetDir, $sRelativeDir, $oP);
+						$sCompiledCode .= $this->CompileClass($oClass, $sTempTargetDir, $sFinalTargetDir, $sRelativeDir, $oP);
 					}
 					catch (DOMFormatException $e)
 					{
@@ -187,7 +227,7 @@ EOF;
 					}
 					try
 					{
-						$sCompiledCode .= $this->CompileMenu($oMenuNode, $sTargetDir, $sRelativeDir, $oP);
+						$sCompiledCode .= $this->CompileMenu($oMenuNode, $sTempTargetDir, $sFinalTargetDir, $sRelativeDir, $oP);
 					}
 					catch (DOMFormatException $e)
 					{
@@ -209,7 +249,7 @@ EOF;
 			{
 				// We have compiled something: write the result file
 				//
-				$sResultFile = $sTargetDir.'/'.$sRelativeDir.'/model.'.$sModuleName.'.php';
+				$sResultFile = $sTempTargetDir.'/'.$sRelativeDir.'/model.'.$sModuleName.'.php';
 				if (is_file($sResultFile))
 				{
 					$this->Log("Updating $sResultFile for module $sModuleName in version $sModuleVersion ($iClassCount classes)");
@@ -264,7 +304,7 @@ EOF;
 
 		// Compile the dictionaries -out of the modules
 		//
-		$sDictDir = $sTargetDir.'/dictionaries';
+		$sDictDir = $sTempTargetDir.'/dictionaries';
 		if (!is_dir($sDictDir))
 		{
 			$this->Log("Creating directory $sDictDir");
@@ -274,9 +314,9 @@ EOF;
 		$oDictionaries = $this->oFactory->ListActiveChildNodes('dictionaries', 'dictionary');
 		foreach($oDictionaries as $oDictionaryNode)
 		{
-			$this->CompileDictionary($oDictionaryNode, $sTargetDir);
+			$this->CompileDictionary($oDictionaryNode, $sTempTargetDir, $sFinalTargetDir);
 		}
-	}
+	} // Compile()
 
 	/**
 	 * Helper to form a valid ZList from the array built by GetNodeAsArrayOfItems()
@@ -483,7 +523,7 @@ EOF;
 		return $sRet;
 	}
 
-	protected function CompileClass($oClass, $sTargetDir, $sModuleRelativeDir, $oP)
+	protected function CompileClass($oClass, $sTempTargetDir, $sFinalTargetDir, $sModuleRelativeDir, $oP)
 	{
 		$sClass = $oClass->getAttribute('id');
 		$oProperties = $oClass->GetUniqueElement('properties');
@@ -570,7 +610,7 @@ EOF;
 			$aClassParams['display_template'] = "utils::GetAbsoluteUrlModulesRoot().'$sDisplayTemplate'";
 		}
 	
-		$this->CompileFiles($oProperties, $sTargetDir.'/'.$sModuleRelativeDir, '');
+		$this->CompileFiles($oProperties, $sTempTargetDir.'/'.$sModuleRelativeDir, $sFinalTargetDir.'/'.$sModuleRelativeDir, '');
 		if (($sIcon = $oProperties->GetChildText('icon')) && (strlen($sIcon) > 0))
 		{
 			$sIcon = $sModuleRelativeDir.'/'.$sIcon;
@@ -1000,9 +1040,9 @@ EOF;
 	}// function CompileClass()
 
 
-	protected function CompileMenu($oMenu, $sTargetDir, $sModuleRelativeDir, $oP)
+	protected function CompileMenu($oMenu, $sTempTargetDir, $sFinalTargetDir, $sModuleRelativeDir, $oP)
 	{
-		$this->CompileFiles($oMenu, $sTargetDir.'/'.$sModuleRelativeDir, $sModuleRelativeDir);
+		$this->CompileFiles($oMenu, $sTempTargetDir.'/'.$sModuleRelativeDir, $sFinalTargetDir.'/'.$sModuleRelativeDir, $sModuleRelativeDir);
 
 		$sMenuId = $oMenu->getAttribute("id");
 		$sMenuClass = $oMenu->getAttribute("xsi:type");
@@ -1054,7 +1094,7 @@ EOF;
 					$oDefNode = $oXMLDoc->importNode($oNode, true); // layout, cells, etc Nodes and below
 					$oRootNode->appendChild($oDefNode);
 				}
-				$oXMLDoc->save($sTargetDir.'/'.$sModuleRelativeDir.'/'.$sFileName);
+				$oXMLDoc->save($sTempTargetDir.'/'.$sModuleRelativeDir.'/'.$sFileName);
 			}
 			$sNewMenu = "new DashboardMenuNode('$sMenuId', $sTemplateSpec, $sParentSpec, $fRank);";
 			break;
@@ -1429,7 +1469,7 @@ EOF;
 	return $sPHP;
 	} // function CompileUserRights
 
-	protected function CompileDictionary($oDictionaryNode, $sTargetDir)
+	protected function CompileDictionary($oDictionaryNode, $sTempTargetDir, $sFinalTargetDir)
 	{
 		$sLang = $oDictionaryNode->getAttribute('id');
 		$sEnglishLanguageDesc = $oDictionaryNode->GetChildText('english_description');
@@ -1458,13 +1498,13 @@ $sEntriesPHP
 ));
 EOF;
 		$sSafeLang = str_replace(' ', '-', strtolower(trim($sLang)));
-		$sDictFile = $sTargetDir.'/dictionaries/'.$sSafeLang.'.dict.php';
+		$sDictFile = $sTempTargetDir.'/dictionaries/'.$sSafeLang.'.dict.php';
 		file_put_contents($sDictFile, $sPHPDict);
 	}
 
 	// Transform the file references into the corresponding filename (and create the file in the relevant directory)
 	//
-	protected function CompileFiles($oNode, $sTargetDir, $sRelativePath)
+	protected function CompileFiles($oNode, $sTempTargetDir, $sFinalTargetDir, $sRelativePath)
 	{
 		$oFileRefs = $oNode->GetNodes(".//fileref");
 		foreach ($oFileRefs as $oFileRef)
@@ -1482,8 +1522,8 @@ EOF;
 				$sData = base64_decode($oNodes->item(0)->GetChildText('data'));
 				$aPathInfo = pathinfo($sName);
 				$sFile = 'icon-file'.$iFileId.'.'.$aPathInfo['extension'];
-				$sFilePath = $sTargetDir.'/images/'.$sFile;
-				@mkdir($sTargetDir.'/images');
+				$sFilePath = $sTempTargetDir.'/images/'.$sFile;
+				@mkdir($sTempTargetDir.'/images');
 				file_put_contents($sFilePath, $sData);
 				if (!file_exists($sFilePath))
 				{

+ 38 - 0
setup/setuputils.class.inc.php

@@ -623,6 +623,44 @@ class SetupUtils
 			return false;
 		}
 	}
+
+	/**
+	 * Helper to move a directory when the parent directory of the target dir cannot be written
+	 * To be used as alternative to rename()	 	 
+	 * Files/Subdirs of the source directory are moved one by one
+	 * Returns void
+	 */
+	public static function movedir($sSource, $sDest)
+	{
+		if (!is_dir($sSource))
+		{
+			throw new Exception("movedir: the source directory '$sSource' is not a valid directory or cannot be read");
+		}
+		if (!is_dir($sDest))
+		{
+			self::builddir($sDest);
+		}
+		else
+		{
+			self::tidydir($sDest);
+		}
+
+		$aFiles = scandir($sSource);
+		if(sizeof($aFiles) > 0)
+		{
+			foreach($aFiles as $sFile)
+			{
+				if ($sFile == '.' || $sFile == '..')
+				{
+					// Skip
+					continue;
+				}
+				rename($sSource.'/'.$sFile, $sDest.'/'.$sFile);
+			}
+		}
+		rmdir($sSource);
+	}
+
 	static function GetPreviousInstance($sDir)
 	{
 		$bFound = false;