From 6ad4b6352315004fd8c730a00a2c65caa612239e Mon Sep 17 00:00:00 2001 From: hahn <axel.hahn@iml.unibe.ch> Date: Fri, 18 Oct 2013 12:47:11 +0200 Subject: [PATCH] temp checkin 8 --- public_html/deployment/.htaccess | 51 +- public_html/deployment/act_accept.php | 107 +- public_html/deployment/act_build.php | 86 +- public_html/deployment/act_cleanup.php | 20 + public_html/deployment/act_deploy.php | 103 +- public_html/deployment/act_doc.php | 42 +- public_html/deployment/act_overview.php | 58 +- public_html/deployment/classes/html.tpl.php | 342 ++-- .../deployment/classes/project.class.php | 1624 +++++++++-------- .../deployment/classes/projectlist.class.php | 511 +++--- .../deployment/config/inc_projects_config.php | 300 +-- public_html/deployment/gen__htusers.bat | 9 + public_html/deployment/inc_functions.php | 165 +- public_html/deployment/index.php | 102 +- shellscripts/sync_packagedir_to_puppet.php | 25 + 15 files changed, 1963 insertions(+), 1582 deletions(-) create mode 100644 public_html/deployment/act_cleanup.php create mode 100644 public_html/deployment/gen__htusers.bat create mode 100644 shellscripts/sync_packagedir_to_puppet.php diff --git a/public_html/deployment/.htaccess b/public_html/deployment/.htaccess index 270fd260..d0880edb 100644 --- a/public_html/deployment/.htaccess +++ b/public_html/deployment/.htaccess @@ -1,20 +1,31 @@ -RewriteEngine on - -# parse htm files -# RewriteRule ^(.*)\.htm$ /deployment/shared/parse.php [PT,QSA] - -# rewrite bootstrap-ressources -# RewriteCond %{REQUEST_FILENAME} !-f -# RewriteCond %{REQUEST_FILENAME} !-d -# RewriteRule /(css|js|img)/(.*)$ /deployment/gfranko-Document-Bootstrap-cf0730d/$1/$2 -# RewriteRule /(css|js|img)/(.*)$ /deployment-gui/bootstrap/$1/$2 - -RewriteBase /deployment -RewriteRule ^([a-zA-Z0-9_-]+)$ index.php?prj=$1 -RewriteRule ^([a-zA-Z0-9_-]+)/$ index.php?prj=$1 -RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)$ index.php?prj=$1&action=$2 -RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ index.php?prj=$1&action=$2 -RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)$ index.php?prj=$1&action=$2&par3=$3 -RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ index.php?prj=$1&action=$2&par3=$3 -RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)$ index.php?prj=$1&action=$2&par3=$3&par4=$4 -RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ index.php?prj=$1&action=$2&par3=$3&par4=$4 + +AuthUserFile /var/www/ci.iml.unibe.ch/public_html/deployment/.htusers +AuthName "IML DEPLOYMENT-Passwort" +AuthType Basic + +#Allow any valid user +require valid-user + + +RewriteEngine on + +# parse htm files +# RewriteRule ^(.*)\.htm$ /deployment/shared/parse.php [PT,QSA] + +# rewrite bootstrap-ressources +# RewriteCond %{REQUEST_FILENAME} !-f +# RewriteCond %{REQUEST_FILENAME} !-d +# RewriteRule /(css|js|img)/(.*)$ /deployment/gfranko-Document-Bootstrap-cf0730d/$1/$2 +# RewriteRule /(css|js|img)/(.*)$ /deployment-gui/bootstrap/$1/$2 + +RewriteBase /deployment +RewriteRule ^([a-zA-Z0-9_-]+)$ index.php?prj=$1 +RewriteRule ^([a-zA-Z0-9_-]+)/$ index.php?prj=$1 +RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)$ index.php?prj=$1&action=$2 +RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ index.php?prj=$1&action=$2 +RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)$ index.php?prj=$1&action=$2&par3=$3 +RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ index.php?prj=$1&action=$2&par3=$3 +RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)$ index.php?prj=$1&action=$2&par3=$3&par4=$4 +RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ index.php?prj=$1&action=$2&par3=$3&par4=$4 + +# besser alles ohne extension auf index.php routen und dort verarbeiten. \ No newline at end of file diff --git a/public_html/deployment/act_accept.php b/public_html/deployment/act_accept.php index c7c6feb5..d5688052 100644 --- a/public_html/deployment/act_accept.php +++ b/public_html/deployment/act_accept.php @@ -1,54 +1,53 @@ -<?php - -require_once("./config/inc_projects_config.php"); -require_once("./classes/project.class.php"); - - -// --- Checks -$oPrj=new project($aParams["prj"]); - -if (array_key_exists("par3", $aParams)){ - $sPhase=$aParams["par3"]; -} - - -$sOut='<h1>Accept :: '.$oPrj->getLabel().' - '.$sPhase.'</h1><hr>'; -if (array_key_exists("confirm", $aParams)) { - $sOut.=$oPrj->accept($sPhase); - -} else { - - if (!$sPhase){ - $sOut.='ERROR: no Phase.'; - } else { - $aPhaseData=$oPrj->getPhaseInfos($sPhase); - - $sOut.=' - <p> - auf dem Server installiert:<br> - <pre>'.print_r($aPhaseData["depoyed"], true).'</pre> - </p> - '; - - if (!$oPrj->canAcceptPhase($sPhase)){ - $sOut.="ERROR: the phase $sPhase cannot be accepted."; - } else { - // Eingabe Kommentare zum Deployment - $sOut.=' - - <form action="?" method="post" enctype="multipart/form-data"> - <input type="hidden" name="confirm" value="1"> - <input type="submit" class="btn btn-primary" value="Deploy"> - </form> - '; - } - } -} - - -$sOut.=aHome(); - - -// -- Ausgabe -$sPhpOut=$sOut; -?> +<?php + +require_once("./config/inc_projects_config.php"); +require_once("./classes/project.class.php"); + + +// --- Checks +$oPrj=new project($aParams["prj"]); + +if (array_key_exists("par3", $aParams)){ + $sPhase=$aParams["par3"]; +} + + +$sOut=''; +if (array_key_exists("confirm", $aParams)) { + $sOut.=$oPrj->accept($sPhase); +} else { + + if (!$sPhase){ + $sOut.='ERROR: no Phase.<br>'; + } else { + $aPhaseData=$oPrj->getPhaseInfos($sPhase); + + $sOut.=' + <p> + auf dem Server installiert:<br> + <pre>'.print_r($aPhaseData["depoyed"], true).'</pre> + </p> + '; + + if (!$oPrj->canAcceptPhase($sPhase)){ + $sOut.="ERROR: the phase $sPhase cannot be accepted."; + } else { + // Eingabe Kommentare zum Deployment + $sOut.=' + + <form action="?" method="post" enctype="multipart/form-data"> + <input type="hidden" name="confirm" value="1"> + <input type="submit" class="btn btn-primary" value="Deploy"> + </form> + '; + } + } +} + + +$sOut.=aHome(); + + +// -- Ausgabe +$sPhpOut=$sOut; +?> diff --git a/public_html/deployment/act_build.php b/public_html/deployment/act_build.php index 9703fd8c..e8d22bcc 100644 --- a/public_html/deployment/act_build.php +++ b/public_html/deployment/act_build.php @@ -1,43 +1,43 @@ -<?php - -require_once("./config/inc_projects_config.php"); -require_once("./classes/project.class.php"); -require_once("./classes/formgen.class.php"); - - -// --- Checks -$oPrj=new project($aParams["prj"]); - - -$sOut='<h1>Build :: '.$oPrj->getLabel().'</h1><hr>'; - -if (array_key_exists("confirm", $aParams)) { - $sOut.=$oPrj->build(); -} else { - $sNext=$oPrj->getNextPhase(); - $sOut.=' - <p> - Es wird ein neues Paket erstellt und fü die Phase <em class="'.$sNext.'">'.$sNext.'</em> bereitgestellt.<br> - </p> - '; - - // Eingabe Kommentare zum Deployment - $sOut.=' - <hr> - <form action="?" method="post" enctype="multipart/form-data"> - <input type="hidden" name="confirm" value="1"> - <!-- ' . enterDeployinfos() . ' - <hr> - --> - <input type="submit" class="btn btn-primary" value="Paket für ['.$sNext.'] erstellen"> - </form> - '; -} - - -$sOut.=aHome(); - - -// -- Ausgabe -$sPhpOut=$sOut; -?> +<?php + +require_once("./config/inc_projects_config.php"); +require_once("./classes/project.class.php"); +require_once("./classes/formgen.class.php"); + + +// --- Checks +$oPrj=new project($aParams["prj"]); + + +$sOut=''; + +if (array_key_exists("confirm", $aParams)) { + $sOut.=$oPrj->build(); +} else { + $sNext=$oPrj->getNextPhase(); + $sOut.=' + <p> + Es wird ein neues Paket erstellt und fü die Phase <em class="'.$sNext.'">'.$sNext.'</em> bereitgestellt.<br> + </p> + '; + + // Eingabe Kommentare zum Deployment + $sOut.=' + <hr> + <form action="?" method="post" enctype="multipart/form-data"> + <input type="hidden" name="confirm" value="1"> + <!-- ' . enterDeployinfos() . ' + <hr> + --> + <input type="submit" class="btn btn-primary" value="Paket für ['.$sNext.'] erstellen"> + </form> + '; +} + + +$sOut.=aHome(); + + +// -- Ausgabe +$sPhpOut=$sOut; +?> diff --git a/public_html/deployment/act_cleanup.php b/public_html/deployment/act_cleanup.php new file mode 100644 index 00000000..0a006456 --- /dev/null +++ b/public_html/deployment/act_cleanup.php @@ -0,0 +1,20 @@ +<?php + +require_once("./config/inc_projects_config.php"); +require_once("./classes/project.class.php"); + +$oPrj=new project($sPrj); + +$sPhpOut=' + + <h3>Archive Dir: '.$aConfig['archiveDir'].'</h3> + deleted:<br> + <pre>' . print_r($oPrj->cleanupArchive(), true) . '</pre> + versions left:<br> + <pre>' . print_r($oPrj->getVersions(), true). '</pre> + + <h3>Builds Dir: '.$aConfig['buildDir'].'</h3> + <pre>' . print_r($oPrj->cleanupBuilds(), true). '</pre> + '; + +?> diff --git a/public_html/deployment/act_deploy.php b/public_html/deployment/act_deploy.php index 04e41252..33825e26 100644 --- a/public_html/deployment/act_deploy.php +++ b/public_html/deployment/act_deploy.php @@ -1,51 +1,52 @@ -<?php - -require_once("./config/inc_projects_config.php"); -require_once("./classes/project.class.php"); - - -// --- Checks -$oPrj=new project($aParams["prj"]); - - -$sOut='<h1>Deploy :: '.$oPrj->getLabel().'</h1><hr>'; -if (array_key_exists("par3", $aParams)){ - $sPhase=$aParams["par3"]; -} - - -if (array_key_exists("confirm", $aParams)) { - $sOut.=$oPrj->deploy($sPhase); -} else { - if ($sPhase){ - $sPhase=$aParams["par3"]; - - $aPhaseData=$oPrj->getPhaseInfos($sPhase); - - $sOut.=' - <p> - in der Queue:<br> - <pre>'.print_r($aPhaseData["onhold"], true).'</pre> - </p> - '; - - // Eingabe Kommentare zum Deployment - $sOut.=' - - <form action="?" method="post" enctype="multipart/form-data"> - <input type="hidden" name="confirm" value="1"> - <input type="submit" class="btn btn-primary" value="Deploy"> - </form> - '; - } else { - $sOut.='ERROR: missing name of the phase.'; - } -} - - -$sOut.=aHome(); - - -// -- Ausgabe -$sPhpOut=$sOut; -?> +<?php + +require_once("./config/inc_projects_config.php"); +require_once("./classes/project.class.php"); +require_once("./inc_functions.php"); + + +// --- Checks +$oPrj=new project($aParams["prj"]); + + +$sOut=''; +if (array_key_exists("par3", $aParams)){ + $sPhase=$aParams["par3"]; +} + + +if (array_key_exists("confirm", $aParams)) { + $sOut.=$oPrj->deploy($sPhase); +} else { + if ($sPhase){ + $sPhase=$aParams["par3"]; + + $aPhaseData=$oPrj->getPhaseInfos($sPhase); + + $sOut.=' + <p> + in der Queue:<br> + <pre>'.print_r($aPhaseData["onhold"], true).'</pre> + </p> + '; + + // Eingabe Kommentare zum Deployment + $sOut.=' + + <form action="?" method="post" enctype="multipart/form-data"> + <input type="hidden" name="confirm" value="1"> + <input type="submit" class="btn btn-primary" value="Deploy"> + </form> + '; + } else { + $sOut.=getBox("error", 'ERROR: missing name of the phase.'); + } +} + + +$sOut.=aHome(); + + +// -- Ausgabe +$sPhpOut=$sOut; +?> diff --git a/public_html/deployment/act_doc.php b/public_html/deployment/act_doc.php index 91c30af2..08c451d8 100644 --- a/public_html/deployment/act_doc.php +++ b/public_html/deployment/act_doc.php @@ -1,21 +1,21 @@ -<?php - -require_once("./classes/classinfos.class.php"); -$aClasses=array( - "project"=>"project", - "projectlist"=>"projectlist", - ); -$sClass=$sPrj; - -$sPhpOut=''; -foreach ($aClasses as $sClassfile=>$sName){ - - $sPhpOut.='<a href="/deployment/'.$sClassfile.'/doc/">'.$sClassfile.'</a> | '; -} -$sPhpOut.='<hr>'; - -require_once("./classes/$sClass.class.php"); -$o=new classinfos($sPrj); -$sPhpOut.=$o->render(); -?> - +<?php + +require_once("./classes/classinfos.class.php"); +$aClasses=array( + "project"=>"project", + "projectlist"=>"projectlist", + ); +$sClass=$sPrj; + +$sPhpOut=''; +foreach ($aClasses as $sClassfile=>$sName){ + + $sPhpOut.='<a href="/deployment/'.$sClassfile.'/doc/">'.$sClassfile.'</a> | '; +} +$sPhpOut.= '<hr>'; + +require_once("./classes/$sClass.class.php"); +$o=new classinfos($sPrj); +$sPhpOut.=$o->render(); +?> + diff --git a/public_html/deployment/act_overview.php b/public_html/deployment/act_overview.php index 12c2ce90..8ff0e708 100644 --- a/public_html/deployment/act_overview.php +++ b/public_html/deployment/act_overview.php @@ -1,11 +1,47 @@ -<?php - -require_once("./config/inc_projects_config.php"); -require_once("./classes/projectlist.class.php"); - -$oPrjList=new projectlist($aProjects); -// $oPrjList->setConfig($aProjects); -$sPhpOut='<h1>Overview</h1>'.$oPrjList->renderOverview(); - -?> - +<?php + +require_once("./config/inc_projects_config.php"); + +if (!array_key_exists("prj", $aParams)){ + + // overview over all projects + require_once("./classes/projectlist.class.php"); + $oPrjList=new projectlist($aProjects); + // $oPrjList->setConfig($aProjects); + + $sPhpOut=$oPrjList->renderOverview(); +} else { + require_once("./classes/project.class.php"); + $oPrj=new project($aParams["prj"]); + $sPhpOut=' + <h3>Actions</h3> + <a href="./build/" class="btn">build</a> + <a href="./cleanup/" class="btn">cleanup</a> + <br> + <br> + <div class="tabbable"> <!-- Only required for left/right tabs --> + <ul class="nav nav-tabs"> + <li class="active"><a href="#tab1" data-toggle="tab">Config</a></li> + <li><a href="#tab2" data-toggle="tab">Queues</a></li> + <li><a href="#tab3" data-toggle="tab">Versions</a></li> + </ul> + <div class="tab-content"> + <div class="tab-pane active" id="tab1"> + <h3>Config</h3> + <pre>'.print_r($oPrj->getConfig(), true).'</pre> + </div> + <div class="tab-pane" id="tab2"> + <h3>Queues</h3> + <pre>'.print_r($oPrj->getAllPhaseInfos(), true).'</pre> + </div> + <div class="tab-pane" id="tab3"> + <h3>Versions</h3> + In the Archive exist these builds:<br> + <pre>'.print_r($oPrj->getVersions(), true).'</pre> + </div> + </div> + </div> + '; +} +?> + diff --git a/public_html/deployment/classes/html.tpl.php b/public_html/deployment/classes/html.tpl.php index 47bbe2e0..d18ee910 100644 --- a/public_html/deployment/classes/html.tpl.php +++ b/public_html/deployment/classes/html.tpl.php @@ -1,161 +1,181 @@ -<!doctype html> -<html> - <head> - <meta name="robots" content="noindex,nofollow" /> - - <meta charset="utf-8"> - <!-- Mobile viewport optimized --> - <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0" /> - <!-- Mobile Internet Explorer allows us to activate ClearType technology for smoothing fonts for easy reading --> - <!-- - not html5 valid: - <meta http-equiv="cleartype" content="on"> - --> - - <!-- Le styles --> - <link href="/deployment/bootstrap/css/bootstrap.css" rel="stylesheet"> - <link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.21/themes/black-tie/jquery-ui.css" rel="stylesheet"> - <link href="/deployment/bootstrap/css/jquery.tocify.css" rel="stylesheet"> - <link href="/deployment/bootstrap/css/prettify.css" type="text/css" rel="stylesheet" /> - <link href="/deployment/bootstrap/css/styles.css" type="text/css" rel="stylesheet" /> - - <style> - - @media (max-width: 767px) { - #toc { - position: relative; - width: 100%; - margin: 0px 0px 20px 0px; - } - } - - body{} - body, label, input, button, select, textarea, p, .btn { - font-size: 12px; - } - #header{ - background:#eee; background: linear-gradient(#fff,#eee); - padding: 1em; font-size: 300%; - color:#a33; - text-shadow: 1px 1px 0 #fff, 10px 10px 10px #aaa; - border-bottom: 2px solid #ccc; - } - - h2{font-size: 16px; margin: 0;} - h3{font-size: 14px; margin: 0;} - - ul {} - ul li { - margin-bottom: 3px; - } - - thead{background:#444; color:#eee; font-size: 130%;} - - #tbloverview th{width: 25%;} - #tbloverview td{border-left: 1px solid #ccc;} - th.preview{background:#358; color:#eee;} - th.stage{background:#388; color:#eee;} - th.live{background:#3a3; color: #eee; } - .preview{background:#f4f8ff; color:#333; background: rgba(210,220,255, 0.7);} - .stage{background:#f4ffff; color:#333; background: rgba(210,255,255, 0.7);} - .live{background:#f0fff0; color:#333; background: rgba(210,255,210, 0.7);} - - .preview, .stage, .live{ - padding: 4px; - } - - .warning{background: #fe8;} - .error{background: #fcc;} - .center{text-align: center;} - .inactive{color:#999; font-style: italic;} - - .versioninfo{ - border: 0px solid #ccc; - padding: 0px; - border-radius: 5px; - margin-bottom: 10px; - } - - .navbar-fixed-top{box-shadow: 0 5px 10px rgba(0,0,0,0.1); } - .navbar-inner{background-color: #eee; background-image: linear-gradient(to bottom, #fafafa, #ddd, #eee);} - .navbar .brand{color:#d62; font-weight: bold; font-size: 150%;} - .navbar .nav > li{border-left: 1px solid rgba(255,255,255,0.3);} - .active1{ - background-color: #cef; - box-shadow: 0 3px 8px rgba(0, 0, 0, 0.125) inset; - border-radius: 3px 3px 0 0; - } - .nav li:hover{ - background-color: rgba(0,0,0,0.04); - } - .container-fluid{padding-top: 6px;} - .row-fluid{margin-top: 10px;} - .footerbar{background-color: #f8f8f8; background-image: linear-gradient(to bottom, #f8f8f8, #f0f0f0, #fff); padding: 20px; - border-radius: 20px 20px 0 0; font-size: 80%; text-align: center; margin-top: 40px; - color:#aaa; - } - - - </style> - - <!-- - <meta name="robots" content="noindex, nofollow" /> - - - <style type="text/css" title="currentStyle"> - @import "/shared/js/datatables/media/css/demo_page.css"; - @import "/shared/js/datatables/media/css/demo_table.css"; - </style> - - <script type="text/javascript" src="/shared/js/jquery.js"></script> - <script type="text/javascript" src="/shared/js/datatables/media/js/jquery.dataTables.min.js"></script> - - <link rel="stylesheet" type="text/css" href="main.css" media="screen"> - <script type="text/javascript" src="main.js"></script> - --> - {{HEADER}} - - - </head> - <body> - - <div id="divcontent"> - - {{CONTENT}} - - </div> - {{FOOTER}}{{JSONREADY}} - - <!-- Placed at the end of the document so the pages load faster --> - <script src="/deployment/bootstrap/js/jquery-1.8.0.min.js"></script> - <script src="/deployment/bootstrap/js/jquery-ui-1.8.23.custom.min.js"></script> - <script src="/deployment/bootstrap/js/bootstrap.min.js"></script> - <script src="/deployment/bootstrap/js/history.js"></script> - <script src="/deployment/bootstrap/js/jquery.tocify.min.js"></script> - <script src="/deployment/bootstrap/js/prettify.js"></script> - <!-- <script src="js/githubrepo.js"></script> --> - <script> - $(function() { - - $("#toc").tocify({ selectors: "h2, h3, h4", scrollTo: 60, highlightOffset: 60, extendPage: false }); - - prettyPrint(); - - $(".optionName").popover({ trigger: "hover" }); - - $("a[href='#']").click(function(event) { - - event.preventDefault(); - - }); - - }); - - GetMessages(); - - </script> - - - </body> -</html> - +<!doctype html> +<html> + <head> + <meta name="robots" content="noindex,nofollow" /> + + <meta charset="utf-8"> + <!-- Mobile viewport optimized --> + <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0" /> + <!-- Mobile Internet Explorer allows us to activate ClearType technology for smoothing fonts for easy reading --> + <!-- + not html5 valid: + <meta http-equiv="cleartype" content="on"> + --> + + <!-- Le styles --> + <link href="/deployment/bootstrap/css/bootstrap.css" rel="stylesheet"> + <link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.21/themes/black-tie/jquery-ui.css" rel="stylesheet"> + <link href="/deployment/bootstrap/css/jquery.tocify.css" rel="stylesheet"> + <link href="/deployment/bootstrap/css/prettify.css" type="text/css" rel="stylesheet" /> + <link href="/deployment/bootstrap/css/styles.css" type="text/css" rel="stylesheet" /> + + <style> + + @media (max-width: 767px) { + #toc { + position: relative; + width: 100%; + margin: 0px 0px 20px 0px; + } + } + + body{} + body, label, input, button, select, textarea, p, .btn { + font-size: 12px; + } + + #header,#footer{ + background:#eee; background: linear-gradient(#fff,#eee); + padding: 1em; font-size: 300%; + color:#a33; + text-shadow: 1px 1px 0 #fff, 10px 10px 10px #aaa; + border-bottom: 2px solid #ccc; + } + #footer{ + padding: 0.3em; + background: linear-gradient(#eee,#fff); + font-size: 100%; + color:#a33; + text-align: right; + text-shadow: 1px 1px 0 #fff, 3px 3px 3px #aaa; + border-bottom: 1px solid #eee; + margin-top: 5em; + } + #header2{ + background: none; + padding: 0.5em; margin-bottom: 2em; + } + #content{ + margin-top: 2em; + border-left: 0px solid #ccc; + padding: 1em; + box-shadow: 0 0 50px #eee; + } + h2{font-size: 16px; margin: 0;} + h3{font-size: 14px; margin: 0;} + + ul {} + ul li { + margin-bottom: 3px; + } + + thead{background:#444; color:#eee; font-size: 130%;} + + #tbloverview th{width: 25%;} + #tbloverview td{border-left: 1px solid #ccc;} + th.preview{background:#358; color:#eee;} + th.stage{background:#388; color:#eee;} + th.live{background:#3a3; color: #eee; } + .preview{background:#f4f8ff; color:#333; background: rgba(210,220,255, 0.7);} + .stage{background:#f4ffff; color:#333; background: rgba(210,255,255, 0.7);} + .live{background:#f0fff0; color:#333; background: rgba(210,255,210, 0.7);} + + .preview, .stage, .live{ + padding: 4px; + } + + .warning{background: #fe8;} + .error{background: #fcc;} + .center{text-align: center;} + .inactive{color:#999; font-style: italic;} + + .versioninfo{ + border: 0px solid #ccc; + padding: 0px; + border-radius: 5px; + margin-bottom: 10px; + } + + .navbar-fixed-top{box-shadow: 0 5px 10px rgba(0,0,0,0.1); } + .navbar-inner{background-color: #eee; background-image: linear-gradient(to bottom, #fafafa, #ddd, #eee);} + .navbar .brand{color:#d62; font-weight: bold; font-size: 150%;} + .navbar .nav > li{border-left: 1px solid rgba(255,255,255,0.3);} + .active1{ + background-color: #cef; + box-shadow: 0 3px 8px rgba(0, 0, 0, 0.125) inset; + border-radius: 3px 3px 0 0; + } + .nav li:hover{ + background-color: rgba(0,0,0,0.04); + } + .container-fluid{padding-top: 6px;} + .row-fluid{margin-top: 10px;} + .footerbar{background-color: #f8f8f8; background-image: linear-gradient(to bottom, #f8f8f8, #f0f0f0, #fff); padding: 20px; + border-radius: 20px 20px 0 0; font-size: 80%; text-align: center; margin-top: 40px; + color:#aaa; + } + + + </style> + + <!-- + <meta name="robots" content="noindex, nofollow" /> + + + <style type="text/css" title="currentStyle"> + @import "/shared/js/datatables/media/css/demo_page.css"; + @import "/shared/js/datatables/media/css/demo_table.css"; + </style> + + <script type="text/javascript" src="/shared/js/jquery.js"></script> + <script type="text/javascript" src="/shared/js/datatables/media/js/jquery.dataTables.min.js"></script> + + <link rel="stylesheet" type="text/css" href="main.css" media="screen"> + <script type="text/javascript" src="main.js"></script> + --> + {{HEADER}} + + + </head> + <body> + + <div id="divcontent"> + + {{CONTENT}} + + </div> + {{FOOTER}}{{JSONREADY}} + + <!-- Placed at the end of the document so the pages load faster --> + <script src="/deployment/bootstrap/js/jquery-1.8.0.min.js"></script> + <script src="/deployment/bootstrap/js/jquery-ui-1.8.23.custom.min.js"></script> + <script src="/deployment/bootstrap/js/bootstrap.min.js"></script> + <script src="/deployment/bootstrap/js/history.js"></script> + <script src="/deployment/bootstrap/js/jquery.tocify.min.js"></script> + <script src="/deployment/bootstrap/js/prettify.js"></script> + <!-- <script src="js/githubrepo.js"></script> --> + <script> + $(function() { + + $("#toc").tocify({ selectors: "h2, h3, h4", scrollTo: 60, highlightOffset: 60, extendPage: false }); + + prettyPrint(); + + $(".optionName").popover({ trigger: "hover" }); + + $("a[href='#']").click(function(event) { + + event.preventDefault(); + + }); + + }); + + GetMessages(); + + </script> + + + </body> +</html> + diff --git a/public_html/deployment/classes/project.class.php b/public_html/deployment/classes/project.class.php index 515d5e25..7d3e6a6a 100644 --- a/public_html/deployment/classes/project.class.php +++ b/public_html/deployment/classes/project.class.php @@ -1,726 +1,900 @@ -<?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(); - - /** - * places of version infos in each deployment phase - * @var type - */ - private $_aPlaces = array( - "onhold" => "Queue", - "ready4deployment" => "Repo", - "deployed" => "Installiert", - ); - private $_iRcAll = 0; - - // ---------------------------------------------------------------------- - // constructor - // ---------------------------------------------------------------------- - - /** - * constructor - * use setConfig method if you initialize without config - * @param array $aConfig config 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: "" . $this->_aConfig['packageDir'] . ""."); - - if (!array_key_exists("archiveDir", $this->_aConfig)) - die("ERROR::CONFIG: key "archiveDir" 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: "" . $this->_aConfig['archiveDir'] . ""."); - - if (!array_key_exists("phases", $this->_aPrjConfig)) - die("ERROR::CONFIG: key "phases" 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() { - $s = $this->_aConfig['workDir'] . "/build/" . $this->_aPrjConfig["fileprefix"] . "_" . date("Ymd-His"); - // TODO: auskommentieren - $s = $this->_aConfig['workDir'] . "/build/" . $this->_aPrjConfig["fileprefix"] . "_temp"; - return $s; - } - - /** - * 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->_aConfig["archiveDir"] . '/' . $this->_aConfig["id"] . '/' . $sTimestamp; - } - - /** - * 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 (!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] = json_decode(file_get_contents($sJsonfile), true); - $aTmp[$sKey]["infofile"] = $sJsonfile; - $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] = json_decode(file_get_contents($sJsonfile), true); - $aTmp[$sKey]["infofile"] = $sJsonfile; - $aTmp[$sKey]["packagefile"] = $sPkgfile; - $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]["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 - // ---------------------------------------------------------------------- - - public function build() { - global $aParams; - $sReturn = false; - - $this->_iRcAll = 0; - - // -------------------------------------------------- - // create workdir - // -------------------------------------------------- - $sTempDir = $this->_getTempDir(); - $sFirstLevel = $this->getNextPhase(); - if (!$sFirstLevel) return false; - - $sReturn.="<h2>Create a temporary build dir</h2>"; - if (!file_exists($sTempDir)) { - $sReturn.="* create " . $sTempDir . "<br>"; - mkdir($sTempDir); - } - $sReturn.=$this->_execAndSend("ls -ld " . $sTempDir); - if (!file_exists($sTempDir)) { - $sReturn.="ERROR: build - $sTempDir was not created."; - return $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.=$sVersioninfo; - - break; - - default: - die("ERROR: Build Type not supported: " . $this->_aPrjConfig["build"]["type"]); - break; - } - - - // -------------------------------------------------- - // execute hook - // -------------------------------------------------- - $sHookfile = $this->_aConfig['hooks']['build-aftercheckout']; - $sReturn.='<h2>Execute Hook ' . $sHookfile . '</h2>'; - if (file_exists($sTempDir . '/' . $sHookfile)) { - $sReturn.=$this->_execAndSend('cd ' . $sTempDir . ' && bash "' . $sHookfile . '"'); - } else { - $sReturn.='SKIP. Hook was not found.<br>'; - } - - // -------------------------------------------------- - // TODO: cleanup .git, .svn, ...? - // -------------------------------------------------- - // -------------------------------------------------- - // create package - // -------------------------------------------------- - $sReturn.='<h2>Create package</h2>'; - - // public_html must exist - $sWebroot = $sTempDir . '/public_html'; - if (!file_exists($sWebroot)) { - $sReturn.='ERROR: a subdir public_html does not exist.'; - return $sReturn; - } - - // 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))) { - $sReturn.="ERROR: directory was not created."; - return $sReturn; - } - - // TODO: cleanup - - $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) { - $sReturn.='<h3>ERROR: creation failed</h3>One of the commands failed (see above).<br>You can ask the sysadmins and analyze with them the created temp directory "' . $sTempDir . '".'; - return $sReturn; - } - - // TODO: force synch archive to puppet master - - $sReturn.=$this->queue($sFirstLevel, $sTs2); - - - $sReturn.="<h2>cleanup $sTempDir</h2>"; - $sReturn.=$this->_execAndSend("rm -rf $sTempDir"); - - $sReturn.="* Move [Paketarchiv]/[prj].[tgz+json] zu [RepoDir-von-" . $sFirstLevel . "]<br>"; - $sReturn.="<br>"; - - - return $sReturn; - } - - public function queue($sTargetphase, $sVersion) { - - if (!$this->isActivePhase($sTargetphase)) return false; - - $sReturn="<h2>Queue to $sTargetphase</h2>"; - $sPlace="onhold"; - - $sLinkTarget = $this->_getArchiveDir($sVersion); - $sLinkName = $this->_getFileBase($sTargetphase, $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) { - $sReturn.='<h3>ERROR: Queuing failed</h3>One of the commands failed (see above).'; - return $sReturn; - } - - // TODO: force synch to puppet master - - $sReturn.="<br>SUCCESS: the version $sVersion was set to place $sPlace<br>"; - - - $sReturn.=$this->deploy($sTargetphase); - // $sReturn.=$this->_execAndSend(""); - - return $sReturn; - } - - 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.="SKIP: no current queue - $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) { - $sReturn.='<h3>ERROR: Deployment failed</h3>One of the commands failed (see above).'; - return $sReturn; - } - - // TODO: force synch to puppet master - - $sReturn.="<br>SUCCESS: deployment was done and will be installed soon<br>"; - return $sReturn; - } - -} - +<?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: "" . $this->_aConfig['packageDir'] . ""."); + + if (!array_key_exists("archiveDir", $this->_aConfig)) + die("ERROR::CONFIG: key "archiveDir" 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: "" . $this->_aConfig['archiveDir'] . ""."); + + if (!array_key_exists("phases", $this->_aPrjConfig)) + die("ERROR::CONFIG: key "phases" 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 "' . $sTempDir . '".". $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 "' . $sTempDir . '".". $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 "public_html" 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 "$sTempDir"". $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 "' . $sTempDir . '".' . $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; + } + + public function queue($sTargetphase, $sVersion) { + + if (!$this->isActivePhase($sTargetphase)) return false; + + $sReturn="<h2>Queue to $sTargetphase</h2>"; + $sPlace="onhold"; + + $sLinkTarget = $this->_getArchiveDir($sVersion); + $sLinkName = $this->_getFileBase($sTargetphase, $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($sTargetphase); + + return $sReturn; + } + + 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; + } + +} + ?> \ No newline at end of file diff --git a/public_html/deployment/classes/projectlist.class.php b/public_html/deployment/classes/projectlist.class.php index 7a628404..bc43c487 100644 --- a/public_html/deployment/classes/projectlist.class.php +++ b/public_html/deployment/classes/projectlist.class.php @@ -1,254 +1,259 @@ -<?php - -require_once 'project.class.php'; - -/** - * class for project overview - */ -class projectlist { - - // ---------------------------------------------------------------------- - // CONFIG - // ---------------------------------------------------------------------- - protected $_aProjects = array(); - protected $_aPhases = array("preview", "stage", "live"); - protected $_sCfgfile="../config/inc_projects_config.php"; - - // ---------------------------------------------------------------------- - // constructor - // ---------------------------------------------------------------------- - - /** - * constructor - * @param array $aProjects array with all projects (overrides config data) - */ - public function __construct($aProjects = false) { - $this->_readConfig(); - if ($aProjects) { - $this->setConfig($aProjects); - } - } - - // ---------------------------------------------------------------------- - // private functions - // ---------------------------------------------------------------------- - - /** - * read config file - * @return boolean - */ - private function _readConfig() { - $this->_aProjects = array(); - require(__dir__ . '/' . $this->_sCfgfile); - $this->_aProjects = $aProjects; - $this->_aPhases = $aConfig["phases"]; - $this->_verifyConfig(); - return true; - } - - /** - * validate config data - * @return boolean - */ - private function _verifyConfig() { - if (!count($this->_aProjects)) { - die("ERROR: no project was found."); - } - // TODO: verify - return true; - } - - // ---------------------------------------------------------------------- - // GETTER - // ---------------------------------------------------------------------- - - /** - * get list of all project IDs - * @return type - */ - public function getProjects() { - return array_keys($this->_aProjects); - } - - // ---------------------------------------------------------------------- - // SETTER - // ---------------------------------------------------------------------- - - /** - * applay a custom project array - TODO: DLETE?? - * @param array $aProjects - * @return boolean - */ - public function setConfig($aProjects) { - $this->_aProjects = $aProjects; - $this->_verifyConfig(); - return true; - } - - // ---------------------------------------------------------------------- - // ACTIONS - // ---------------------------------------------------------------------- - - /** - * render html for overview table - * @return string - */ - public function renderOverview() { - $sOut = ''; - $oPrj = false; - - $sDebug = ''; - - // loop over projects - foreach ($this->_aProjects as $sPrj => $aData) { - $oPrj = new project($sPrj); - - $aPrjData = $oPrj->getAllPhaseInfos(); - $sDebug .= "<h2>$sPrj</h2>CONFIG: <pre>".print_r($oPrj->getConfig(), true)."</pre> DATA: <pre>" . print_r($aPrjData, true) . "</pre><hr>"; - - $sOutPhases = ''; - - // loop over phases ... - foreach (array_keys($this->_aPhases) as $sPhase) { - $aTmp = $aPrjData[$sPhase]; - $h = "onhold"; - $q = "ready4deployment"; - $d = "deployed"; - - if (!$oPrj->isActivePhase($sPhase)) { - $sOutPhases.=' - <td class="' . $sPhase . '" colspan="3"> - <div class="versioninfo center inactive"><i class="icon-ban-circle"></i> inactive</div> - </td>'; - } else { - - // --- on hold - $sInfoH = ''; - if (array_key_exists("ok", $aTmp[$h]) && array_key_exists("timestamp", $aTmp[$h])) { - $sInfoH = '<div class="versioninfo"> - <i class="icon-calendar"></i> ' . $aTmp[$h]["date"] . '<br> - <i class="icon-tag"></i> ' . $aTmp[$h]["revision"] . '<br> - Anm.: ' . $aTmp[$h]["remark"] . '<br>'; - if (array_key_exists("deploytimes", $this->_aPhases[$sPhase]) || true){ - $sInfoH .= '<br>Deployment: '. implode(",", array_values($this->_aPhases[$sPhase]["deploytimes"])); - } - $sInfoH .= ' - <br> - <a href="/deployment/'.$sPrj.'/deploy/'.$sPhase.'/" class="btn '.$sPhase.'"><i class=" icon-forward"></i> Deploy to '.$sPhase.'-Queue</a> - </div>'; - } else { - if (array_key_exists("error", $aTmp[$h])) { - $sInfoH = '<div class="error"><i class="icon-exclamation-sign"></i> FEHLER:<br>' . $aTmp[$h]["error"] . '</div>'; - } else { - $sInfoH = 'empty'; - } - } - - // --- ready4deployment have the same version? - $sInfoQ = ''; - if (array_key_exists("ok", $aTmp[$q]) && array_key_exists("ok", $aTmp[$d]) && $aTmp[$q]["timestamp"] > " " && $aTmp[$q]["timestamp"] == $aTmp[$d]["timestamp"]) { - $sInfoQ = "deployed"; - } else { - if (array_key_exists("ok", $aTmp[$q])) { - $sInfoQ = '<div class="versioninfo"> - <i class="icon-calendar"></i> ' . $aTmp[$q]["date"] . '<br> - <i class="icon-tag"></i> ' . $aTmp[$q]["revision"] . '<br> - Anm.: ' . $aTmp[$q]["remark"] . ' - </div>'; - } else { - if (array_key_exists("error", $aTmp[$q])) { - $sInfoQ = '<div class="error"><i class="icon-exclamation-sign"></i> FEHLER:<br>' . $aTmp[$q]["error"] . '</div>'; - } - } - } - - // -- deployment infos - $sInfoD = ''; - if (array_key_exists("ok", $aTmp[$d])){ - $sInfoD = ' - <i class="icon-calendar"></i> ' . $aTmp[$d]["date"] . '<br> - <i class="icon-tag"></i> ' . $aTmp[$d]["revision"] . '<br> - Anm.: ' . $aTmp[$d]["remark"] . ' - '; - } - if (array_key_exists("warning", $aTmp[$d])) { - $sInfoD = '<div class="warning"><i class="icon-info-sign"></i> WARNUNG:<br>' . $aTmp[$d]["warning"] . '</div>'; - } - if (array_key_exists("error", $aTmp[$d])) { - $sInfoD = '<div class="error"><i class="icon-exclamation-sign"></i> FEHLER:<br>' . $aTmp[$d]["error"] . '</div>'; - } - - if ($oPrj->canAcceptPhase($sPhase)){ - $sNext=$oPrj->getNextPhase($sPhase); - $sInfoD.='<br><a href="/deployment/'.$sPrj.'/accept/'.$sPhase.'/" class="btn '.$sNext.'"><i class=" icon-forward"></i> Accept für ['.$sNext.']</a>'; - } - - // output - $sOutPhases.=' - <td class="' . $sPhase . '"> - <div class="versioninfo"> - ' . $sInfoH . ' - </div> - </td> - <td class="' . $sPhase . '"> - <div class="versioninfo"> - ' . $sInfoQ . ' - </div> - </td> - <td class="' . $sPhase . '"> - <div class="versioninfo"> - ' . $sInfoD . ' - </div> - </td> - '; - } - } - - - // render output - $sOut.=' - <tr> - <td class="prj"> - <strong>' . $oPrj->getLabel() . '</strong><br> - ' . $oPrj->getDescription() . '<br>'; - if ($oPrj->canAcceptPhase()){ - $sNext=$oPrj->getNextPhase(); - $sOut.='<a href="/deployment/'.$sPrj.'/build/" class="btn '.$sNext.'"><i class=" icon-forward"></i> Build für ['.$sNext.']</a>'; - } - $sOut.='</td> - ' . $sOutPhases . ' - </tr>'; - } - if ($sOut) - $sOut = ' - <table class="table" id="tbloverview"> - <thead> - <tr> - <th class="prj">Projekt</th> - <th class="preview" colspan="3" >Preview</th> - <th class="stage" colspan="3" >Stage</th> - <th class="live" colspan="3" >Live</th> - </tr> - <tr> - <td></td> - <td class="preview">Queue</td> - <td class="preview">Repo</td> - <td class="preview">Installiert</td> - <td class="stage">Queue</td> - <td class="stage">Repo</td> - <td class="stage">Installiert</td> - <td class="live">Queue</td> - <td class="live">Repo</td> - <td class="live">Installiert</td> - </tr> - </thead> - <tbody> - ' . $sOut . '</tbody></table>'; - - $sOut.=$sDebug; - return $sOut; - } - -} - +<?php + +require_once 'project.class.php'; + +/** + * class for project overview + */ +class projectlist { + + // ---------------------------------------------------------------------- + // CONFIG + // ---------------------------------------------------------------------- + protected $_aProjects = array(); + protected $_aPhases = array("preview", "stage", "live"); + protected $_sCfgfile="../config/inc_projects_config.php"; + + // ---------------------------------------------------------------------- + // constructor + // ---------------------------------------------------------------------- + + /** + * constructor + * @param array $aProjects array with all projects (overrides config data) + */ + public function __construct($aProjects = false) { + $this->_readConfig(); + if ($aProjects) { + $this->setConfig($aProjects); + } + } + + // ---------------------------------------------------------------------- + // private functions + // ---------------------------------------------------------------------- + + /** + * read config file + * @return boolean + */ + private function _readConfig() { + $this->_aProjects = array(); + require(__dir__ . '/' . $this->_sCfgfile); + $this->_aProjects = $aProjects; + $this->_aPhases = $aConfig["phases"]; + $this->_verifyConfig(); + return true; + } + + /** + * validate config data + * @return boolean + */ + private function _verifyConfig() { + if (!count($this->_aProjects)) { + die("ERROR: no project was found."); + } + // TODO: verify + return true; + } + + // ---------------------------------------------------------------------- + // GETTER + // ---------------------------------------------------------------------- + + /** + * get list of all project IDs + * @return type + */ + public function getProjects() { + return array_keys($this->_aProjects); + } + + // ---------------------------------------------------------------------- + // SETTER + // ---------------------------------------------------------------------- + + /** + * applay a custom project array - TODO: DLETE?? + * @param array $aProjects + * @return boolean + */ + public function setConfig($aProjects) { + $this->_aProjects = $aProjects; + $this->_verifyConfig(); + return true; + } + + // ---------------------------------------------------------------------- + // ACTIONS + // ---------------------------------------------------------------------- + + /** + * render html for overview table + * @return string + */ + public function renderOverview() { + $sOut = ''; + $oPrj = false; + + $sNavi=''; + $sNavi.='<option value="trproject">Alle</option>'; + // loop over projects + foreach ($this->_aProjects as $sPrj => $aData) { + + + $oPrj = new project($sPrj); + + $sNavi.='<option value="'.$sPrj.'">'.$oPrj->getLabel().'</option>'; + $aPrjData = $oPrj->getAllPhaseInfos(); + + $sOutPhases = ''; + + // loop over phases ... + foreach (array_keys($this->_aPhases) as $sPhase) { + $aTmp = $aPrjData[$sPhase]; + $h = "onhold"; + $q = "ready4deployment"; + $d = "deployed"; + + if (!$oPrj->isActivePhase($sPhase)) { + $sOutPhases.=' + <td class="' . $sPhase . '" colspan="3"> + <div class="versioninfo center inactive"><i class="icon-ban-circle"></i> inactive</div> + </td>'; + } else { + + // --- on hold + $sInfoH = ''; + if (array_key_exists("ok", $aTmp[$h]) && array_key_exists("timestamp", $aTmp[$h])) { + $sInfoH = '<div class="versioninfo"> + <i class="icon-calendar"></i> ' . $aTmp[$h]["date"] . '<br> + <i class="icon-tag"></i> ' . $aTmp[$h]["revision"] . '<br> + Anm.: ' . $aTmp[$h]["remark"] . '<br>'; + if (array_key_exists("deploytimes", $this->_aPhases[$sPhase]) || true){ + $sInfoH .= '<br>Deployment: '. implode(",", array_values($this->_aPhases[$sPhase]["deploytimes"])); + } + $sInfoH .= ' + <br> + <a href="/deployment/'.$sPrj.'/deploy/'.$sPhase.'/" class="btn '.$sPhase.'"><i class=" icon-forward"></i> Deploy to '.$sPhase.'-Queue</a> + </div>'; + } else { + if (array_key_exists("error", $aTmp[$h])) { + $sInfoH = '<div class="error"><i class="icon-exclamation-sign"></i> FEHLER:<br>' . $aTmp[$h]["error"] . '</div>'; + } else { + $sInfoH = 'empty'; + } + } + + // --- ready4deployment have the same version? + $sInfoQ = ''; + if (array_key_exists("ok", $aTmp[$q]) && array_key_exists("ok", $aTmp[$d]) && $aTmp[$q]["timestamp"] > " " && $aTmp[$q]["timestamp"] == $aTmp[$d]["timestamp"]) { + $sInfoQ = "deployed"; + } else { + if (array_key_exists("ok", $aTmp[$q])) { + $sInfoQ = '<div class="versioninfo"> + <i class="icon-calendar"></i> ' . $aTmp[$q]["date"] . '<br> + <i class="icon-tag"></i> ' . $aTmp[$q]["revision"] . '<br> + Anm.: ' . $aTmp[$q]["remark"] . ' + </div>'; + } else { + if (array_key_exists("error", $aTmp[$q])) { + $sInfoQ = '<div class="error"><i class="icon-exclamation-sign"></i> FEHLER:<br>' . $aTmp[$q]["error"] . '</div>'; + } + } + } + + // -- deployment infos + $sInfoD = ''; + if (array_key_exists("ok", $aTmp[$d])){ + $sInfoD = ' + <i class="icon-calendar"></i> ' . $aTmp[$d]["date"] . '<br> + <i class="icon-tag"></i> ' . $aTmp[$d]["revision"] . '<br> + Anm.: ' . $aTmp[$d]["remark"] . ' + '; + } + if (array_key_exists("warning", $aTmp[$d])) { + $sInfoD = '<div class="warning"><i class="icon-info-sign"></i> WARNUNG:<br>' . $aTmp[$d]["warning"] . '</div>'; + } + if (array_key_exists("error", $aTmp[$d])) { + $sInfoD = '<div class="error"><i class="icon-exclamation-sign"></i> FEHLER:<br>' . $aTmp[$d]["error"] . '</div>'; + } + + if ($oPrj->canAcceptPhase($sPhase)){ + $sNext=$oPrj->getNextPhase($sPhase); + $sInfoD.='<br><a href="/deployment/'.$sPrj.'/accept/'.$sPhase.'/" class="btn '.$sNext.'"><i class=" icon-forward"></i> Accept für ['.$sNext.']</a>'; + } + + // output + $sOutPhases.=' + <td class="' . $sPhase . '"> + <div class="versioninfo"> + ' . $sInfoH . ' + </div> + </td> + <td class="' . $sPhase . '"> + <div class="versioninfo"> + ' . $sInfoQ . ' + </div> + </td> + <td class="' . $sPhase . '"> + <div class="versioninfo"> + ' . $sInfoD . ' + </div> + </td> + '; + } + } + + + // render output + $sOut.=' + <tr class="'.$sPrj.' trproject"> + <td class="prj"> + <a href="/deployment/'.$sPrj.'/" class="btn "><i class=" icon-book"></i> '.$oPrj->getLabel().'</a><br> + ' . $oPrj->getDescription() . '<br>'; + if ($oPrj->canAcceptPhase()){ + $sNext=$oPrj->getNextPhase(); + $sOut.='<a href="/deployment/'.$sPrj.'/build/" class="btn '.$sNext.'"><i class=" icon-forward"></i> Build für ['.$sNext.']</a>'; + } + $sOut.='</td> + ' . $sOutPhases . ' + </tr>'; + } + if ($sOut) + $sOut = ' + Projekt-Filter: + <select onchange="$(\'.trproject\').hide(); $(\'.\' + this.value).show();"> + '.$sNavi.' + </select> + <table class="table" id="tbloverview"> + <thead> + <tr> + <th class="prj">Projekt</th> + <th class="preview" colspan="3" >Preview</th> + <th class="stage" colspan="3" >Stage</th> + <th class="live" colspan="3" >Live</th> + </tr> + <tr> + <td></td> + <td class="preview">Queue</td> + <td class="preview">Repo</td> + <td class="preview">Installiert</td> + <td class="stage">Queue</td> + <td class="stage">Repo</td> + <td class="stage">Installiert</td> + <td class="live">Queue</td> + <td class="live">Repo</td> + <td class="live">Installiert</td> + </tr> + </thead> + <tbody> + ' . $sOut . '</tbody></table>'; + + return $sOut; + } + +} + ?> \ No newline at end of file diff --git a/public_html/deployment/config/inc_projects_config.php b/public_html/deployment/config/inc_projects_config.php index a554ba65..4a4cbf6d 100644 --- a/public_html/deployment/config/inc_projects_config.php +++ b/public_html/deployment/config/inc_projects_config.php @@ -1,146 +1,154 @@ -<?php - - -// ---------------------------------------------------------------------- -// fetch status infos von den einzelnen Phasen -// ---------------------------------------------------------------------- - -$aConfig=array( - - // Basispfad: - 'workDir'=>'/var/imldeployment', - 'hooks'=>array( - 'build-aftercheckout'=>'hooks/build.sh', - ), - - - 'phases'=>array( - "preview"=>array( - // "deploytimes"=>array('/(Mon|Tue|Wed|Thu)\ 4/'), - ), - "stage"=>array(), - "live"=>array( - // Als Idee ... Datum mit Regex filtern - // wenn deploytimes existiert, dann wird nach dem Deploy das Paket - // in einer Queue zurueckgehalten - "deploytimes"=>array('/(Mon|Tue|Wed|Thu)\ 14\:/'), - ), - ), -); - - -// ---------------------------------------------------------------------- -// Projekte -// ---------------------------------------------------------------------- -$aProjects=array( - "ci"=>array( - "label"=>"CI Webgui", - "fileprefix"=>"ci-webgui", - "description" => 'Webgui zum Deployen von Web-Projekten', - "contact" => 'axel.hahn@iml.unibe.ch', - "build"=>array( - "type"=>"git", // one of git, svn, ... - "ssh"=>"git@gitlab.iml.unibe.ch:admins/imldeployment.git", - - // der public Key muss beim Git-Repo hinterlegt sein - "keyfile"=>"config/sshkeys/git@gitlab.iml.unibe.ch", - ), - - "phases"=>array( - "preview"=>array( - // "url"=>"http://preview.scrudu.iml.unibe.ch/", - "url"=>"http://ci.iml.unibe.ch/deployment/", - ), - "stage"=>array( - ), - "live"=>array( - // "url"=>"http://www.scrudu.iml.unibe.ch/" - ), - ), - ), - "scrudu"=>array( - "label"=>"SCRUDU", - "fileprefix"=>"scrudu", - "description" => 'Das sagenumwogene Scrudu Dings', - "contact" => 'Entwickler, Verantwortliche etc.', - "build"=>array( - "type"=>"git", // one of git, svn, ... - // for github: - // generate ssh keypair - // https://help.github.com/articles/generating-ssh-keys - "ssh"=>"ssh gituser@gitserver:/path/to/project", - // der public Key muss beim Git-Repo hinterlegt sein - "keyfile"=>"config/sshkeys/git@gitlab.iml.unibe.ch", - ), - - "phases"=>array( - "preview"=>array( - // "url"=>"http://preview.scrudu.iml.unibe.ch/", - ), - "stage"=>array( - // "url"=>"http://stage.scrudu.iml.unibe.ch/" - ), - "live"=>array( - // "url"=>"http://www.scrudu.iml.unibe.ch/" - ), - ), - ), - "prj1"=>array( - "label"=>"Test-Projekt.", - "fileprefix"=>"testprj", - "description" => 'Eine kurze Beschreibung, um was für eine Applikation es sich handelt', - "contact" => 'Entwickler, Verantwortliche etc.', - "build"=>array( - "type"=>"git", // one of git, svn, ... - "url"=>"ssh gituser@gitserver:/path/to/project", - ), - - "phases"=>array(), - ), -); - -// ---------------------------------------------------------------------- -// Checkout-Informationen fuer versch. Sourcen -// ---------------------------------------------------------------------- -$aTypeAbstraction=array( - - "git"=>array( - "checkout"=>"git pull [url]", - "getrevision"=>"", - "built"=>"[workdir]/buildpackage.sh", - "deleteDirs"=>array( - ".git", - "buildpackage.sh" - ), - ), - /* - "svn"=>array( - "checkout"=>"svn co [url]", - "getrevision"=>"svn info | grep '^Revision' | cut -f 2 -d ':' ", - "built"=>false, - "deleteDirs"=>".svn", - ), - */ - -); - -// ---------------------------------------------------------------------- -// override for local development -// ---------------------------------------------------------------------- - -switch ($_SERVER["SERVER_NAME"]) { - case "localhost": - case "dev.ci.iml.unibe.ch": - $aConfig['workDir']="D:\imldeployment"; - $aProjects["scrudu"]["phases"]["preview"]["url"]="http://localhost/deployment/"; - break; - - default: - break; -} - - -$aConfig=array_merge($aConfig, array( - 'packageDir'=>$aConfig['workDir'].'/packages', - 'archiveDir'=>$aConfig['workDir'].'/packages/_files', -)); +<?php + + +// ---------------------------------------------------------------------- +// fetch status infos von den einzelnen Phasen +// ---------------------------------------------------------------------- + +$aConfig=array( + + // Basispfad: + 'workDir'=>'/var/imldeployment', + 'versionsToKeep'=>10, // for cleanup: keep n unused versions + 'builtsToKeep'=>3, // for cleanup: keep n failed builds + 'hooks'=>array( + 'build-aftercheckout'=>'hooks/onbuild', + ), + + // rsync of archives + 'mirrorPackages'=>array( + 'puppet'=>'ladmin@calcium.iml.unibe.ch:/share/imldeployment', + ), + + + 'phases'=>array( + "preview"=>array( + // "deploytimes"=>array('/(Mon|Tue|Wed|Thu)\ 4/'), + ), + "stage"=>array(), + "live"=>array( + // wenn deploytimes existiert, dann wird nach dem Deploy das Paket + // in einer Queue zurueckgehalten + "deploytimes"=>array('/(Mon|Tue|Wed|Thu)\ 14\:/'), + ), + ), +); + + +// ---------------------------------------------------------------------- +// Projekte +// ---------------------------------------------------------------------- +$aProjects=array( + "ci"=>array( + "label"=>"CI Webgui", + "fileprefix"=>"ci-webgui", + "description" => 'Webgui zum Deployen von Web-Projekten', + "contact" => 'axel.hahn@iml.unibe.ch', + "build"=>array( + "type"=>"git", // one of git, svn, ... + "ssh"=>"git@gitlab.iml.unibe.ch:admins/imldeployment.git", + + // der public Key muss beim Git-Repo hinterlegt sein + "keyfile"=>"config/sshkeys/git@gitlab.iml.unibe.ch", + ), + + "phases"=>array( + "preview"=>array( + // "url"=>"http://preview.scrudu.iml.unibe.ch/", + "url"=>"http://ci.iml.unibe.ch/deployment/", + ), + "stage"=>array( + ), + "live"=>array( + // "url"=>"http://www.scrudu.iml.unibe.ch/" + ), + ), + ), + "scrudu"=>array( + "label"=>"SCRUDU", + "fileprefix"=>"scrudu", + "description" => 'Das sagenumwogene Scrudu Dings', + "contact" => 'Entwickler, Verantwortliche etc.', + "build"=>array( + "type"=>"git", // one of git, svn, ... + // for github: + // generate ssh keypair + // https://help.github.com/articles/generating-ssh-keys + "ssh"=>"git@github.com:iml-it/scrudu.git", + // der public Key muss beim Git-Repo hinterlegt sein + "keyfile"=>"config/sshkeys/git@gitlab.iml.unibe.ch", + ), + + "phases"=>array( + "preview"=>array( + "url"=>"http://preview.scrudu.iml.unibe.ch/", + ), + "stage"=>array( + // "url"=>"http://stage.scrudu.iml.unibe.ch/" + ), + "live"=>array( + // "url"=>"http://www.scrudu.iml.unibe.ch/" + ), + ), + ), + "prj1"=>array( + "label"=>"Test-Projekt.", + "fileprefix"=>"testprj", + "description" => 'Eine kurze Beschreibung, um was für eine Applikation es sich handelt', + "contact" => 'Entwickler, Verantwortliche etc.', + "build"=>array( + "type"=>"git", // one of git, svn, ... + "url"=>"ssh gituser@gitserver:/path/to/project", + ), + + "phases"=>array(), + ), +); + +// ---------------------------------------------------------------------- +// Checkout-Informationen fuer versch. Sourcen +// ---------------------------------------------------------------------- +$aTypeAbstraction=array( + + "git"=>array( + "checkout"=>"git pull [url]", + "getrevision"=>"", + "built"=>"[workdir]/buildpackage.sh", + "deleteDirs"=>array( + ".git", + "buildpackage.sh" + ), + ), + /* + "svn"=>array( + "checkout"=>"svn co [url]", + "getrevision"=>"svn info | grep '^Revision' | cut -f 2 -d ':' ", + "built"=>false, + "deleteDirs"=>".svn", + ), + */ + +); + +// ---------------------------------------------------------------------- +// override for local development +// ---------------------------------------------------------------------- + +if ($_SERVER && array_key_exists("SERVER_NAME", $_SERVER)){ + switch ($_SERVER["SERVER_NAME"]) { + case "localhost": + case "dev.ci.iml.unibe.ch": + $aConfig['workDir']="D:\imldeployment"; + $aProjects["scrudu"]["phases"]["preview"]["url"]="http://localhost/deployment/"; + break; + + default: + break; + } +} + +$aConfig=array_merge($aConfig, array( + 'buildDir'=>$aConfig['workDir'].'/build', + 'packageDir'=>$aConfig['workDir'].'/packages', + 'archiveDir'=>$aConfig['workDir'].'/packages/_files', +)); diff --git a/public_html/deployment/gen__htusers.bat b/public_html/deployment/gen__htusers.bat new file mode 100644 index 00000000..282452ca --- /dev/null +++ b/public_html/deployment/gen__htusers.bat @@ -0,0 +1,9 @@ +@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% +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 c687339f..5ad8f098 100644 --- a/public_html/deployment/inc_functions.php +++ b/public_html/deployment/inc_functions.php @@ -1,50 +1,115 @@ -<?php - -global $aParams; -$aParams=array(); -if (count($_GET)) foreach($_GET as $key=>$value) $aParams[$key]=$value; -if (count($_POST)) foreach($_POST as $key=>$value) $aParams[$key]=$value; - -/* -print_r($_GET); -print_r($_POST); -print_r($aParams); -*/ - -/** - * get link as home button - * @return string - */ -function aHome(){ - return '<a href="/deployment/?" class="btn"><i class="icon-home"></i> zur Übersicht</a>'; -} - - -function enterDeployinfos(){ - global $aParams; - $sIdUser="inputUser"; - $sIdComment="inputComment"; - $sUser=(array_key_exists($sIdUser, $aParams))?$aParams[$sIdUser]:""; - $sComment=(array_key_exists($sIdComment, $aParams))?$aParams[$sIdComment]:""; - - $sOut=' - <div class="control-group"> - <label class="control-label" for="inputUser">Benutzername</label> - <div class="controls"> - <input type="text" id="inputUser" name="inputUser" placeholder="Benutzername" value="'.$sUser.'"> - </div> - </div> - <div class="control-group"> - <label class="control-label" for="inputComment">Kommentar zum Deployment</label> - <div class="controls"> - <textarea id= rows="3" id="inputComment" name="inputComment" placeholder="Kommentar">'.$sComment.'</textarea> - </div> - </div> - '; - - return $sOut; -} - - - -?> +<?php + +global $aParams; +$aParams=array(); +if (count($_GET)) foreach($_GET as $key=>$value) $aParams[$key]=$value; +if (count($_POST)) foreach($_POST as $key=>$value) $aParams[$key]=$value; + +/* +print_r($_GET); +print_r($_POST); +print_r($aParams); +*/ + +/** + * get home link as button + * @return string + */ +function aHome(){ + global $aParams; + if (!array_key_exists("prj", $aParams)) return false; + return '<a href="/deployment/?" class="btn"><i class="icon-home"></i> zur Übersicht</a>'; +} +/** + * get project Home link as button + * @return string + */ +function aPrjHome(){ + global $aParams; + if (!array_key_exists("prj", $aParams)) return false; + if (!array_key_exists("action", $aParams)) return false; + + require_once("./config/inc_projects_config.php"); + require_once("./classes/project.class.php"); + $oPrj=new project($aParams["prj"]); + return '<a href="/deployment/'.$aParams["prj"].'/" class="btn"><i class="icon-book"></i> Projekt-Home '.$oPrj->getLabel().' </a>'; +} + +/** + * auto generate upper part of the page with header and navigation + * @global type $aParams + * @return type + */ +function getTopArea(){ + global $aParams; + $sReturn=''; + if (!array_key_exists("prj", $aParams)){ + $sReturn='<h1>Übersicht</h1><span>Alle Projekte und Versionen in den einzelnen Phasen</span>'; + } else { + require_once("./config/inc_projects_config.php"); + require_once("./classes/project.class.php"); + $oPrj=new project($aParams["prj"]); + $sReturn=' '. aHome() . ' ' . aPrjHome().' + <h1>'.$oPrj->getLabel().'</h1><span>'.$oPrj->getDescription().'</div>'; + if (array_key_exists("action", $aParams)){ + $sReturn.='<h2>Aktion: '.$aParams["action"].'</h2>'; + } + } + return '<div id="header2">'.$sReturn.'</div>'; +} + +/** + * 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 + */ +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>'; + +} + +function enterDeployinfos(){ + global $aParams; + $sIdUser="inputUser"; + $sIdComment="inputComment"; + $sUser=(array_key_exists($sIdUser, $aParams))?$aParams[$sIdUser]:""; + $sComment=(array_key_exists($sIdComment, $aParams))?$aParams[$sIdComment]:""; + + $sOut=' + <div class="control-group"> + <label class="control-label" for="inputUser">Benutzername</label> + <div class="controls"> + <input type="text" id="inputUser" name="inputUser" placeholder="Benutzername" value="'.$sUser.'"> + </div> + </div> + <div class="control-group"> + <label class="control-label" for="inputComment">Kommentar zum Deployment</label> + <div class="controls"> + <textarea id= rows="3" id="inputComment" name="inputComment" placeholder="Kommentar">'.$sComment.'</textarea> + </div> + </div> + '; + + return $sOut; +} + + + +?> diff --git a/public_html/deployment/index.php b/public_html/deployment/index.php index 6179e9e3..9c5be12a 100644 --- a/public_html/deployment/index.php +++ b/public_html/deployment/index.php @@ -1,47 +1,55 @@ -<?php - -require_once("./inc_functions.php"); -require_once("./classes/page.class.php"); - - -$sPrj=""; -$sAction="overview"; - -if (array_key_exists("prj", $aParams)) { - $sPrj=$aParams["prj"]; -} -if (array_key_exists("action", $aParams)) { - if (file_exists(__DIR__ . '/act_' . $aParams["action"] . ".php")){ - $sAction=$aParams["action"]; - } -} - -// ------ action - -$sActionFile=__DIR__ . '/act_' . $sAction . ".php"; -include($sActionFile); - - - -// ------ Ausgabe - -$sPhpOut=' - <div id="header"> - IML DEPLOYMENT GUI - </div> - '.$sPhpOut; - -$oPage = new Page(); -$oPage->setOutputtype('html'); -$oPage->addResponseHeader("Pragma: no-cache"); -$oPage->addJsOnReady(' - $(\'.tblOverview\').dataTable({ - "bPaginate": false, - "bLengthChange": false - }); -'); - -$oPage->setContent($sPhpOut); -echo $oPage->render(); - -?> +<?php + +require_once("./inc_functions.php"); +require_once("./classes/page.class.php"); + +$sPrj=""; +$sAction="overview"; + +// ------ check parameters + +if (array_key_exists("prj", $aParams)) { + $sPrj=$aParams["prj"]; +} +if (array_key_exists("action", $aParams)) { + if (file_exists(__DIR__ . '/act_' . $aParams["action"] . ".php")){ + $sAction=$aParams["action"]; + } +} + +// ------ action + +$sActionFile=__DIR__ . '/act_' . $sAction . ".php"; +include($sActionFile); + + +// ------ Ausgabe + +$sPhpOut=' + <div id="header"> + IML DEPLOYMENT GUI + </div> + <br> + '. getTopArea() .' + <div id="content"> + '.$sPhpOut.' + </div> + <div id="footer"> + IML Deployment © '.date("Y").' <a href="http://www.iml.unibe.ch/">Institut für medizinische Lehre; Universität Bern</a> + </div> + '; + +$oPage = new Page(); +$oPage->setOutputtype('html'); +$oPage->addResponseHeader("Pragma: no-cache"); +$oPage->addJsOnReady(' + $(\'.tblOverview\').dataTable({ + "bPaginate": false, + "bLengthChange": false + }); +'); + +$oPage->setContent($sPhpOut); +echo $oPage->render(); + +?> diff --git a/shellscripts/sync_packagedir_to_puppet.php b/shellscripts/sync_packagedir_to_puppet.php new file mode 100644 index 00000000..ab799f9c --- /dev/null +++ b/shellscripts/sync_packagedir_to_puppet.php @@ -0,0 +1,25 @@ +<?php + /** + * + * FOR CRONJOB + * sync archive files to pupet master + * + */ + + require_once("../public_html/deployment/config/inc_projects_config.php"); + + echo "========= SYNC archive files ==========\n\n"; + + if (array_key_exists('mirrorPackages', $aConfig) && count($aConfig['mirrorPackages']) ){ + foreach (array_keys($aConfig["phases"]) as $sPhase){ + echo "===== $sPhase \n"; + foreach($aConfig['mirrorPackages'] as $sLabel=>$sTarget){ + + $sCmd="/usr/bin/rsync --delete -rv ".$aConfig["packageDir"]."/$sPhase/* $sTarget/$sPhase ; \n"; + echo "--- $sLabel\n"; + echo "\$ $sCmd\n"; + $sOut=shell_exec($sCmd); + } + } + } +?> \ No newline at end of file -- GitLab