diff --git a/config/inc_projects_config.php b/config/inc_projects_config.php index 53c00fcc609fdb56ff667403baa17812da3beaa0..9debed14af8a92e74d96f0e7e7c2e52380e6648b 100644 --- a/config/inc_projects_config.php +++ b/config/inc_projects_config.php @@ -11,7 +11,7 @@ $aConfig = array( 'builtsToKeep' => 3, // for cleanup: keep n failed builds 'hooks' => array( 'build-postclone' => 'hooks/onbuild-postclone', - 'build-precompress' => 'hooks/onbuild', + 'build-precompress' => 'hooks/onbuild', // TODO: remove this ), 'lang' => 'de', // for available languages see ./config/lang/*.json // rsync of archives @@ -86,9 +86,19 @@ switch (php_uname("n")) { break; } +// ---------------------------------------------------------------------- +// TODO: include custom settings that were saved in the GUI +// ---------------------------------------------------------------------- + + +// ---------------------------------------------------------------------- +// generate some vars +// ---------------------------------------------------------------------- + $aConfig = array_merge($aConfig, array( 'appRootDir' => dirname(dirname(__FILE__)), 'configDir' => dirname(__FILE__), + 'dataDir' => $aConfig['workDir'] . '/data', // to write data: ssh keys, projects, database 'buildDir' => $aConfig['workDir'] . '/build', 'buildDefaultsDir' => $aConfig['workDir'] . '/defaults', 'packageDir' => $aConfig['workDir'] . '/packages', diff --git a/config/lang/de.json b/config/lang/de.json index fc2547ecdbbd2e5d0ab85e21904e6a2217545c0f..a31ebe36c9efd64aa32525b6579eb07bea9a6c93 100644 --- a/config/lang/de.json +++ b/config/lang/de.json @@ -70,7 +70,7 @@ "class-project-error-build-dir-was-not-created": "Das Verzeichnis %s wurde nicht angelegt.", "class-project-error-build-type-not-supported": "Repository Typ %s wird nicht unterstützt.", "class-project-error-build-docroot-not-found": "Es gibt kein Unterverzeichnis "public_html" oder "public" im Arbeitsverzeichnis.", - "class-project-error-command-failed": "Eines der Kommandos ist fehlgeschlagen (s.o.).<br>Frage ggf. den Administrator. Das Arbeitsverzeichnis wird für eine Analyse nicht gelöscht.", + "class-project-error-command-failed": "Eines der Kommandos ist fehlgeschlagen (s. Fehlermeldung in der Ausgabe).<br>Frage ggf. den Administrator. Das Arbeitsverzeichnis wird für eine Analyse nicht gelöscht.", "class-project-error-getPhaseInfos-package-not-found": "Die Paket-Datei (.tgz) wurde nicht gefunden: %s", "class-project-error-getPhaseInfos-requires-phase": "Die Methode getPhaseInfos erfordert die Angabe eine Phase.", "class-project-error-datafile-does-not-exist": "Die Paket-Datei "%s" existiert nicht.", @@ -139,6 +139,8 @@ "class-project-warning-cannot-delete-build-dir": "WARNUNG: Das Build-Verzeichnis %s konnte nicht gelöscht werden.", "class-project-warning-phase-not-active": "Die Phase %s ist nicht aktiv.", + "class-user-error-deny-no-role": "FEHLER: Sie haben nicht genügend Berechtigungen.", + "page-accept-error-cannot-accept-phase": "Die Phase [%s] kann nicht akzeptiert werden.", "page-accept-info": "Die Software wurde erfolgreich in der Phase <span class=\"%s\">%s</span> getestet und soll auf die nächstePhase <span class=\"%s\">%s</span> ausgerollt werden?", "page-accept-warning-version-exists-in-next-queue": "In der Queue von Phase [%s] ist die Version von [%s] bereits vorhanden!", @@ -195,14 +197,18 @@ "accept-hint": "Accept Phase [%s] und in die Queue von Phase [%s] stellen.", "all": "alle", "archive": "Archiv", + "branch": "Branch", + "branch-select": "Vorhandene Branches", "build": "Build", "build-hint": "neues Paket erstellen und in Phase [%s] stellen.", "build-from": "Build vom", "build-type": "Build Typ", "commitmessage": "Commit-Message", + "change": "Wechseln", "contact": "Kontakt", "creating-directory": "Lege das Verzeichnis %s an.", "creating-file": "Lege Datei %s an.", + "defaultbranch": "[default: Master oder Trunk]", "deploy": "Deploy", "deploy-hint": "Deploy der Queue von Phase [%s]", "deploytimes": "Deployment Zeitpunkte", @@ -228,7 +234,7 @@ "new-project-hint": "ein neues Projekt anlegen", "no": "nein", "none": "kein(e)", - "packages": "Pakete", + "packages": "Pakete", "phase": "Phase", "phase-details": "Details", "phase-details-hint": "Details zur Phase [%s]", diff --git a/config/lang/en.json b/config/lang/en.json index f20a90f75ec7bbc296524b568208e445ebd8fefc..2178d4d254dcd6f90071305cf12c3cca0250ccb5 100644 --- a/config/lang/en.json +++ b/config/lang/en.json @@ -69,7 +69,7 @@ "class-project-error-build-dir-was-not-created": "The directory %s was not created.", "class-project-error-build-type-not-supported": "Repository type %s is not supported.", "class-project-error-build-docroot-not-found": "There is no subdirectory "public_html" or "public" in the working directory.", - "class-project-error-command-failed": "The execution of a command failed (see above).<br>Ask your admin. The working directory was NOT deleted that you can analyze the problem.", + "class-project-error-command-failed": "The execution of a command failed (see error message in the output below).<br>Ask your admin. The working directory was NOT deleted that you can analyze the problem.", "class-project-error-getPhaseInfos-package-not-found": "The package file (.tgz) was not found: %s", "class-project-error-getPhaseInfos-requires-phase": "The method getPhaseInfos requires the name of a pfase.", "class-project-error-datafile-does-not-exist": "The package file "%s" does not exist.", @@ -140,6 +140,8 @@ "class-project-warning-cannot-delete-build-dir": "WARNING: The Build directory %s could not be deleted.", "class-project-warning-phase-not-active": "The phase %s is not aktive.", + "class-user-error-deny-no-role": "ERRROR: Your User has not enough permissions.", + "page-accept-error-cannot-accept-phase": "The phase [%s] cannot be accepted.", "page-accept-info": "The software was tested successfully in phase <span class=\"%s\">%s</span> and shall be rolled out in the next phase <span class=\"%s\">%s</span>?", "page-accept-warning-version-exists-in-next-queue": "In the queue of phase [%s] the version [%s] already exists!", @@ -197,14 +199,18 @@ "accept-hint": "Accept phase [%s] and put package to the queue of phase [%s].", "all": "all", "archive": "Archive", + "branch": "Branch", + "branch-select": "Existing branches", "build": "Build", "build-hint": "Create new package and rollout to first active phase [%s].", "build-from": "Build date", "build-type": "Build type", + "change": "Change", "commitmessage": "Commit message", "contact": "contact", "creating-directory": "Creating directory %s", "creating-file": "Creating file %s", + "defaultbranch": "[default: master or trunk]", "deploy": "Deploy", "deploy-hint": "Deploy queue of phase [%s]", "deploytimes": "Deploy time window", diff --git a/config/projects/.htkeep b/config/projects/.htkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/config/sshkeys/.htkeep b/config/sshkeys/.htkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/database/.htkeep b/database/.htkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/hooks/onbuild b/hooks/onbuild index 9475aa37f562274bfbc43997ba6c504963476ec9..868e19370d85bb75ea7bf9eff73e1afe710e3c30 100644 --- a/hooks/onbuild +++ b/hooks/onbuild @@ -1,16 +1,15 @@ #!/bin/bash # ====================================================================== # -# ONBUILD fuer CI Deployment GUI +# ONDEPLOY fuer CI Deployment GUI # # Aufgaben: -# - gibt dem Wrapper fuer SSH Connections zu Git X-Rechte -# - minimiert Rechte auf SSH Keyfiles +# - Projekt-Configs anpassen # -# 2013-11-12 axel.hahn@iml.unibe.ch +# 2014-05-07 axel.hahn@iml.unibe.ch # ====================================================================== -echo ONBUILD fuer CI Deployment GUI +echo ONDEPLOY fuer CI Deployment GUI echo cd `dirname $0` cd .. diff --git a/public_html/deployment/classes/actionlog.class.php b/public_html/deployment/classes/actionlog.class.php index 9c325bca8d77f88719268db7f7a68e0323abd86a..d592286539b17c06ca9b64860e449a2751507c36 100644 --- a/public_html/deployment/classes/actionlog.class.php +++ b/public_html/deployment/classes/actionlog.class.php @@ -34,7 +34,7 @@ class Actionlog { if (!is_array($aConfig) || !array_key_exists("appRootDir", $aConfig)) { die(__CLASS__ . "::".__FUNCTION__." ERROR: configuration with \$aConfig was not loaded."); } - $this->_dbfile = $aConfig['appRootDir'] . '/database/logs.db'; + $this->_dbfile = $aConfig['dataDir'] . '/database/logs.db'; if (!file_exists($this->_dbfile)) { $this->_createDb(); if (!file_exists($this->_dbfile)) { diff --git a/public_html/deployment/classes/base.class.php b/public_html/deployment/classes/base.class.php new file mode 100644 index 0000000000000000000000000000000000000000..8ef4c3559a3e833105a9312dbe5aecf0c54ded5a --- /dev/null +++ b/public_html/deployment/classes/base.class.php @@ -0,0 +1,26 @@ +<?php + +require_once 'user.class.php'; + +/** + * base class for available variables and objects in other objects + * + * @author hahn + */ +class base { + + var $oUser=false; + + /** + * init user with optional given user + * @param type $sUser + */ + public function __construct(){ + $this->oUser=new user(); + + if (method_exists($this, "_construct2")){ + $this->_construct2(); + } + } + +} diff --git a/public_html/deployment/classes/formgen.class.php b/public_html/deployment/classes/formgen.class.php index 271d3a64319705e7457bb920c8733bb68ea6a260..8d33c8ec68ae3e7e0c7ebaaa012e329622f85a09 100644 --- a/public_html/deployment/classes/formgen.class.php +++ b/public_html/deployment/classes/formgen.class.php @@ -52,7 +52,7 @@ class formgen { } $sReturn.='<form '; if (array_key_exists("meta", $this->aForm[$sFormId])) { - foreach (array("method", "action", "target", "accept-charset", "class") as $sAttr) { + foreach (array("method", "action", "target", "accept-charset", "class", "id", "name") as $sAttr) { if (array_key_exists($sAttr, $this->aForm[$sFormId]["meta"])) { $sReturn.=$sAttr . '="' . $this->aForm[$sFormId]["meta"][$sAttr] . '" '; } diff --git a/public_html/deployment/classes/project.class.php b/public_html/deployment/classes/project.class.php index 0dd0ac95654964c9a2c9bfd8bfcde3ffe7b0944b..c6091e30c124dc5e47ead78800bfa5919f283f28 100644 --- a/public_html/deployment/classes/project.class.php +++ b/public_html/deployment/classes/project.class.php @@ -71,7 +71,8 @@ class project { * @var type */ private $_oVcs = false; - + private $_sBranchname = false; + // ---------------------------------------------------------------------- // constructor // ---------------------------------------------------------------------- @@ -133,8 +134,24 @@ class project { die(sprintf(t("class-project-error-missing-prjkey"), $sKey, print_r($this->_aPrjConfig, true))); } } - + // TODO: verify ausbauen + /* + if (!$this->_aConfig["dataDir"]) { + die(t("class-project-error-datadir-empty")); + } + if (!file_exists($this->_aConfig["dataDir"])) { + die(sprintf(t("class-project-error-data-does-not-exist"), $this->_aConfig['dataDir'])); + } + + foreach (array("database", "projects", "sshkeys") as $sKey) { + $sTestDir=$this->_aConfig["dataDir"]."/$sKey"; + if (!file_exists($sTestDir)) { + mkdir($sTestDir); + // die(sprintf(t("class-project-error-missing-prjkey"), $sKey, print_r($this->_aPrjConfig, true))); + } + } + */ return true; } @@ -151,52 +168,52 @@ class project { ob_implicit_flush(true); } // ob_end_flush(); - + $sReturn.="[" . date("H:i:s d.m.Y") . "] "; $sReturn.=$bUseHtml ? "<strong>$sCommand</strong>" : "$sCommand"; $sReturn.=$bUseHtml ? "<br>" : "\n"; - $sOutput=false; + $sOutput = false; exec($sCommand, $aOutput, $iRc); - $sReturn.=(count($aOutput))?implode("\n", $aOutput)."\n":""; + $sReturn.=(count($aOutput)) ? implode("\n", $aOutput) . "\n" : ""; /* - $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 - ); - if ($bFlush) { - flush(); - } - $process = proc_open($sCommand, $descriptorspec, $pipes, realpath('./'), array()); - - - $sErrors = false; - if (is_resource($process)) { - while ($s = fgets($pipes[1])) { - $sReturn.=$s; - if ($bFlush) { - flush(); - } - } - while ($s = fgets($pipes[2])) { - $sErrors.=$s; - if ($bFlush) { - flush(); - } - } - } - if ($sErrors) { - $sReturn.="STDERR:\n" . $sErrors; - } - $oStatus = proc_get_status($process); - $iRc = $oStatus['exitcode']; + $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 + ); + if ($bFlush) { + flush(); + } + $process = proc_open($sCommand, $descriptorspec, $pipes, realpath('./'), array()); + + + $sErrors = false; + if (is_resource($process)) { + while ($s = fgets($pipes[1])) { + $sReturn.=$s; + if ($bFlush) { + flush(); + } + } + while ($s = fgets($pipes[2])) { + $sErrors.=$s; + if ($bFlush) { + flush(); + } + } + } + if ($sErrors) { + $sReturn.="STDERR:\n" . $sErrors; + } + $oStatus = proc_get_status($process); + $iRc = $oStatus['exitcode']; */ - + if ($iRc != 0) { - $this->_logaction("command failed: $sCommand - rc=".$iRc, __FUNCTION__, "error"); + $this->_logaction("command failed: $sCommand - rc=" . $iRc, __FUNCTION__, "error"); } - + $this->_iRcAll += $iRc; $sReturn.="[" . date("H:i:s d.m.Y") . "] " . t("exitcode") . " " . $iRc; if ($bUseHtml) { @@ -234,7 +251,7 @@ class project { if (!$sId) { die(t("class-project-error-_getConfigFile-requires-id")); } - return $this->_aConfig["configDir"] . '/projects/' . $sId . ".json"; + return $this->_aConfig["dataDir"] . '/projects/' . $sId . ".json"; } /** @@ -495,7 +512,7 @@ class project { // keep a few $iKeep = $bDeleteAll ? 0 : $this->_aConfig["versionsToKeep"]; - while (count($aUnused) > $iKeep) { + while (count($aUnused) >= $iKeep) { $sVersion = array_shift($aUnused); $sDir2 = $sDir . '/' . $sVersion; if ($this->_rmdir($sDir2)) { @@ -679,7 +696,7 @@ class project { */ public function getProjects() { $aReturn = array(); - foreach (glob($this->_aConfig["configDir"] . '/projects/' . "/*.json") as $filename) { + foreach (glob(dirname($this->_getConfigFile("dummy")). "/*.json") as $filename) { $aReturn[] = str_replace(".json", "", basename($filename)); } sort($aReturn); @@ -757,10 +774,10 @@ class project { if (!$sPhase) { // for better performance: skip check on overview page /* - $aRepodata = $this->getRepoRevision(); - if (!array_key_exists("revision", $aRepodata)) { - return false; - } + $aRepodata = $this->getRepoRevision(); + if (!array_key_exists("revision", $aRepodata)) { + return false; + } */ $sNext = $this->getNextPhase($sPhase); return $sNext > ''; @@ -775,22 +792,100 @@ class project { return false; } $sNext = $this->getNextPhase($sPhase); - if (!$sNext){ + if (!$sNext) { return false; } - + // ensure that _aData is filled $this->getPhaseInfos($sPhase); // array key "ok" must be in the ready2install and deployed info if ( array_key_exists($sPhase, $this->_aData["phases"]) && array_key_exists("onhold", $this->_aData["phases"][$sPhase]) && array_key_exists("ready2install", $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]["ready2install"]) && array_key_exists("ok", $this->_aData["phases"][$sPhase]["deployed"]) - ){ + ) { return true; } return false; } + /** + * get html form with selectr for remote branches + * @param string $sActiveBranchname force active branch name + * @return string + */ + public function getRemoteBranches($sActiveBranchname = false) { + $aReturn = array(); + $aRadios = array(); + $bFoundActive = false; + $i = 0; + if (!$this->_oVcs) { + $this->_initVcs(); + } + + if (!$sActiveBranchname) { + $sActiveBranchname = $this->_sBranchname; + } + if ($this->_oVcs) { + if (!method_exists($this->_oVcs, "getRemoteBranches")) { + // the version control class does not have this method + return ''; + } + foreach ($this->_oVcs->getRemoteBranches() as $sBranch) { + $aRadios[$sBranch] = array( + 'label' => $sBranch, + ); + // if no param was given the first branch will be marked + if (!$sActiveBranchname) { + $sActiveBranchname = $sBranch; + } + if ($sBranch == $sActiveBranchname) { + $bFoundActive = true; + $aRadios[$sBranch]['checked'] = 'checked'; + } else { + $aRadios[$sBranch]['onclick'] = 'document.getElementById(\'submitBranch\').click()'; + } + }; + } + // no branches were found + if (count($aRadios) == 0) { + return ''; + } + + $aForms = array( + 'frmSelectBranch' => array( + 'meta' => array( + 'method' => 'POST', + 'action' => '?', + 'id' => 'frmSelectBranch', + ), + 'validate' => array(), + 'form' => array( + 'branchname' => array( + 'type' => 'radio', + 'name' => 'branchname', + 'label' => '<strong>' . t('branch-select') . '</strong>', + 'validate' => 'isastring', + 'options' => $aRadios, + ), + ), + ), + ); + + // submit to switch branches - only if a selection is available + if (count($aRadios) > 1 || !$bFoundActive) { + $aForms['frmSelectBranch']['form']['submitBranch'] = array( + 'type' => 'submit', + 'name' => 'btnsave', + 'label' => t("change"), + 'value' => '<i class="icon-ok"></i> ' . t("change"), + ); + } + + $oFrm = new formgen($aForms); + return $oFrm->renderHtml('frmSelectBranch'); + // return $oFrm->renderHtmlElement('dummy',$aFormData); + } + /** * get current revision and log message from remote repo * @return array @@ -802,12 +897,12 @@ class project { ) { return $this->_aData["phases"]["source"]; } - - if (!$this->_aPrjConfig["build"]["type"]){ + + if (!$this->_aPrjConfig["build"]["type"]) { $this->_aData["phases"]["source"] = array("error" => t("class-project-error-repo-type-not-set"),); } else { $this->_initVcs(); - if ($this->_oVcs){ + if ($this->_oVcs) { $this->_aData["phases"]["source"] = $this->_oVcs->getRepoRevision(); } else { $this->_aData["phases"]["source"] = array( @@ -817,23 +912,31 @@ class project { } return $this->_aData["phases"]["source"]; } - + /** * init version control system (git, ...) * @return vcs-object */ - private function _initVcs(){ + private function _initVcs() { if (!$this->_oVcs) { - if (!$this->_aPrjConfig["build"]["type"]){ + if (!$this->_aPrjConfig["build"]["type"]) { $this->_aData["phases"]["source"] = array("error" => t("class-project-error-repo-type-not-set"),); } else { - if (!@include_once("vcs.".$this->_aPrjConfig["build"]["type"].".class.php")){ + if (!@include_once("vcs." . $this->_aPrjConfig["build"]["type"] . ".class.php")) { $this->_aData["phases"]["source"] = array( "error" => sprintf(t("class-project-error-repo-type-not-supported"), $this->_aPrjConfig["build"]["type"]), ); } else { - - $this->_oVcs=new vcs($this->_aPrjConfig["build"]); + $aConfig = $this->_aPrjConfig["build"]; + // for vcs classes + $aConfig["appRootDir"] = $this->_aConfig["appRootDir"]; + $aConfig["dataDir"] = $this->_aConfig["dataDir"]; + $this->_oVcs = new vcs($aConfig); + if ($this->_sBranchname) { + if (method_exists($this->_oVcs, "setCurrentBranch")) { + $this->_oVcs->setCurrentBranch($this->_sBranchname); + } + } } } } @@ -864,9 +967,7 @@ class project { $this->_aConfig["id"] = $sId; $this->_aPrjConfig = json_decode(file_get_contents($this->_getConfigFile($sId)), true); - - // for vcs classes - $this->_aPrjConfig["build"]["appRootDir"]=$this->_aConfig["appRootDir"]; + // $aData=json_decode(file_get_contents($this->_getConfigFile($sId)), true); // echo "<pre>" . print_r($aData, true) . "</pre>"; @@ -874,6 +975,21 @@ class project { return true; } + /** + * set a branchname + * @param string $sBranchname name of the branch, i.e. "origin/master" + * @return bool + */ + public function setBranchname($sBranchname) { + $this->_sBranchname = $sBranchname; + if ($this->_oVcs) { + if (method_exists($this->_oVcs, "setCurrentBranch")) { + $this->_oVcs->setCurrentBranch($sBranchname); + } + } + return $this->_sBranchname; + } + // ---------------------------------------------------------------------- // ACTIONS // ---------------------------------------------------------------------- @@ -999,8 +1115,8 @@ class project { $sError = sprintf(t('class-project-error-build-type-not-supported'), $this->_aPrjConfig["build"]["type"]); $this->_logaction($sError, __FUNCTION__, "error"); return $this->getBox("error", $sError . $sReturn); - } - + } + // -------------------------------------------------- // create workdir // -------------------------------------------------- @@ -1032,17 +1148,15 @@ class project { // -------------------------------------------------- $sReturn.='<h3>' . t('class-project-build-label-get-sources-from-version-control') . '</h3>'; - $this->_oVcs->getSources($sTempDir); + $sReturn.='<pre>' . $this->_oVcs->getSources($sTempDir) . '</pre>'; - $aVersion=$this->_oVcs->getRevision($sTempDir); - $sRevision=$aVersion["revision"]; - $sCommitMsg=$aVersion["message"]; + $aVersion = $this->_oVcs->getRevision($sTempDir); + $sRevision = $aVersion["revision"]; + $sCommitMsg = $aVersion["message"]; $sCommitMsg = str_replace("\n", "<br>", $sCommitMsg); $sCommitMsg = str_replace('"', """, $sCommitMsg); - // $sCommitMsg = str_replace('<', "<", $sCommitMsg); - // $sCommitMsg = str_replace('>', ">", $sCommitMsg); $sReturn.=$this->getBox("info", $sCommitMsg); - + $sReturn.=$this->_execAndSend("ls -lisa $sTempDir"); if (!$this->_iRcAll == 0) { @@ -1062,7 +1176,7 @@ class project { $sHookfile = $this->_aConfig['hooks']['build-postclone']; $sReturn.='<h3>' . t('class-project-build-label-execute-hook-postclone') . ' (' . $sHookfile . ')</h3>'; if (file_exists($sTempDir . '/' . $sHookfile)) { - $sReturn.=$this->_execAndSend('chmod 755 '.$sTempDir.'/hooks/on*'); + $sReturn.=$this->_execAndSend('chmod 755 ' . $sTempDir . '/hooks/on*'); // $this->_iRcAll = 0; $sReturn.=$this->_execAndSend('bash --login -c \'' . $sTempDir . '/' . $sHookfile . '\''); if (!$this->_iRcAll == 0) { @@ -1098,7 +1212,7 @@ class project { $sHookfile = $this->_aConfig['hooks']['build-precompress']; $sReturn.='<h3>' . t('class-project-build-label-execute-hook-precompress') . ' (' . $sHookfile . ')</h3>'; if (file_exists($sTempDir . '/' . $sHookfile)) { - $sReturn.=$this->_execAndSend('chmod 755 '.$sTempDir.'/hooks/on*'); + $sReturn.=$this->_execAndSend('chmod 755 ' . $sTempDir . '/hooks/on*'); // $this->_iRcAll = 0; $sReturn.=$this->_execAndSend('bash --login -c \'' . $sTempDir . '/' . $sHookfile . '\''); if (!$this->_iRcAll == 0) { @@ -1118,10 +1232,10 @@ class project { // cleanup .git, .svn, ... // -------------------------------------------------- $sReturn.='<h3>' . t('class-project-build-label-cleanup-project') . '</h3>'; - if ($this->_oVcs){ + if ($this->_oVcs) { $this->_oVcs->cleanupWorkdir($sTempDir); } - + // $sReturn.=$this->_execAndSend("cd $sTempDir && rm -rf .git"); // $sReturn.=$this->_execAndSend("cd $sTempDir && rm -rf .svn"); // $sReturn.=$this->_execAndSend("find $sTempDir -type d -name '.svn' -exec rm -rf {} \;"); @@ -1161,24 +1275,38 @@ class project { // generate info file $sTs = date("Y-m-d H:i:s"); $sTs2 = date("Ymd_His"); + $sBranch = ($this->_sBranchname ? $this->_sBranchname : t("defaultbranch")); $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 . '", - "version": "' . $sTs2 . '", - "revision": "' . $sRevision . '", - "message": "' . $sCommitMsg . '" - }'; + $aInfos = array( + 'date' => $sTs, + 'version' => $sTs2, + 'branch' => $sBranch, + 'revision' => $sRevision, + 'message' => $sCommitMsg, + ); + /* + $sInfos = '{ + "date": "' . $sTs . '", + "version": "' . $sTs2 . '", + "branch": "' . $sBranch . '", + "revision": "' . $sRevision . '", + "message": "' . $sCommitMsg . '" + }'; + * + */ /* "user": "' . $aParams["inputUser"] . '", "remark": "' . $aParams["inputComment"] . '" */ $sReturn.=t("class-project-info-build-write-meta-to-webroot") . "<br>"; - file_put_contents($sInfoFileWebroot, $sInfos); + // file_put_contents($sInfoFileWebroot, $sInfos); + file_put_contents($sInfoFileWebroot, json_encode($aInfos)); $sReturn.=$this->_execAndSend("ls -l $sInfoFileWebroot"); + $sReturn.=$this->_execAndSend("cat $sInfoFileWebroot"); if (!file_exists(dirname($sPackageFileArchiv))) { $sReturn.=sprintf(t("creating-directory"), dirname($sPackageFileArchiv)) . "<br>"; @@ -1201,7 +1329,8 @@ class project { // write info file (.json) $sReturn.=sprintf(t("creating-file"), $sInfoFileArchiv) . "<br>"; - file_put_contents($sInfoFileArchiv, $sInfos); + // file_put_contents($sInfoFileArchiv, $sInfos); + file_put_contents($sInfoFileArchiv, json_encode($aInfos)); // copy template files if (file_exists($sTempDir . '/hooks/templates/')) { @@ -1436,7 +1565,7 @@ class project { // $sReturn.=$this->_execAndSend("ln -s $sLinkTarget $sLinkName"); if (array_key_exists('mirrorPackages', $this->_aConfig) && count($this->_aConfig['mirrorPackages'])) { foreach ($this->_aConfig['mirrorPackages'] as $sLabel => $aTarget) { - $sReturn.='<h3>'.sprintf(t("class-project-info-deploy-synching-package"), $sLabel) . "</h3>"; + $sReturn.='<h3>' . sprintf(t("class-project-info-deploy-synching-package"), $sLabel) . "</h3>"; if (array_key_exists('type', $aTarget)) { $sCmd = false; // $sSource=$this->_aConfig["packageDir"]."/$sPhase/*"; @@ -1473,7 +1602,7 @@ class project { // TODO: run puppet agent on target server(s) - for preview only if (array_key_exists("puppethost", $this->_aPrjConfig["phases"][$sPhase]) && $this->_aPrjConfig["phases"][$sPhase]["puppethost"] ) { - $sReturn.='<h3>'.t("class-project-info-deploy-start-puppet") . '</h3>'; + $sReturn.='<h3>' . t("class-project-info-deploy-start-puppet") . '</h3>'; $sCmd = 'ssh ' . $this->_aConfig["installPackages"]["user"] . '@' . $this->_aPrjConfig["phases"][$sPhase]["puppethost"] . ' ' . $this->_aConfig["installPackages"]["command"]; @@ -1874,7 +2003,9 @@ class project { ' . $this->_renderBar($sPhase, $sPlace) . ' <i class="icon-calendar"></i> ' . t('build-from') . ' ' . date("d.m.Y H:i:s", strtotime($aData["date"])); if ($bLong) { - $sReturn.='<br><i class="icon-tag"></i> ' . t('revision') . ': ' . $aData["revision"] . '<br> + $sReturn.='<br> + <i class="icon-bookmark"></i> ' . t('branch') . ': ' . $aData["branch"] . '<br> + <i class="icon-tag"></i> ' . t('revision') . ': ' . $aData["revision"] . '<br> <i class="icon-comment"></i> ' . t('commitmessage') . ':<br><pre>' . strip_tags($aData["message"], '<br>') . '</pre>'; if ($sPlace == "deployed" && array_key_exists("url", $this->_aPrjConfig["phases"][$sPhase])) { $sReturn.='<i class="icon-globe"></i> ' . t('url') . ': <a href="' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '">' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '</a><br>'; @@ -1996,9 +2127,9 @@ class project { $aRepodata = $this->getRepoRevision(); if (array_key_exists("revision", $aRepodata)) { - $sRevision = $aRepodata["revision"]; - $sReturn.=$this->_getChecksumDiv($sRevision); - $sReturn.= '<i class="icon-tag"></i> ' . t('revision') . ': ' . $sRevision; + $sReturn.=$this->_getChecksumDiv($aRepodata["revision"]); + $sReturn.= '<i class="icon-bookmark"></i> ' . t('branch') . ': ' . (array_key_exists("branch", $aRepodata) ? $aRepodata["branch"] : '-') . '<br>'; + $sReturn.= '<i class="icon-tag"></i> ' . t('revision') . ': ' . $aRepodata["revision"] . '<br>'; $sReturn.="<pre>" . strip_tags($aRepodata["message"], '<br>') . "</pre>"; } else { $sReturn .= $this->getBox("error", sprintf(t('class-project-error-no-repoaccess'), $aRepodata["error"])) @@ -2010,8 +2141,11 @@ class project { default: $sReturn .= $this->getBox("error", sprintf(t('class-project-error-wrong-buildtype'), $this->_aPrjConfig["build"]["type"])); } + if (array_key_exists("url", $this->_aPrjConfig["build"])) { + $sReturn.=t('repository-url') . ': ' . $this->_aPrjConfig["build"]["url"] . '<br>'; + } if (array_key_exists("webaccess", $this->_aPrjConfig["build"])) { - $sReturn.='<br>' . t('repository-access-browser') . ':<br><a href="' . $this->_aPrjConfig["build"]["webaccess"] . '">' . $this->_aPrjConfig["build"]["webaccess"] . '</a><br>'; + $sReturn.=t('repository-access-browser') . ':<br><a href="' . $this->_aPrjConfig["build"]["webaccess"] . '">' . $this->_aPrjConfig["build"]["webaccess"] . '</a><br>'; } return $sReturn; } @@ -2038,6 +2172,7 @@ class project { $sLinktitle = t('infos'); if (array_key_exists("message", $aInfos)) { $sInfos.='<i class="icon-calendar"></i> ' . t('build-from') . ' ' . date("d.m.Y H:i:s", strtotime($aInfos["date"])) . '<br>' + . '<i class="icon-bookmark"></i> ' . t('branch') . ': ' . $aInfos["branch"] . '<br>' . '<i class="icon-tag"></i> ' . t('revision') . ': ' . $aInfos["revision"] . '<br>' . '<i class="icon-comment"></i> ' . t('commitmessage') . ':<br><span class="pre">' . strip_tags($aInfos["message"], '<br>') . '</span>'; if (array_key_exists("more", $aOptions)) { @@ -2134,93 +2269,92 @@ class project { return $sReturn; } - /** * render graphical overview of process (in project overview) * @return string */ - public function renderVisual(){ - $sReturn=''; - $sContinue='»»'; + public function renderVisual() { + $sReturn = ''; + $sContinue = '»»'; - $sRepoBar=''; + $sRepoBar = ''; $aRepodata = $this->getRepoRevision(); if (array_key_exists("revision", $aRepodata)) { - $sRepoBar=$this->_getChecksumDiv($aRepodata["revision"]); + $sRepoBar = $this->_getChecksumDiv($aRepodata["revision"]); } else { - $sRepoBar='<span class="error">' . t("error") . '</span>'; + $sRepoBar = '<span class="error">' . t("error") . '</span>'; } - - $sPackagebar=''; - $aVersions=$this->_getVersionUsage(); - foreach ($aVersions as $sVersion=>$aData){ - $sBar=$aData["info"]["revision"]?$this->_getChecksumDiv($aData["info"]["revision"]):''; - $sPackagebar.='<span title="'.$sVersion.'" style="float: left; background:#eee; height: 3px; width:'.(100/count($aVersions)).'%">'.$sBar.' </span>'; + + $sPackagebar = ''; + $aVersions = $this->_getVersionUsage(); + foreach ($aVersions as $sVersion => $aData) { + $sBar = $aData["info"]["revision"] ? $this->_getChecksumDiv($aData["info"]["revision"]) : ''; + $sPackagebar.='<span title="' . $sVersion . '" style="float: left; background:#eee; height: 3px; width:' . (100 / count($aVersions)) . '%">' . $sBar . ' </span>'; } - - $sPhaseImg=''; - $sLastPhase=''; + + $sPhaseImg = ''; + $sLastPhase = ''; foreach ($this->getActivePhases() as $sPhase) { - if ($sPhaseImg){ - $sAction=$sContinue; - if ($this->canAcceptPhase($sLastPhase)){ - $sAction=$this->renderLink("accept", $sLastPhase); + if ($sPhaseImg) { + $sAction = $sContinue; + if ($this->canAcceptPhase($sLastPhase)) { + $sAction = $this->renderLink("accept", $sLastPhase); } - $sPhaseImg.='<div class="action">'.$sAction.'</div>'; + $sPhaseImg.='<div class="action">' . $sAction . '</div>'; } - $sLastPhase=$sPhase; + $sLastPhase = $sPhase; - $sFullbar=''; - foreach (array_keys($this->_aPlaces) as $sPlace){ - $sFullbar.='<span title="'.$this->_aPlaces[$sPlace].'" style="float: left; background:#eee; height: 3px; width:'.(100/count($this->_aPlaces)).'%">'.$this->_renderBar($sPhase, $sPlace).' </span>'; + $sFullbar = ''; + foreach (array_keys($this->_aPlaces) as $sPlace) { + $sFullbar.='<span title="' . $this->_aPlaces[$sPlace] . '" style="float: left; background:#eee; height: 3px; width:' . (100 / count($this->_aPlaces)) . '%">' . $this->_renderBar($sPhase, $sPlace) . ' </span>'; } - $sDetail=$sFullbar.'<br><a href="#h3phases" class="scroll-link">'.$sPhase.'</a>'; - + $sDetail = $sFullbar . '<br><a href="#h3phases" class="scroll-link">' . $sPhase . '</a>'; + $sPhaseImg.=' - <div class="process '.$sPhase.'"> - <div class="details">'.$sDetail.' </div> - <div><img src="/deployment/images/process/bg_phase.png" alt="'.t("phase").' '.$sPhase.'"></div> + <div class="process ' . $sPhase . '"> + <div class="details">' . $sDetail . ' </div> + <div><img src="/deployment/images/process/bg_phase.png" alt="' . t("phase") . ' ' . $sPhase . '"></div> </div>'; } - $sReturn=' + $sReturn = ' <div class="visualprocess"> <div class="process"> - <div class="title">'.t("versioncontrol").'</div> + <div class="title">' . t("versioncontrol") . '</div> <div class="details"> - '.$sRepoBar.'<br> + ' . $sRepoBar . '<br> <a href="#h3repo" class="scroll-link">' . t("repositoryinfos") . '</a><br> - <strong>'. $this->_aPrjConfig["build"]["type"].'</strong> '.preg_replace('/.*\@(.*):.*/','($1)',$this->_aPrjConfig["build"]["url"]).'<br> + <strong>' . $this->_aPrjConfig["build"]["type"] . '</strong> ' . preg_replace('/.*\@(.*):.*/', '($1)', $this->_aPrjConfig["build"]["url"]) . '<br> </div> <div> - <img src="/deployment/images/process/bg_vcs.png" alt="'.t("versioncontrol").'"> + <img src="/deployment/images/process/bg_vcs.png" alt="' . t("versioncontrol") . '"> </div> </div> <div class="process"> <div class="title"> </div> - <div class="action">'.($this->canAcceptPhase()?$this->renderLink("build"):'').'</div> + <div class="action">' . ($this->canAcceptPhase() ? $this->renderLink("build") : '') . '</div> </div> <div class="process archive"> - <div class="title">'.t("archive").'</div> + <div class="title">' . t("archive") . '</div> <div class="details"> - '.$sPackagebar.'<br> + ' . $sPackagebar . '<br> <a href="#h3versions" class="scroll-link">' . t("packages") . '</a><br> - (<strong>'.count($this->_getVersionUsage()) . '</strong>) + (<strong>' . count($this->_getVersionUsage()) . '</strong>) </div> - <div><img src="/deployment/images/process/bg_archive.png" alt="'.t("archive").'"></div> + <div><img src="/deployment/images/process/bg_archive.png" alt="' . t("archive") . '"></div> </div> <div class="process"> <div class="title"> </div> - <div class="action">'.$sContinue.'</div> + <div class="action">' . $sContinue . '</div> </div> <div class="process phases"> - <div class="title">'.t("phases").'</div> - '.($sPhaseImg?$sPhaseImg:'<div class="process">'.t("none").'</div>').' + <div class="title">' . t("phases") . '</div> + ' . ($sPhaseImg ? $sPhaseImg : '<div class="process">' . t("none") . '</div>') . ' </div> </div> <div style="clear: both;"></div> @@ -2229,10 +2363,10 @@ class project { <strong>' . t('contact') . '</strong>: ' . $this->_aPrjConfig['contact'] . '<br><br> ' . $this->renderLink("setup") . ' '; - + return $sReturn; } - + /** * return html code for the setup form of an exsiting project * @return string diff --git a/public_html/deployment/classes/projectlist.class.php b/public_html/deployment/classes/projectlist.class.php index 8273a02112c33a2194e3df6de975460ad0d1d53d..6ed6ce0471834732d291c59756bbccd2635b9428 100644 --- a/public_html/deployment/classes/projectlist.class.php +++ b/public_html/deployment/classes/projectlist.class.php @@ -10,25 +10,25 @@ 2013-11-08 Axel <axel.hahn@iml.unibe.ch> ###################################################################### */ +require_once 'base.class.php'; require_once 'project.class.php'; /** * class for project overview */ -class projectlist { +class projectlist extends base{ // ---------------------------------------------------------------------- // CONFIG - // ---------------------------------------------------------------------- + // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // constructor // ---------------------------------------------------------------------- /** - * constructor - * @param array $aProjects array with all projects (overrides config data) + * constructor2 called from constructor of base class */ - public function __construct($aProjects = false) { - + public function _construct2() { + // define } // ---------------------------------------------------------------------- @@ -49,6 +49,10 @@ class projectlist { * @return string */ public function renderOverview() { + + if (!$this->oUser->hasRole("viewProjectOverview")){ + return $this->oUser->showDenied(); + } $sOut = ''; // table $sOut2 = ''; // tiles $oPrj = false; diff --git a/public_html/deployment/classes/sws.class.php b/public_html/deployment/classes/sws.class.php index 64ef326103ab7a42ab9bf38773dc8af9f743b3d7..5da960db10976988d6108636fb9dc48f63538909 100644 --- a/public_html/deployment/classes/sws.class.php +++ b/public_html/deployment/classes/sws.class.php @@ -506,8 +506,8 @@ class sws { . '<blockquote>' . '<h5>init parameters of class "'.$sMyClass.'"</h5>' - . '<code>$o = new '.$sMyClass.' ( ... )</code><br>' . '<blockquote>' + . '<code>$o = new '.$sMyClass.' ( ... )</code><br>' . $this->_showMethodInputForm($sMyClass, "__construct", $sIdPrefix."init") ; if (count($RefClass['actions']["__construct"]['params'])){ @@ -519,8 +519,8 @@ class sws { $sReturn .= '</blockquote>' . '<h5>parameters of method "'.$sAction.'"</h5>' - . '<code>$o ->' . $sAction . ' ( ... )</code><br>' . '<blockquote>' + . '<code>$o ->' . $sAction . ' ( ... )</code><br>' . $this->_showMethodInputForm($sMyClass, $sAction, $sIdPrefix."args"); if (count($RefClass['actions'][$sAction]['params'])){ $sReturn .='OR<br><fieldset><legend>json</legend>' @@ -734,6 +734,10 @@ class sws { if ( sVal.indexOf(\'"\')<0 && sVal.indexOf("\'")<0 + && sVal.indexOf("\[")<0 + && sVal.indexOf("\]")<0 + && sVal.indexOf("\{")<0 + && sVal.indexOf("\}")<0 ){ sVal=\'"\'+sVal+\'"\'; } diff --git a/public_html/deployment/classes/user.class.php b/public_html/deployment/classes/user.class.php new file mode 100644 index 0000000000000000000000000000000000000000..9cbddf405d08ea0a38741e53834a1975a7e8b37e --- /dev/null +++ b/public_html/deployment/classes/user.class.php @@ -0,0 +1,146 @@ +<?php + +/** + * user class contains username and its roles + * This class is used in the base class + * + * @author hahn + */ +class user { + + private $_sUsername=false; + private $_aUserGroups=array(); + private $_aUserRoles=array(); + private $_sLastCheckedRole=false; + + /** + * init user with optional given user + * @param type $sUser + */ + public function __construct($sUser=false){ + $this->setUser($sUser); + } + + /** + * detect a user + * @return type + */ + private function _autoDetectUser(){ + $sUser=false; + if (is_array($_SERVER) && array_key_exists("PHP_AUTH_USER", $_SERVER)){ + $sUser=$_SERVER["PHP_AUTH_USER"]; + } + return $sUser; + } + + /** + * TODO: reimplement + * get the user groups of the current user from an internal source. + * The function returns a flat aray with names of the groups + * @return array + */ + private function _getUserGroups(){ + $aGroups=array(); + if ($this->_sUsername){ + $aGroups[]="authenticated"; + $aGroups[]=$this->_sUsername; + } + $this->_aUserGroups=$aGroups; + return $this->_aUserGroups; + } + + /** + * TODO: reimplement + * get the user roles of the current user from an internal source. + * The function returns a flat aray with names of the roles + * @return array + */ + private function _getUserRoles(){ + $aRoles=array(); + + // anonymous roles: + // $aRoles[]="view"; + $aRoles[]="viewProjectOverview"; + + if ($this->hasGroup("authenticated")){ + if ($this->hasGroup("developer")){ + $aRoles[]="build"; + /* + $aRoles[]="deploy"; + $aRoles[]="accept"; + $aRoles[]="setup-project"; + * + */ + } + if ($this->hasGroup("admin")){ + // $aRoles[]="setup-all"; + } + } + + $this->_aUserRoles=$aRoles; + return $this->_aUserRoles; + } + + /** + * TODO: implement authentication somewhere + * set a new authenticated user + * @param string $sUser username + */ + public function setUser($sUser=false){ + if (!$sUser){ + $sUser=$this->_autoDetectUser(); + } + $this->_sUsername=$sUser; + $this->_getUserGroups(); + $this->_getUserRoles(); + } + + /** + * get the current username + * @return string + */ + public function getUsername(){ + return $this->_sUsername; + } + /** + * get a flat array with roles of the current user + * @return string + */ + public function getUserGroups(){ + return $this->_aUserGroups; + } + /** + * get a flat array with roles of the current user + * @return string + */ + public function getUserRoles(){ + return $this->_aUserRoles; + } + + /** + * check if the current user has a given role name + * @param string $sGroupname name of the role to check + * @return type + */ + public function hasGroup($sGroupname){ + return (array_search($sGroupname, $this->_aUserGroups)!==false); + } + /** + * check if the current user has a given role name + * @param string $sRolename name of the role to check + * @return type + */ + public function hasRole($sRolename){ + $this->_sLastCheckedRole=$sRolename; + return (array_search($sRolename, $this->_aUserRoles)!==false); + } + + /** + * return html code to display a denied message + * @return type + */ + public function showDenied(){ + return '<div class="error">'.t("class-user-error-deny-no-role").' ('.$this->_sLastCheckedRole.')</div>'; + } + +} diff --git a/public_html/deployment/classes/vcs.git.class.php b/public_html/deployment/classes/vcs.git.class.php index 5e167636b98d5e23e91413f3a6a1421a648286ec..b38eaf5485d26a04a791a6cfc8d4d54a6c711511 100644 --- a/public_html/deployment/classes/vcs.git.class.php +++ b/public_html/deployment/classes/vcs.git.class.php @@ -14,34 +14,46 @@ class vcs implements iVcs { /** * configuration - * @var type + * @var array */ - private $_aCfg=array(); - + private $_aCfg = array(); + /** * temp dir to fetch repo version and ommit message; its value will be * generated in set_config() - * @var type + * @var string */ - private $_sTempDir= false; // - + private $_sTempDir = false; // + /** * filename of ssh key file with complete path - * @var type + * @var string */ private $_sKeyfile = false; - + /** * filename of ssh wrapper script with complete path - * @var type + * @var string */ private $_sWrapper = false; + /** + * flat array with remote branch names + * @var array + */ + private $_aRemoteBranches = array(); + + /** + * name of the remote branch to access + * @var type + */ + private $_sCurrentBranch = "origin/master"; + /** * constructor - * @param type $aRepoConfig + * @param array $aRepoConfig */ - public function __construct($aRepoConfig=array()) { + public function __construct($aRepoConfig = array()) { $this->setConfig($aRepoConfig); } @@ -50,42 +62,49 @@ class vcs implements iVcs { * @param array $aRepoConfig * @return boolean */ - public function setConfig($aRepoConfig=array()){ - + public function setConfig($aRepoConfig = array()) { + // checks - foreach(array("type", "url") as $key){ - if (!array_key_exists($key, $aRepoConfig)){ - die("ERROR: key $key does not exist in config <pre>". print_r($aRepoConfig, true)."</pre>"); + foreach (array("type", "url") as $key) { + if (!array_key_exists($key, $aRepoConfig)) { + die("ERROR: key $key does not exist in config <pre>" . print_r($aRepoConfig, true) . "</pre>"); } - if (!$aRepoConfig[$key]){ - die("ERROR: key $key in config exists but is empty<pre>". print_r($aRepoConfig, true)."</pre>"); + if (!$aRepoConfig[$key]) { + die("ERROR: key $key in config exists but is empty<pre>" . print_r($aRepoConfig, true) . "</pre>"); } } - if ($aRepoConfig["type"]!=="git") { - die("ERROR: type is not git<pre>". print_r($aRepoConfig, true)."</pre>"); + if ($aRepoConfig["type"] !== "git") { + die("ERROR: type is not git<pre>" . print_r($aRepoConfig, true) . "</pre>"); } - + // set config array - $this->_aCfg=$aRepoConfig; + $this->_aCfg = $aRepoConfig; // define temp dir - $this->_sTempDir=$this->_aCfg["url"]; - $this->_sTempDir=preg_replace('/[\@\.\:\/]/', '_', $this->_sTempDir); - $this->_sTempDir=(getenv("temp")?getenv("temp"):"/tmp").'/checkout_vcsgit_'. $this->_sTempDir . "/"; - $this->_sKeyfile = $this->_aCfg["appRootDir"] . "/" . $this->_aCfg["auth"]; + $this->_sTempDir = $this->_aCfg["url"]; + $this->_sTempDir = preg_replace('/[\@\.\:\/]/', '_', $this->_sTempDir); + $this->_sTempDir = (getenv("temp") ? getenv("temp") : "/tmp") . '/checkout_vcsgit_' . $this->_sTempDir . "/"; + $this->_sKeyfile = $this->_aCfg["dataDir"] . "/sshkeys/" . $this->_aCfg["auth"]; $this->_sWrapper = $this->_aCfg["appRootDir"] . "/shellscripts/gitsshwrapper.sh"; - - return $this->_aCfg=$aRepoConfig; + + return $this->_aCfg = $aRepoConfig; + } + + /** + * set the current branch + * @param string $sBranchname name of the branch + */ + public function setCurrentBranch($sBranchname) { + return $this->_sCurrentBranch=$sBranchname; } - /** * helper: dump values * @return boolean */ - public function dump(){ - echo "<h3>Dump class ".__CLASS__."</h3>"; + public function dump() { + echo "<h3>Dump class " . __CLASS__ . "</h3>"; echo "config array: <pre>" . print_r($this->_aCfg, true) . "</pre>"; - echo "temp dir to read revision: ".$this->_sTempDir."<br>"; + echo "temp dir to read revision: " . $this->_sTempDir . "<br>"; return true; } @@ -98,8 +117,8 @@ class vcs implements iVcs { if (!is_dir($sWorkDir)) { return false; } - shell_exec('rm -rf "'.$sWorkDir.'/.git"'); - @unlink($sWorkDir."/.gitignore"); + shell_exec('rm -rf "' . $sWorkDir . '/.git"'); + @unlink($sWorkDir . "/.gitignore"); return true; } @@ -111,6 +130,43 @@ class vcs implements iVcs { return $this->_aCfg["type"]; } + /** + * read remote repository and get a flat array with names of all branches + * @return array + */ + private function _fetchRemoteBranches() { + $aReturn = array(); + $sGitCmd = 'export GIT_SSH="' . $this->_sWrapper . '" ; export PKEY="' . $this->_sKeyfile . '" ; '; + if (!file_exists($this->_sTempDir . ".git")) { + $sGitCmd.='mkdir "' . $this->_sTempDir . '" && cd "' . $this->_sTempDir . '" && '; + $sGitCmd.='git init >/dev/null && '; + $sGitCmd.='git remote add origin "' . $this->getUrl() . '" && '; + } else { + $sGitCmd.='cd "' . $this->_sTempDir . '" && '; + } + $sGitCmd.='git branch -r ; '; + exec($sGitCmd, $aOutput, $iRc); + if ($iRc == 0) { + foreach ($aOutput as $sBranch) { + // $aReturn[] = str_replace("origin/", "", trim($sBranch)); + $aReturn[] = trim($sBranch); + } + } + $this->_aRemoteBranches = $aReturn; + return $aReturn; + } + + /** + * get a flat array with names of all remote branches + * @return array + */ + public function getRemoteBranches() { + if (!$this->_aRemoteBranches) { + $this->_aRemoteBranches = $this->_fetchRemoteBranches(); + } + return $this->_aRemoteBranches; + } + /** * get current revision and log message from remote repository * @see $this::getRevision @@ -119,7 +175,7 @@ class vcs implements iVcs { public function getRepoRevision() { return $this->getRevision(false); } - + /** * get current revision and log message from an existing directory or a * remote repository @@ -137,25 +193,25 @@ class vcs implements iVcs { * ); * @return array */ - public function getRevision($sWorkDir=false) { - $aReturn=array(); - $sGitCmd = 'export GIT_SSH="'.$this->_sWrapper.'" ; export PKEY="'.$this->_sKeyfile.'" ; '; - - if ($sWorkDir){ - $sGitCmd.='cd "'.$sWorkDir.'" && '; + public function getRevision($sWorkDir = false) { + $aReturn = array(); + $sGitCmd = 'export GIT_SSH="' . $this->_sWrapper . '" ; export PKEY="' . $this->_sKeyfile . '" ; '; + + if ($sWorkDir) { + $sGitCmd.='cd "' . $sWorkDir . '" && '; } else { if (!file_exists($this->_sTempDir . ".git")) { - $sGitCmd.='mkdir "'.$this->_sTempDir.'" && cd "'.$this->_sTempDir.'" && '; + $sGitCmd.='mkdir "' . $this->_sTempDir . '" && cd "' . $this->_sTempDir . '" && '; $sGitCmd.='git init >/dev/null && '; $sGitCmd.='git remote add origin "' . $this->getUrl() . '" && '; } else { - $sGitCmd.='cd "'.$this->_sTempDir.'" && '; + $sGitCmd.='cd "' . $this->_sTempDir . '" && '; } - $sGitCmd.='git fetch --update-head-ok 2>&1 && '; + $sGitCmd.='git fetch --update-head-ok --depth 1 2>&1 && '; } - - $sGitCmd.='git log -1 origin/master ; '; - $sLoginfo.= shell_exec($sGitCmd); + + $sGitCmd.='git log -1 "' . $this->_sCurrentBranch . '" ; '; + $sLoginfo = shell_exec($sGitCmd); $sRevision = false; if (preg_match('#commit\ (.*)#', $sLoginfo, $aRev)) { @@ -164,6 +220,7 @@ class vcs implements iVcs { if ($sRevision) { $aReturn = array( + "branch" => $this->_sCurrentBranch, "revision" => $sRevision, "message" => $sLoginfo ); @@ -172,10 +229,9 @@ class vcs implements iVcs { $sLoginfo = $sGitCmd; } $aReturn = array( - "error" => $sLoginfo + "error" => '<pre>'.$sLoginfo.'<hr>'.$sGitCmd.'</pre>' ); } - return $aReturn; } @@ -184,14 +240,17 @@ class vcs implements iVcs { * @return bool */ public function getSources($sWorkDir) { - if (!$sWorkDir || !is_dir($sWorkDir)){ + if (!$sWorkDir || !is_dir($sWorkDir)) { return false; } + $sBranchname=$this->_sCurrentBranch; + $sBranchname=str_replace("origin/", "", $sBranchname); - $sGitCmd = 'export GIT_SSH="'.$this->_sWrapper.'" ; export PKEY="'.$this->_sKeyfile.'" ; '; - $sGitCmd .= 'git clone "'.$this->getUrl().'" "'.$sWorkDir.'"; '; - - return shell_exec($sGitCmd); + $sGitCmd = 'export GIT_SSH="' . $this->_sWrapper . '" ; export PKEY="' . $this->_sKeyfile . '" ; '; + $sGitCmd .= 'echo git clone --depth 1 --recursive --branch "' . $sBranchname . '" "' . $this->getUrl() . '" "' . $sWorkDir . '" ; '; + $sGitCmd .= 'git clone --depth 1 --recursive --branch "' . $sBranchname . '" "' . $this->getUrl() . '" "' . $sWorkDir . '" 2>&1; '; + $sReturn=shell_exec($sGitCmd); + return $sReturn; } /** diff --git a/public_html/deployment/gen__htusers.bat b/public_html/deployment/gen__htusers.bat index 282452caace50348126ee3b7934c222560416a62..ba85c8a44cd0e8ca36d5ad4cd76054a79c69d734 100644 --- a/public_html/deployment/gen__htusers.bat +++ b/public_html/deployment/gen__htusers.bat @@ -1,9 +1,10 @@ -@echo off - -set outfile=%~dp0.htusers - -if not exist %outfile% echo.>%outfile% -C:\xampp\apache\bin\htpasswd.exe -b %outfile% iml deployment -echo %outfile% -type %outfile% +@echo off + +set outfile=%~dp0.htusers + +if not exist %outfile% echo.>%outfile% +C:\xampp\apache\bin\htpasswd.exe -b %outfile% iml deployment +C:\xampp\apache\bin\htpasswd.exe -b %outfile% admin deployment +echo %outfile% +type %outfile% timeout 20 \ No newline at end of file diff --git a/public_html/deployment/inc_functions.php b/public_html/deployment/inc_functions.php index 7d2ecfeca98cbe35a29fc33901b8a628279f4086..b2536f36d47615b226ac89878d589554849b17f7 100644 --- a/public_html/deployment/inc_functions.php +++ b/public_html/deployment/inc_functions.php @@ -55,8 +55,19 @@ if (isset($_SERVER) && is_array($_SERVER) && array_key_exists("REQUEST_URI", $_S if (count($_POST)) foreach ($_POST as $key => $value) $aParams[$key] = $value; + + /* force integer params + foreach (array("id") as $sKey) { + if (array_key_exists($sKey, $aParams)) { + $aParams[$sKey]=(int)$aParams[$sKey]; + } + } + */ + + foreach(array_keys($aParams) as $sKey) { + $aParams[$sKey]=str_replace(array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $aParams[$sKey]); + } } - $sImageBase = "/deployment/images/nuvola64x64/"; $aImages = array( 'overview' => 'apps/fsview.png', @@ -109,9 +120,9 @@ function getTopArea() { global $aParams, $sImageBase, $aImages, $aConfig; $sReturn = ''; require_once("./classes/project.class.php"); - require_once("./classes/projectlist.class.php"); - - $oPrjList = new projectlist(); + require_once("./classes/user.class.php"); + $oUser=new user(); + $sMyPhase = "[phase]"; $sMyRev = " [no rev] "; $sJsonfile = $_SERVER["DOCUMENT_ROOT"] . "ci-webgui.json"; @@ -120,23 +131,12 @@ function getTopArea() { if (array_key_exists("date", $aJson)) $sMyRev = $aJson["date"]; } - $sPhase = '(unknown)'; - // TODO - Phasen nach Hostname - das kann nicht public gehen - $aPhases = array( - 'dev.ci.iml.unibe.ch' => array('phase' => 'dev',), - 'aum-cba02.unibe.ch' => array('phase' => 'preview',), - 'ci.iml.unibe.ch' => array('phase' => 'live',), - ); - if (array_key_exists($_SERVER["SERVER_NAME"], $aPhases)) { - $sPhase = $aPhases[$_SERVER["SERVER_NAME"]]["phase"]; - } $sBaseUrl = '/deployment/'; $sWikiBaseUrl = 'https://secure.iml.unibe.ch/wiki/doku.php'; $sReturn = ' <span id="top"></span> <div class="navbar"> - <span class="version ' . $sPhase . '">' . $sPhase . ' :: ' . $sMyRev . '</span> <div class="navbar-inner"> <span class="brand">IML Deployment GUI</span> <ul class="nav"> @@ -144,13 +144,13 @@ function getTopArea() { <li class="dropdown'; if (!array_key_exists("prj", $aParams))$sReturn.=' active'; $sReturn.='">' . str_replace('</a>', ' <b class="caret"></b></a>', aHome("")) . ' - <ul class="dropdown-menu"> - <li><a href="' . $sBaseUrl . 'all/setup/">' . t("menu-settings") . '</a></li> - <li><a href="' . $sBaseUrl . 'all/setup/actionlog/">' . t("menu-logs") . '</a></li> - <li><a href="' . $sBaseUrl . 'all/setup/new/">' . t("menu-new-project") . '</a></li> - </ul> - </li> - + <ul class="dropdown-menu"> + <li><a href="' . $sBaseUrl . 'all/setup/">' . t("menu-settings") . '</a></li> + <li><a href="' . $sBaseUrl . 'all/setup/actionlog/">' . t("menu-logs") . '</a></li> + <li><a href="' . $sBaseUrl . 'all/setup/new/">' . t("menu-new-project") . '</a></li> + </ul> + </li> + <!-- list of projects --> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">' . t("menu-projects") . '<b class="caret"></b></a> @@ -167,7 +167,7 @@ function getTopArea() { if (array_key_exists("prj", $aParams) && $aParams["prj"] <> "all") { $oPrj = new project($aParams["prj"]); $sReturn.=' - <li style="border-left: 1px solid #ccc;" class="active">' . aPrjHome(" ") . '</li> + <li class="active">' . aPrjHome(" ") . '</li> '; if (array_key_exists("action", $aParams) and FALSE) { $sReturn.='<li><a href="#">Aktion: ' . $aParams["action"] . '</a></li>'; @@ -206,6 +206,16 @@ function getTopArea() { --> </ul> <ul class="nav pull-right"> + <!-- userdata --> + <li class="dropdown"> + <a href="#" class="dropdown-toggle" data-toggle="dropdown">user: ' . $oUser->getUsername() . '<b class="caret"></b></a> + <ul class="dropdown-menu" style="width: 300px;"> + <li>groups: <pre>'.print_r($oUser->getUserGroups(), true).'</pre></li> + <li>roles: <pre>'.print_r($oUser->getUserRoles(), true).'</pre></li> + </ul> + </li> + + <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class=" icon-question-sign"></i> ' . t("menu-help") . '<b class="caret"></b></a> <ul class="dropdown-menu"> @@ -218,6 +228,7 @@ function getTopArea() { </li> </ul> </div> + <span class="version ">' . $sMyRev . ' @ '.php_uname("n").'</span> </div><div id="header2">'; if (!array_key_exists("prj", $aParams)) { diff --git a/public_html/deployment/js/functions.js b/public_html/deployment/js/functions.js index 8a146a9c67ec74669dfaa76b24b765e5f224a2e0..dc54f043fc0144355d2357c66ad494ddfa1b87de 100644 --- a/public_html/deployment/js/functions.js +++ b/public_html/deployment/js/functions.js @@ -116,6 +116,6 @@ function filterLogTable(){ $(document).ready(function() { initSoftscroll(); - $(".optionName").popover({trigger: "hover"}); - $("#content").hide().fadeIn(300); + // $(".optionName").popover({trigger: "hover"}); + // $("#content").hide().fadeIn(300); }); \ No newline at end of file diff --git a/public_html/deployment/main.css b/public_html/deployment/main.css index 705a23b9ccd558b5edf826104ae8946f42fca6dc..be5dd3e4e229e029783bb766918d3a58837ea671 100644 --- a/public_html/deployment/main.css +++ b/public_html/deployment/main.css @@ -33,11 +33,11 @@ body, label, input, button, select, textarea, p, .btn { .description{font-weight:bold; color:#ccc; font-size: 150%; font-style: italic;} .navbar .brand {color:#a33;} -.navbar .version {float: right; position: fixed; top: 0px; right: 0;padding: 0 0.5em; - transform: rotate(0deg); +.navbar .version {float: right; right: 0em; position: absolute; padding: 0 0.5em; border-bottom: 1px solid #fff; border-left: 1px solid #fff; box-shadow: 0 0 5px #888; + background: #ccc; border-bottom-left-radius: 10px; } diff --git a/public_html/deployment/pages/act_build.php b/public_html/deployment/pages/act_build.php index 3a71c418a2b5bb98d63a3a17bc1f8e449008fa8e..ec8748fd082fd881abbc76cb6c5150463d043825 100644 --- a/public_html/deployment/pages/act_build.php +++ b/public_html/deployment/pages/act_build.php @@ -19,13 +19,23 @@ $oPrj = new project($aParams["prj"]); $sOut = ''; +// switch to a branch if it was selected +$sBranchname=''; +if (array_key_exists("branchname", $aParams)) { + $sBranchname=$aParams["branchname"]; + $oPrj->setBranchname($aParams["branchname"]); +} + if (!array_key_exists("confirm", $aParams)) { // ------------------------------------------------------------ // let the user click a button to make a new build // ------------------------------------------------------------ $sNext = $oPrj->getNextPhase(); $aPhaseData2 = $oPrj->getPhaseInfos($sNext); - $sOut.='<p>' . sprintf(t("page-build-info"), $sNext, $sNext) . '</p>'; + + $sOut.='<p>' . sprintf(t("page-build-info"), $sNext, $sNext) . '</p> + ' ; + $sRevison = false; $aRepodata = $oPrj->getRepoRevision(); if (array_key_exists("revision", $aRepodata)) { @@ -45,13 +55,16 @@ if (!array_key_exists("confirm", $aParams)) { <table> <thead> <tr> - <th class="versioncontrol">' . t("versioncontrol") . '</th> + <th class="versioncontrol" colspan="2">' . t("versioncontrol") . '</th> <th> </th> <th class="' . $sNext . '" colspan="2">' . $sNext . '</th> </tr> </thead> <tbody> <tr> + <td class=""> + ' . $oPrj->getRemoteBranches() . ' + </td> <td class=""> ' . $oPrj->renderRepoInfo() . ' </td> @@ -77,6 +90,7 @@ if (!array_key_exists("confirm", $aParams)) { $sOut.=' <form action="?" method="post" enctype="multipart/form-data"> <input type="hidden" name="confirm" value="1"> + <input type="hidden" name="branchname" value="'.$sBranchname.'"> <!-- ' . enterDeployinfos() . ' <hr> --> @@ -95,6 +109,7 @@ if (!array_key_exists("confirm", $aParams)) { // action run: start action as background process // it will be initialized with jQuery $.post( "' . $sUrlStartAction . '" ... ) // see a few lines below + if (array_key_exists("run", $aParams)) { echo $oPrj->build($sOutDir . "/" . $aParams["ajax"]); die(); @@ -112,7 +127,7 @@ if (!array_key_exists("confirm", $aParams)) { $sTmpFile = $sOutDir . "/" . $aParams["ajax"]; $sOut = file_exists($sTmpFile) ? file_get_contents($sTmpFile) : ""; - echo $sLine . "/" . $sProcesses; + echo $sLine . $sProcesses; if ($sOut) { echo '<div style="margin-left: 3em; border-left: 5px dotted #eee; padding-left: 1em;">' . $sOut . '</div><div style="clear: both;"></div>' . $sLine; @@ -129,6 +144,7 @@ if (!array_key_exists("confirm", $aParams)) { . "&prj=" . $aParams["prj"] . "&action=" . $aParams["action"] . "&confirm=" . $aParams["confirm"] + . "&branchname=" . $aParams["branchname"] . "&ajax=" . $sTmpFile ; $sUrlStartAction = $sUrl . "&run=1"; diff --git a/public_html/deployment/pages/act_setup.php b/public_html/deployment/pages/act_setup.php index cce7bd3803203d838eb2ac25c258518b31e21df1..3e7a734f6c7772d2da3ea3b06d876830343d70bb 100644 --- a/public_html/deployment/pages/act_setup.php +++ b/public_html/deployment/pages/act_setup.php @@ -29,8 +29,101 @@ if ($aParams["prj"] == "all") { . '<a href="./checklang/">'.t("page-setup-info-check-lang").'</a><br>' . '<a href="./actionlog/">'.t("class-actionlog-title").'</a><br>' . '</p>'; - } + + + $i = 0; + require_once ("./classes/formgen.class.php"); + + // define editable options + $aMapping=array( + 'general'=>array( + '["workDir"]'=>array('type'=>'text'), + '["versionsToKeep"]'=>array('type'=>'text', 'validate'=>'isinteger'), + '["builtsToKeep"]'=>array('type'=>'text', 'validate'=>'isinteger'), + '["lang"]'=>array('type'=>'text'), + ), + 'install'=>array( + '["installPackages"]["user"]'=>array('type'=>'text'), + '["installPackages"]["addkeycommand"]'=>array('type'=>'text'), + '["installPackages"]["testcommand"]'=>array('type'=>'text'), + '["installPackages"]["command"]'=>array('type'=>'text'), + ), + ); + foreach ($aConfig['phases'] as $sPhase => $aPhaseData){ + $aMapping['phase-'.$sPhase]=array( + '["phases"]["'.$sPhase.'"]["css"]["bgdark"]'=>array('type'=>'text'), + '["phases"]["'.$sPhase.'"]["css"]["bglight"]'=>array('type'=>'text'), + '["phases"]["'.$sPhase.'"]["css"]["bgbutton"]'=>array('type'=>'text'), + ); + /* + if (array_key_exists("deploytimes", $aConfig["phases"][$sPhase])){ + $aMapping['phase-'.$sPhase]['["phases"]["'.$sPhase.'"]["deploytimes"]']=array('type'=>'text'); + } + * + */ + } + + $aForms = array( + 'setup' => array( + 'meta' => array( + 'method' => 'POST', + 'action' => '?', + ), + 'validate' => array(), + 'form' => array( + 'input' . $i++ => array( + 'type' => 'hidden', + 'name' => 'setupaction', + 'value' => 'save', + ), + ), + ) + ); + foreach ($aMapping as $sPartname=>$aPartData){ + + // add a headline + $aForms['setup']['form']['input' . $i++] = array( + 'type' => 'markup', + 'value' => '<h3>'.t('setup-deployment-'.$sPartname).'</h3>', + ); + + // add input items + foreach ($aPartData as $sName=>$aFormOptions){ + + $sEval='$sCfgVal=$aConfig'.$sName.';'; + eval($sEval); + + // checks + if (!$sCfgVal){ + $sError.='<li>configration variable $sConfig'.$sName.' does not exist.</li>'; + } + // echo $sEval . ' .. ' . $sName . " :: " . $sCfgVar."<br>"; + $sFormname=str_replace('"', '' , 'aConfig'.$sName); + + $aForms['setup']['form']['input' . $i++] = array( + 'value' => '<h3>'.t('setup-deployment-'.$sPartname).'</h3>', + 'type' => $aFormOptions['type'], + 'name' => $sFormname, + 'label' => 'aConfig'.$sName, + 'value' => $sCfgVal, + // 'required' => 'required', + 'validate' => 'isastring', + 'title' => htmlentities($sCfgVal), + 'size' => 100, + 'placeholder' => htmlentities($sCfgVal), + ); + } + } + if ($sError){ + $sOut.=$oPrj->getBox("error", '<ul>'.$sError.'</ul>'); + } else { + $oForm = new formgen($aForms); + $sOut.=$oForm->renderHtml("setup"); + } + + } + if (array_key_exists("par3", $aParams)) { // ------------------------------------------------------------