<?php define("MICOBU_BASE", "/opt/micobu/rootfs/home/builduser/artefacts"); define("MICOBU_BASE_LINK", "./artefacts"); class micobu_build { public $hash; public $result; public $time_start; public $time_stop; public $project; } class micobu_project { public $name; public $builds; } function glob_sorted_mtime($dir) { $ar = []; array_multisort(array_map('filemtime', ($files = glob("$dir/*", GLOB_ONLYDIR))), SORT_DESC, $files); foreach($files as $filename) { $ar[] = $filename; } return $ar; } function scan_build(&$project, $hash) { $build = new micobu_build(); $build->hash = $hash; $build->result = -1; $build->project = $project; $file = MICOBU_BASE . "/". $project->name . "/" . $hash . "/meta.json"; if (file_exists($file)) { $json = json_decode(file_get_contents($file)); $build->hash = $json->hash; $build->result = $json->result; $build->time_start = $json->time_start; $build->time_stop = $json->time_stop; $build->description = $json->description; } return $build; } function scan_project($name) { $project = new micobu_project(); $project->name = $name; $project->builds = array(); $dirs = glob_sorted_mtime(MICOBU_BASE . "/" . $name); foreach ($dirs as $dir) { $hash = basename($dir); $project->builds += array($hash => scan_build($project, $hash)); } return $project; } function micobu_scan() { $projects = array(); foreach (glob(MICOBU_BASE . "/*", GLOB_ONLYDIR) as $project_path) { $project_name = basename($project_path); $projects += array($project_name => scan_project($project_name)); } return $projects; } function micobu_get_trend($project) { foreach ($project->builds as $build) { if ($build->result > 0) return "<span title='failed'>❌</span>"; else return "<span title='success'>✅</span>"; } return "⌛"; return "🔼"; return "❗"; } function micobu_get_build_icon($build, $verbose = false) { $verb = ($verbose ? ($build->result > 0 ? " Build failed with return code " . $build->result : " Build succeeded") : ""); if ($build->result > 0) return "<span title='Build failed with return code $build->result'>❌</span>$verb"; else return "<span title='Build succeeded'>✅</span>"; } function micobu_get_build_time($build) { return gmdate("i\m s\s", $build->time_stop - $build->time_start); } function micobu_get_weblink($build, $filename) { return MICOBU_BASE_LINK . "/" . $build->project->name . "/" . $build->hash . "/" . $filename; } function micobu_get_dl_buttons($build) { return "<a title='Show log file' href='" . micobu_get_weblink($build, "log.txt") . "'>📰</a> <a title='Show meta info' href='" . micobu_get_weblink($build, "meta.json") . "'>🛈</a> <a title='Show artefacts' href='" . micobu_get_weblink($build, "artefacts.tar.xz") . "'>📂</a>"; } $projects = micobu_scan(); if (isset($_GET['p'])) { $selected_project = $_GET['p']; preg_match("/^[_\-A-Za-z0-9]{1,20}$/", $selected_project, $matches); if (!$matches) die("Invalid request."); if (!isset($projects[$selected_project])) die("Invalid project."); if (isset($_GET['h'])) { $selected_hash = $_GET['h']; preg_match("/^[_\-A-Za-z0-9]{1,40}$/", $selected_hash, $matches); if (!$matches) die("Invalid request."); if (!isset($projects[$selected_project]->builds[$selected_hash])) die("Invalid hash."); } } ?> <!DOCTYPE html> <html> <!-- Hi there, curious person! This is the source code of Micobu's web dashboard. You can find more information on this system on: https://git.notsyncing.net/markus/micobu --> <head> <style> body { font-family: sans-serif; background-color: #111; color: #CCC; } table { background-color: #222; border-radius: 8px; font-size: 20pt; border: 1px solid black; width: 100%; } tr:hover { background-color: #555; } td { border: 1px dotted black; } a { color: inherit; text-decoration: none; } .footer { margin: 8px; color: #666; text-align: right; } .logframe { width: 98vw; margin-left: auto; margin-right: auto; } .log { font-size: 14pt; font-family: monospace; white-space: pre; overflow-x: scroll; } .tdnohighlight:hover { background-color: #222; } </style> <script> function escapeHtml(text) { var map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.replace(/[&<>"']/g, function(m) { return map[m]; }); } </script> </head> <body> <?php $dpath = (isset($selected_project) ? " / <a href='?p=$selected_project'>$selected_project</a>" : ""); $dpath .= (isset($selected_hash) ? " / $selected_hash" : ""); echo "<h1><a href='?'>Micobu CI Dashboard</a>$dpath</h1>"; ?> <div> <?php if (isset($selected_hash)) { //var_dump($projects[$selected_project]->builds[$selected_hash]); $build = $projects[$selected_project]->builds[$selected_hash]; echo "<table>"; echo "<tr><td>Build status</td><td>" . micobu_get_build_icon($build, true). "</td></tr>"; echo "<tr><td>Build time</td><td>" . micobu_get_build_time($build) . "</td></tr>"; echo "<tr><td>Commit info</td><td>" . $build->hash . " (" . $build->description . ")</td></tr>"; echo "<tr><td>Download</td><td>" . micobu_get_dl_buttons($build) . "</td></tr>"; ?> <tr><td class="tdnohighlight" colspan=2><hr></td></tr> <tr><td class="tdnohighlight" colspan=2><div class='logframe'><div class='log' id='buildlog'>Loading log...</div></div></td></tr> <script> var xhttp_ps = new XMLHttpRequest(); xhttp_ps.onreadystatechange = function() { if (this.readyState == 4) document.getElementById("buildlog").innerHTML = (this.status == 200 ? escapeHtml(xhttp_ps.responseText) : "Error loading log."); }; xhttp_ps.open("GET", "<?php echo MICOBU_BASE_LINK . "/$selected_project/$selected_hash/log.txt"; ?>", true); xhttp_ps.send(); </script> <?php echo "</table>"; } else if (isset($selected_project)) { echo "<table>"; foreach ($projects[$selected_project]->builds as $hash => $build) { echo "<tr><td><a href='?p=$selected_project&h=$hash'>" . date(DATE_RFC2822, $build->time_start) . "</a></td><td>" . micobu_get_build_icon($build) . "</td><td><a title='$build->hash'href='?p=$selected_project&h=$hash'>" . $build->description . "</a></td><td>" . micobu_get_dl_buttons($build) . "</td></tr>"; } echo "</table>"; } else { echo "<table>"; foreach ($projects as $project) { echo "<tr><td><a href='?p=" . $project->name ."'>" . $project->name . "</a></td><td>" . micobu_get_trend($project) . "</td></tr>"; } echo "</table>"; } ?> </div> <div class="footer"> <?php echo "Processed " . count($projects) . " repositories in " . round((microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"]) * 1000, 1) . " ms."; ?> </div>