Skip to content
Snippets Groups Projects
Select Git revision
  • 40f15e03f932d44ece888a417b8435af4ebeaf57
  • master default protected
  • simple-task/7248-eol-check-add-node-22
  • 6877_check_iml_deployment
4 results

check_systemdunit

Blame
  • project.class.php 32.80 KiB
    <?php
    
    /**
     * class for single project
     */
    class project {
        // ----------------------------------------------------------------------
        // CONFIG
        // ----------------------------------------------------------------------
    
        /**
         * config file
         * @var type 
         */
        private $_sCfgfile = "../config/inc_projects_config.php";
    
        /**
         * configuration ($aConfig in the config file)
         * @var array
         */
        private $_aConfig = array();
    
        /**
         * configuration of the project (= $aProjects[ID] in the config file)
         * @var array
         */
        private $_aPrjConfig = array();
    
        /**
         * version infos of all phases
         * @var type 
         */
        private $_aData = array();
    
        /**
         * existing versions in the archive dir
         * @var type 
         */
        private $_aVersions = array();
        
        /**
         * places of version infos in each deployment phase
         * @var type 
         */
        private $_aPlaces = array(
            "onhold" => "Queue",
            "ready4deployment" => "Repo",
            "deployed" => "Installiert",
        );
        private $_iRcAll = 0;
    
        // ----------------------------------------------------------------------
        // constructor
        // ----------------------------------------------------------------------
        
        /**
         * constructor
         * @param string $sId  id of the project
         */
        public function __construct($sId = false) {
            $this->_readConfig();
            if ($sId)
                $this->setProjectById($sId);
        }
    
        // ----------------------------------------------------------------------
        // private functions
        // ----------------------------------------------------------------------
    
        /**
         * read default config file
         * @return boolean
         */
        private function _readConfig() {
            require(__dir__ . '/' . $this->_sCfgfile);
            $this->_aConfig = $aConfig;
            return true;
        }
    
        /**
         * validate config data
         * @return boolean
         */
        private function _verifyConfig() {
            if (!count($this->_aPrjConfig))
                die("ERROR::CONFIG: no config was for found the project. check $aProjects in config file.");
    
            if (!array_key_exists("packageDir", $this->_aConfig))
                die("ERROR::CONFIG: packagedir is not defined.");
            if (!$this->_aConfig["packageDir"])
                die("ERROR::CONFIG: packagedir is not set.");
            if (!file_exists($this->_aConfig["packageDir"]))
                die("ERROR::CONFIG: packagedir does not exist: &quot;" . $this->_aConfig['packageDir'] . "&quot;.");
    
            if (!array_key_exists("archiveDir", $this->_aConfig))
                die("ERROR::CONFIG: key &quot;archiveDir&quot; was not found in config.");
            if (!$this->_aConfig["archiveDir"])
                die("ERROR::CONFIG: archiveDir is not set.");
            if (!file_exists($this->_aConfig["archiveDir"]))
                die("ERROR::CONFIG: archiveDir does not exist: &quot;" . $this->_aConfig['archiveDir'] . "&quot;.");
    
            if (!array_key_exists("phases", $this->_aPrjConfig))
                die("ERROR::CONFIG: key &quot;phases&quot; was not found in config.<br><pre>" . print_r($this->_aPrjConfig, true) . "</pre>");
    
            // TODO: verify ausbauen
            return true;
        }
    
        /**
         * execute a commandline; returns a string of output of timestamp, command, output and returncode
         * @param string $sCommand
         * @return string
         */
        private function _execAndSend($sCommand) {
            $sReturn = '';
            $bUseHtml = $_SERVER ? true : false;
    
            ob_implicit_flush(true);
            // ob_end_flush();
            $descriptorspec = array(
                0 => array("pipe", "r"), // stdin is a pipe that the child will read from
                1 => array("pipe", "w"), // stdout is a pipe that the child will write to
                2 => array("pipe", "w")    // stderr is a pipe that the child will write to
            );
            flush();
            $process = proc_open($sCommand, $descriptorspec, $pipes, realpath('./'), array());
    
            $sReturn.="[" . date("H:i:s d.m.Y") . "] ";
            $sReturn.=$bUseHtml ? "<strong>$sCommand</strong>" : "$sCommand";
            $sReturn.=$bUseHtml ? "<br>" : "\n";
    
            $sErrors = false;
            if (is_resource($process)) {
                while ($s = fgets($pipes[1])) {
                    $sReturn.=$s;
                    flush();
                }
                while ($s = fgets($pipes[2])) {
                    $sErrors.=$s;
                    flush();
                }
            }
            if ($sErrors)
                $sReturn.="STDERR:\n" . $sErrors;
            $oStatus = proc_get_status($process);
            $iRc = $oStatus['exitcode'];
            $this->_iRcAll += $iRc;
            $sReturn.="[" . date("H:i:s d.m.Y") . "] exitcode " . $iRc;
            if ($bUseHtml) {
                if ($iRc == 0) {
                    $sReturn = "<pre>" . $sReturn;
                } else {
                    $sReturn = '<pre class="error">' . $sReturn;
                }
                $sReturn.='</pre>';
            }
    
            flush();
            return $sReturn;
        }
    
        // ----------------------------------------------------------------------
        // GETTER
        // ----------------------------------------------------------------------
    
        /**
         * get a temp directory
         * @return string
         */
        private function _getTempDir() {
            return $s = $this->_getBuildDir() . '/' . $this->_aPrjConfig["fileprefix"] . "_" . date("Ymd-His");
        }
    
        private function _getBuildDir(){
            return $this->_aConfig['buildDir'].'/'.$this->_aConfig["id"];
        }
        
        /**
         * get directory for infofile and package (without extension)
         * @param string $sPhase  one of preview|stage|live ...
         * @param string $sPlace  one of onhold|ready4deployment|deployed
         * @return string
         */
        private function _getFileBase($sPhase, $sPlace) {
            if (!array_key_exists($sPhase, $this->_aConfig["phases"])) {
                die("ERROR: _getFileBase - this phase does not exist: $sPhase.");
            }
            if (!array_key_exists($sPlace, $this->_aPlaces)) {
                die("ERROR: _getFileBase - this place does not exist: $sPhase.");
            }
    
    
            // local file for onhold|ready4deployment
            $sBase = $this->_aConfig['packageDir'] . "/" . $sPhase . "/" . $this->_aPrjConfig["fileprefix"];
    
            if ($sPlace == "onhold")
                $sBase.="_onhold";
            // $sBase .= "/" . $this->_aPrjConfig["fileprefix"];
    
            // url for deployed
            if ($sPlace == "deployed") {
                if ($this->isActivePhase($sPhase) && array_key_exists("url", $this->_aPrjConfig["phases"][$sPhase])) {
                    $sBase = $this->_aPrjConfig["phases"][$sPhase]["url"] . $this->_aPrjConfig["fileprefix"];
                } else {
                    $sBase = '';
                }
            }
    
            return $sBase;
        }
    
        /**
         * get filename for info file
         * @param string $sPhase  one of preview|stage|live ...
         * @param string $sPlace  one of onhold|ready4deployment|deployed
         * @return string
         */
        private function _getInfofile($sPhase, $sPlace) {
            $sBase = $this->_getFileBase($sPhase, $sPlace);
            return $sBase ? $sBase .'/'.$this->_aPrjConfig["fileprefix"] . '.json' : false;
        }
    
        /**
         * get filename for package file
         * @param string $sPhase  one of preview|stage|live ...
         * @param string $sPlace  one of onhold|ready4deployment|deployed
         * @return string
         */
        private function _getPackagefile($sPhase, $sPlace) {
            $sBase = $this->_getFileBase($sPhase, $sPlace);
            return $sBase ? $sBase .'/'.$this->_aPrjConfig["fileprefix"] . '.tgz' : false;
        }
    
        private function _getArchiveDir($sTimestamp) {
            if (!$sTimestamp) {
                die("ERROR: getArchiveDir timestamp is required");
            }
            return $this->_getProjectArchiveDir() . '/' . $sTimestamp;
        }
        
        /**
         * get the directory for archive files of this project
         * @return string
         */
        public function _getProjectArchiveDir() {
            return $this->_aConfig["archiveDir"] . '/' . $this->_aConfig["id"];
        }
    
        /**
         * get all existing versions in archive and its usage
         * versions are array keys; where they are used is written in values
         * @return array
         */
        public function getVersions(){
            
            // --- read all file entries
            $aReturn=array();
            $sDir=$this->_getProjectArchiveDir();
            foreach (scandir($this->_getProjectArchiveDir()) as $sEntry){
                if (is_dir($sDir.'/'.$sEntry) && $sEntry != '.' && $sEntry != '..') $aReturn[$sEntry]=false;
            }
            $this->_aVersions=$aReturn;
    
            // --- check version in all phases 
            $this->getAllPhaseInfos();
            foreach ($this->_aData["phases"] as $sPhase=>$aData){
                foreach(array_keys($this->_aPlaces) as $sPlace){
                    if (array_key_exists($sPlace, $aData) && array_key_exists("version", $aData[$sPlace])){
                        $this->_aVersions[$aData[$sPlace]["version"]][]=$sPlace;
                    }
                }
            }
            ksort($this->_aVersions);
            return $this->_aVersions;
        }
    
        /**
         * recursive delete 
         * @param type $dir
         * @return type
         */
        private function _rmdir($dir) {
            foreach (scandir($dir) as $sEntry){
                if (is_dir($dir.'/'.$sEntry) && $sEntry != '.' && $sEntry != '..') {
                    $this->_rmdir($dir.'/'.$sEntry);
                }
                else
                    unlink($dir.'/'.$sEntry);
            }
            return rmdir($dir);
        }
    
        /**
         * cleanup of archive directory; it returns the list of deleted
         * directories as array
         * @return array
         */
        public function cleanupArchive(){
            $aUnused=array();
            $sDir=$this->_getProjectArchiveDir();
            $this->getVersions();
            
            $aDelete=array();
            // find unused versions
            foreach ($this->_aVersions as $sVersion=>$aUsage){
                if (!$aUsage || count($aUsage)==0){
                    $aUnused[]=$sVersion;
                }
            }
            
            // keep a few
            while (count($aUnused)>$this->_aConfig["versionsToKeep"]){
                $sVersion=array_shift($aUnused);
                $sDir2=$sDir.'/'.$sVersion;
                if ($this->_rmdir($sDir2)){
                    $aDelete[]=$sDir2;
                } else {
                    echo "Warning: unable to delete Archive $sDir2<br>";
                };
            }
            
            // rescan versions
            if (count($aDelete)) {
                $this->getVersions();
            }
            
            return $aDelete;
        }
        
        /**
         * cleanup of archive directory; it returns the list of deleted
         * directories as array
         * @return array
         */
        public function cleanupBuilds(){
            $sDir=$this->_getBuildDir();
            $aDirlist=array();
            $aDelete=array();
            foreach (scandir($sDir) as $sEntry){
                if (is_dir($sDir.'/'.$sEntry) && $sEntry != '.' && $sEntry != '..') $aDirlist[]=$sEntry;
            }
            
            // keep a few
            while (count($aDirlist)>$this->_aConfig["builtsToKeep"]){
                $sVersion=array_shift($aDirlist);
                $sDir2=$sDir.'/'.$sVersion;
                if ($this->_rmdir($sDir2)){
                    $aDelete[]=$sDir2;
                } else {
                    echo "Warning: unable to delete Build $sDir2<br>";
                };
            }
            
            return $aDelete;
        }
        
        /**
         * get conmplete config of the project
         * @return array
         */
        public function getConfig() {
            return $this->_aPrjConfig;
        }
    
        /**
         * get name/ label of the project
         * @return string
         */
        public function getLabel() {
            return $this->_aPrjConfig["label"];
        }
    
        /**
         * get description of the project
         * @return string
         */
        public function getDescription() {
            return $this->_aPrjConfig["description"];
        }
    
        /**
         * get deploy and queue infos for all phases
         * @return type
         */
        public function getAllPhaseInfos() {
            if (!array_key_exists("phases", $this->_aData))
                $this->_aData["phases"] = array();
    
            foreach (array_keys($this->_aConfig["phases"]) as $sPhase) {
                if (!array_key_exists($sPhase, $this->_aData["phases"])) {
                    $this->getPhaseInfos($sPhase);
                }
            }
            return $this->_aData["phases"];
        }
    
        /**
         * get statusinfos of a named phase
         * @param string $sPhase name of the phase; one of preview|stage|live
         * @return array
         */
        public function getPhaseInfos($sPhase) {
            if (!$sPhase){
                die("ERROR: project->getPhaseInfos - parameter for phase is required");
            }
            if (!array_key_exists("phases", $this->_aData))
                $this->_aData["phases"] = array();
    
            if (!array_key_exists($sPhase, $this->_aData["phases"])) {
    
                $this->_aData["phases"][$sPhase] = array();
                $aTmp = array();
    
                // a blocked package is waiting for deployment timeslot?
                $sKey = "onhold";
                $sJsonfile = $this->_getInfofile($sPhase, $sKey);
                $aTmp[$sKey] = array();
                if (file_exists($sJsonfile)) {
                    $aJson = json_decode(file_get_contents($sJsonfile), true);
                    if (array_key_exists("timestamp", $aJson)) {
                        $aTmp[$sKey] = $aJson;
                        $aTmp[$sKey]["infofile"] = $sJsonfile;
                        $aTmp[$sKey]["version"] = $aJson["timestamp"];
                        $aTmp[$sKey]["ok"] = 1;
                    } else {
                        $aTmp[$sKey]["error"] = "info file $sJsonfile exists but is corrupt (no timestamp)." . print_r($aJson, true);
                    }
                } else {
                    $aTmp[$sKey]["info"] = "No package is waiting in the queue.";
                    $aTmp[$sKey]["ok"] = 1;
                }
    
                // package for puppet
                $sKey = "ready4deployment";
                $sJsonfile = $this->_getInfofile($sPhase, $sKey);
                $aTmp[$sKey] = array();
                if (file_exists($sJsonfile)) {
                    $sPkgfile = $this->_getPackagefile($sPhase, $sKey);
                    if (file_exists($sPkgfile)) {
                        $aJson = json_decode(file_get_contents($sJsonfile), true);
                        if (array_key_exists("timestamp", $aJson)) {
                            $aTmp[$sKey] = $aJson;
                            $aTmp[$sKey]["infofile"] = $sJsonfile;
                            $aTmp[$sKey]["packagefile"] = $sPkgfile;
                            $aTmp[$sKey]["version"] = $aJson["timestamp"];
                            $aTmp[$sKey]["ok"] = 1;
                        } else {
                            $aTmp[$sKey]["error"] = "info file $sJsonfile exists but is corrupt (no timestamp)." . print_r($aJson, true);
                        }
                    } else {
                        $aTmp[$sKey]["error"] = "package file was not found: $sPkgfile";
                    }
                } else {
                    $aTmp[$sKey]["error"] = "info file was not found: $sJsonfile";
                }
    
                // published data
                $sKey = "deployed";
                $sJsonfile = $this->_getInfofile($sPhase, $sKey);
                $aTmp[$sKey] = array();
                if ($this->isActivePhase($sPhase)) {
                    $sJsonUrl = $this->_aPrjConfig["phases"][$sPhase]["url"] . $this->_aPrjConfig["fileprefix"] . ".json";
                    $sJsonData = @file_get_contents($sJsonUrl);
                    if ($sJsonData) {
                        $aJson = json_decode($sJsonData, true);
                        if (array_key_exists("timestamp", $aJson)) {
                            $aTmp[$sKey] = $aJson;
                            $aTmp[$sKey]["infofile"] = $sJsonUrl;
                            $aTmp[$sKey]["version"] = $aJson["timestamp"];
                            $aTmp[$sKey]["ok"] = 1;
                        } else {
                            $aTmp[$sKey]["error"] = "json url was readable <a href=$sJsonUrl>$sJsonUrl</a> but is corrupt (no timestamp)." . print_r($aJson, true);
                        }
                    } else {
                        $aTmp[$sKey]["error"] = "json url not readable <a href=$sJsonUrl>$sJsonUrl</a>";
                    }
                } else {
                    $aTmp[$sKey]["warning"] = "this phase is not active or has no url";
                }
    
                $this->_aData["phases"][$sPhase] = $aTmp;
            }
            return $this->_aData["phases"][$sPhase];
        }
    
        /**
         * check if the given phase is active for this project
         * @param type $sPhase
         * @return type
         */
        public function isActivePhase($sPhase) {
            return (
                    array_key_exists($sPhase, $this->_aPrjConfig["phases"]) 
                    && array_key_exists("url", $this->_aPrjConfig["phases"][$sPhase]) 
                    && $this->_aPrjConfig["phases"][$sPhase]["url"]
                    );
        }
    
        /**
         * find the next active phase of a project
         * @param string $sPhase current phase; if empty the function sends back the first phase
         */
        public function getNextPhase($sPhase = false) {
            if ($sPhase) {
                if (!array_key_exists($sPhase, $this->_aConfig["phases"])) {
                    die("ERROR: this phase does not exist: $sPhase.");
                }
            }
    
            $sNextPhase = false;
            $bUseNextPhase = $sPhase ? false : true;
            foreach (array_keys($this->_aConfig["phases"]) as $s) {
                if ($bUseNextPhase) {
                    if ($this->isActivePhase($s)) {
                        $sNextPhase = $s;
                        $bUseNextPhase = false;
                        continue;
                    }
                }
                if ($sPhase == $s) {
                    $bUseNextPhase = true;
                }
            }
    
            return $sNextPhase;
        }
    
        /**
         * check: is the deployment to the next phase enabled for this phase?
         * @param type $sPhase
         */
        public function canAcceptPhase($sPhase = false) {
    
            if (!$sPhase) {
                $sNext = $this->getNextPhase($sPhase);
                return $sNext > '';
            }
    
    
            if (!array_key_exists($sPhase, $this->_aConfig["phases"])) {
                die("ERROR: in canDeploy this phase does not exist: $sPhase.");
            }
            if (!$this->isActivePhase($sPhase)) {
                // die("ERROR: the phase $sPhase is not active in this project.");
                return false;
            }
            $sNext = $this->getNextPhase($sPhase);
            if (!$sNext)
                return false;
    
            // ensure that _aData is filled
            $this->getPhaseInfos($sPhase);
    
            // array key "ok" must be in the ready4deployment and deployed info
            if (
                    array_key_exists($sPhase, $this->_aData["phases"]) && array_key_exists("onhold", $this->_aData["phases"][$sPhase]) && array_key_exists("ready4deployment", $this->_aData["phases"][$sPhase]) && array_key_exists("deployed", $this->_aData["phases"][$sPhase]) && array_key_exists("ok", $this->_aData["phases"][$sPhase]["onhold"]) && array_key_exists("ok", $this->_aData["phases"][$sPhase]["ready4deployment"]) && array_key_exists("ok", $this->_aData["phases"][$sPhase]["deployed"])
            )
                return true;
    
            return false;
        }
    
        // ----------------------------------------------------------------------
        // SETTER
        // ----------------------------------------------------------------------
    
        /**
         * apply a config
         * @param array $aConfig
         * @return boolean
          public function setConfig($aConfig) {
          $this->_aConfig = $aConfig;
          $this->_verifyConfig();
          return true;
          }
         */
    
        /**
         * apply a config
         * @param array $aConfig
         * @return boolean
         */
        public function setProjectById($sId) {
            $this->_aPrjConfig = array();
            require(__dir__ . '/' . $this->_sCfgfile);
            if (!array_key_exists("$sId", $aProjects)) {
                die("ERROR: a project with ID $sId does not exist.");
            }
            $this->_aPrjConfig = $aProjects[$sId];
            $this->_aConfig["id"] = $sId;
            $this->_verifyConfig();
            return true;
        }
    
        // ----------------------------------------------------------------------
        // ACTIONS
        // ----------------------------------------------------------------------
    /**
     * get html code of a div around a message
     * @param string $sWarnlevel one of error|success|info|warning to get a colored box
     * @param string $sMessage   message txt
     * @return string
     */
    public function getBox($sWarnlevel, $sMessage){
        $aCfg=array(
            "error"=>array("class"=>"alert alert-error", "prefix"=>"ERROR :-("),
            "success"=>array("class"=>"alert alert-success", "prefix"=>"SUCCESS :-)"),
            "info"=>array("class"=>"alert alert-info", "prefix"=>"INFO"),
            "warning"=>array("class"=>"alert alert-block", "prefix"=>"WARNING"),
        );
        $sClass="";
        $sPrefix="";
        if (array_key_exists($sWarnlevel, $aCfg)){
            $sClass=$aCfg[$sWarnlevel]["class"];
            $sPrefix=$aCfg[$sWarnlevel]["prefix"];
            $sMessage='<strong>'.$aCfg[$sWarnlevel]["prefix"].'</strong> ' . $sMessage;
        }
        return '
            <div class="'.$sClass.'">
                '.$sMessage.'
            </div>';
        
    }
    
        /**
         * Build a new package for the deployment. It will be put to the queue
         * of the first active phase (i.e. preview).
         * If there is no deployment time range it will be deployed too.
         * @global type $aParams
         * @return boolean|string
         */
        public function build() {
            global $aParams;
            $sReturn = false;
    
            $this->_iRcAll = 0;
    
            // --------------------------------------------------
            // create workdir
            // --------------------------------------------------
            $aDirs=$this->cleanupBuilds();
            if (count($aDirs))
                $sReturn.='<h2>Cleanup older failed builds</h2><pre>'.print_r($aDirs, true).'</pre>';
            
            $sTempDir = $this->_getTempDir();
            $sFirstLevel = $this->getNextPhase();
            if (!$sFirstLevel) return false;
            
            $sReturn.="<h2>Create a temporary build dir</h2>";
            if (!file_exists($sTempDir)) {
                $sReturn.=$this->_execAndSend("mkdir -p " . $sTempDir);
            }
            $sReturn.=$this->_execAndSend("ls -ld " . $sTempDir);
            if (!file_exists($sTempDir)) {
                return $this->getBox("error", "$sTempDir was not created.". $sReturn);
            }
    
    
            // --------------------------------------------------
            // checkout
            // --------------------------------------------------
            switch ($this->_aPrjConfig["build"]["type"]) {
                case "git":
    
                    $sReturn.="<h2>Checkout a GIT project</h2>";
                    // $sReturn.=$this->_execAndSend("find " . $this->_aConfig["workDir"]);
                    $sReturn.=$this->_execAndSend("cd $sTempDir && git init");
    
                    $sKeyfile = dirname(dirname(__file__)) . "/" . $this->_aPrjConfig["build"]["keyfile"];
                    $sWrapper = dirname(dirname(dirname(dirname(__file__)))) . "/shellscripts/gitsshwrapper.sh";
                    // $sReturn.=$this->_execAndSend("ls -l " . $sWrapper);
                    // $sReturn.=$this->_execAndSend("ls -l ".$sKeyfile);
                    $sReturn.=$this->_execAndSend("cd $sTempDir && export GIT_SSH=$sWrapper ; export PKEY=$sKeyfile; git pull " . $this->_aPrjConfig["build"]["ssh"]);
    
                    // $sVersion=$this->_execAndSend("cd $sTempDir && export GIT_SSH=$sWrapper ; export PKEY=$sKeyfile; git pull " . $this->_aPrjConfig["build"]["ssh"]);
    
                    // control: directory listing after checkout
                    $sReturn.=$this->_execAndSend("ls -lisa $sTempDir");
    
                    // fetch version infos
                    $sVersioninfo = str_replace("\n", "<br>", shell_exec("cd $sTempDir && git log -1"));
                    $sReturn.=$this->getBox("info", $sVersioninfo);
    
                    break;
    
                default:
                    return $this->getBox("error", "Build Type not supported: " . $this->_aPrjConfig["build"]["type"]. $sReturn);
            }
            if (!$this->_iRcAll == 0) {
                return $this->getBox("error", "checkout failed.</h3>One of the commands failed (see above).<br>You can ask the sysadmins and analyze with them the created temp directory &quot;' . $sTempDir . '&quot;.". $sReturn);
            }
            $sReturn.=$this->getBox("success", "Checkout OK!");
    
            // --------------------------------------------------
            // execute hook
            // --------------------------------------------------
            $sHookfile = $this->_aConfig['hooks']['build-aftercheckout'];
            $sReturn.='<h2>Execute Hook ' . $sHookfile . '</h2>';
            if (file_exists($sTempDir . '/' . $sHookfile)) {
                $sReturn.=$this->_execAndSend('cd ' . $sTempDir . ' && chmod 755 hooks/on*');
                $sReturn.=$this->_execAndSend('cd ' . $sTempDir . ' && "' . $sHookfile . '"');
                if (!$this->_iRcAll == 0) {
                    return $this->getBox("error", "executing hook failed. One of the commands failed.<br>You can ask the sysadmins and analyze with them the created temp directory &quot;' . $sTempDir . '&quot;.". $sReturn);
                }
            } else {
                $sReturn.='SKIP. Hook was not found.<br>';
            }
            
    
            // --------------------------------------------------
            // TODO: cleanup .git, .svn, ...?
            // wenn es kein .git gibt, bricht er ab...
            // --------------------------------------------------
            $sReturn.="<h2>cleanup project</h2>";
            $sReturn.=$this->_execAndSend("cd $sTempDir && rm -rf .git");
            $sReturn.=$this->_execAndSend("cd $sTempDir && rm -rf .svn");
    
    
            // public_html must exist
            $sWebroot = $sTempDir . '/public_html';
            if (!file_exists($sWebroot)) {
                return $this->getBox("error", "a subdir &quot;public_html&quot; does not exist.". $sReturn);
            }
            
            if (!$this->_iRcAll == 0) {
                return $this->getBox("error", "build failed - working directory has errors and is not ready to create package.<br>You can ask the sysadmins and analyze with them the created temp directory &quot;$sTempDir&quot;". $sReturn);
            }
            $sReturn.=$this->getBox("success", "preparations ok - directory is ready for packaging now.");
            
            // --------------------------------------------------
            // create package
            // --------------------------------------------------
            $sReturn.='<h2>Create package</h2>';
    
            // generate info file
            $sTs = date("Y-m-d H:i:s");
            $sTs2 = date("Ymd_His");
            $sInfoFileWebroot = $sWebroot . '/' . basename($this->_getInfofile($sFirstLevel, "deployed"));
            $sInfoFileArchiv = $this->_getArchiveDir($sTs2) . '/' . basename($this->_getInfofile($sFirstLevel, "deployed"));
            $sPackageFileArchiv = $this->_getArchiveDir($sTs2) . '/' . basename($this->_getPackagefile($sFirstLevel, "deployed"));
    
            $sInfos = '{
                "date": "' . $sTs . '",
                "timestamp": "' . $sTs2 . '",
                "revision": "' . $sVersioninfo . '",
                "user": "' . $aParams["inputUser"] . '",
                "remark": "' . $aParams["inputComment"] . '"
            }';
    
            
            
            $sReturn.="writing info file into webroot...<br>";
            file_put_contents($sInfoFileWebroot, $sInfos);
            $sReturn.=$this->_execAndSend("ls -l $sInfoFileWebroot");
    
            if (!file_exists(dirname($sPackageFileArchiv))) {
                $sReturn.="* create " . dirname($sPackageFileArchiv) . "<br>";
                mkdir(dirname($sPackageFileArchiv), 0775, true);
            }
            $sReturn.=$this->_execAndSend("ls -ld " . dirname($sPackageFileArchiv));
            if (!file_exists(dirname($sPackageFileArchiv))) {
                return $this->getBox("error", "directory was not created: " . dirname($sPackageFileArchiv) . $sReturn);
                return $sReturn;
            }
    
            $sReturn.="create archive $sPackageFileArchiv<br>";
            $sReturn.=$this->_execAndSend("cd $sTempDir && tar -czf $sPackageFileArchiv .");
            $sReturn.="writing info file into archive...<br>";
            file_put_contents($sInfoFileArchiv, $sInfos);
            $sReturn.="<br>Created Archive files:<br>";
            $sReturn.=$this->_execAndSend("ls -l $sPackageFileArchiv $sInfoFileArchiv");
    
            if (!$this->_iRcAll == 0) {
                return $this->getBox("error", 'creation failed One of the commands failed (see below).<br>You can ask the sysadmins and analyze with them the created temp directory &quot;' . $sTempDir . '&quot;.' . $sReturn);
            }
            
            
            $sReturn.="<h2>cleanup $sTempDir</h2>";
            $sReturn.="<h3>cleanup $sTempDir</h3>";
            $sReturn.=$this->_execAndSend("rm -rf $sTempDir");
            $sReturn.="<h3>cleanup Archive</h3>removing the oldest unused packages ...";
            $sReturn.='<pre>'. print_r($this->cleanupArchive(), true).'</pre>';
            
            $sReturn.=$this->getBox("success", "Build finished successfully.");
    
            // TODO: force synch archive to puppet master
    
            $sReturn.=$this->queue($sFirstLevel, $sTs2);
    
    
            
            return $sReturn;
        }
    
        /**
         * put a packaged version into the queue of a specified phase
         * @param string $sPhase    name of the phase
         * @param string $sVersion  version 
         * @return string
         */
        public function queue($sPhase, $sVersion) {
    
            if (!$this->isActivePhase($sPhase)) return false;
            
            $sReturn="<h2>Queue to $sPhase</h2>";
            $sPlace="onhold";
    
            $sLinkTarget = $this->_getArchiveDir($sVersion);
            $sLinkName = $this->_getFileBase($sPhase, $sPlace);
    
            // --------------------------------------------------
            // Checks
            // --------------------------------------------------
            if (!$sLinkName) {
                die("ERROR: Queuing failed - sLinkName is empty.");
            }
            if (!$sLinkTarget) {
                die("ERROR: Queuing  failed - sLinkTarget is empty.");
            }
            if (!file_exists($sLinkTarget)) {
                die("ERROR: Queuing failed - version $sVersion is invalid. The directory $sLinkTarget does not exist.");
            }
    
            // --------------------------------------------------
            // create the new link
            // --------------------------------------------------
            $this->_iRcAll = 0;
            if (file_exists($sLinkName)) {
                $sReturn.="removing existing version<br>";
                $sReturn.=$this->_execAndSend("rm -f $sLinkName");
            }
            $sReturn.="linking to new version <br>";
            $sReturn.=$this->_execAndSend("ln -s $sLinkTarget $sLinkName");
            $sReturn.=$this->_execAndSend("ls -l $sLinkName | fgrep $sLinkTarget");
    
    
            if (!$this->_iRcAll == 0) {
                return $this->getBox("error", 'Queuing failed One of the commands failed.' . $sReturn);
            }
            $sReturn.=$this->getBox("success", "the version $sVersion was set to place $sPlace");
            $sReturn.=$this->deploy($sPhase);
            
            return $sReturn;
        }
        
        /**
         * deploy a queued package - this moves the queue into the repo directory
         * and will be installed on server within 30 min
         * @param type $sTargetphase
         * @return boolean|string
         */
        public function deploy($sTargetphase) {
            if (!$this->isActivePhase($sTargetphase)) return false;
            
            $sReturn="<h2>Deploy to $sTargetphase</h2>";
            
            $sQueueLink = $this->_getFileBase($sTargetphase, "onhold");
            $sRepoLink = $this->_getFileBase($sTargetphase, "ready4deployment");
            
            if (array_key_exists("deploytimes", $this->_aConfig["phases"][$sTargetphase])){
                // check if the a deploy time is reached
                $sNow=date("D H:i:s");
                $sReturn.="check if one of the deployment times is reached and matches $sNow<br>";
                $bCanDeploy=false;
                foreach ($this->_aConfig["phases"][$sTargetphase]["deploytimes"] as $sRegex){
                    $sReturn.="... $sRegex<br>";
                    if (preg_match($sRegex, $sNow)){
                        $bCanDeploy=true;
                    }
                }
                if (!$bCanDeploy){
                    $sReturn.="SKIP: deployment time was not reached.<br>";
                    return $sReturn;
                }
                $sReturn.="OK, deployment time was reached.<br>";
                // if ()
            }
            if (!file_exists($sQueueLink)){
                $sReturn.=$this->getBox("warning", "SKIP: nothing to do - the current queue is empty ($sQueueLink does not exist).");
                return $sReturn;
            }
            if (!file_exists($sQueueLink)){
                $sReturn.="SKIP: no current queue - $sQueueLink does not exist.";
                return $sReturn;
            }
            
    
            // --------------------------------------------------
            // move the queue link to the repo name
            // --------------------------------------------------
            $this->_iRcAll = 0;
            if (file_exists($sRepoLink)) {
                $sReturn.="removing existing version<br>";
                $sReturn.=$this->_execAndSend("rm -f $sRepoLink");
            }
            $sReturn.="moving queue to repo<br>";
            $sReturn.=$this->_execAndSend("mv $sQueueLink $sRepoLink");
    
    
            if (!$this->_iRcAll == 0) {
                return $this->getBox("error", "Deployment failed - One of the commands failed." . $sReturn);
            }
            
            // TODO: force synch to puppet master
            
            $sReturn.=$this->getBox("success", "SUCCESS: deployment was done and will be installed soon.");
            return $sReturn;
        }
    
    }
    
    ?>