/[soft]/build_system/web/index.php
ViewVC logotype

Contents of /build_system/web/index.php

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1253 - (show annotations) (download)
Mon May 9 18:36:23 2011 UTC (9 years, 3 months ago) by misc
File size: 13940 byte(s)
nicer message when there is missing deps

1 <?php
2 /**
3 * Mageia build-system quick status report script.
4 *
5 * @copyright Copyright (C) 2011 Mageia.Org
6 *
7 * @author Olivier Blin
8 * @author Pascal Terjan
9 * @author Romain d'Alverny
10 * @author Michael Scherer
11 *
12 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU GPL v2
13 *
14 * This program is free software; you can redistribute it and/or modify it
15 * under the terms of the GNU General Public License aspublished by the
16 * Free Software Foundation; either version 2 of the License, or (at your
17 * option) any later version.
18 *
19 *
20 * Shows submitted packages in the past $max_modified 24 hours and their
21 * status (built & uploaded, failed build, rejected, etc.).
22 *
23 * This was written anew in Jan. 2011 because existing Mandriva build-system
24 * web report code was not clearly licensed at this very time.
25 */
26
27 error_reporting(E_ALL);
28
29 /**
30 * @param array $pkg
31 *
32 * @return string
33 */
34 function pkg_gettype($pkg) {
35 if (array_key_exists("rejected", $pkg["status"]))
36 return "rejected";
37 if (array_key_exists("youri", $pkg["status"])) {
38 if (array_key_exists("src", $pkg["status"]))
39 return "youri";
40 else
41 return "uploaded";
42 }
43 if (array_key_exists("failure", $pkg["status"]))
44 return "failure";
45 if (array_key_exists("done", $pkg["status"]))
46 return "partial";
47 if (array_key_exists("build", $pkg["status"]))
48 return "building";
49 if (array_key_exists("todo", $pkg["status"]))
50 return "todo";
51 return "unknown";
52 }
53
54 /**
55 * @param integer $num
56 *
57 * @return string
58 */
59 function plural($num) {
60 if ($num > 1)
61 return "s";
62 }
63
64 /**
65 * Return timestamp from package key
66 * @param string $key package submission key
67 *
68 * @return integer
69 */
70
71 function key2timestamp($key) {
72 global $tz;
73
74 $date = DateTime::createFromFormat("YmdHis", $key+0, $tz);
75 if ($date <= 0)
76 return null;
77
78 return $date->getTimestamp();
79 }
80
81 function timediff($start, $end) {
82 /**
83 * Return human-readable time difference
84 *
85 * @param integer $start timestamp
86 * @param integer $end timestamp, defaults to now
87 *
88 * @return string
89 */
90 if (is_null($end)) {
91 $end = time();
92 }
93 $diff = $end - $start;
94 if ($diff<60)
95 return $diff . " second" . plural($diff);
96 $diff = round($diff/60);
97 if ($diff<60)
98 return $diff . " minute" . plural($diff);
99 $diff = round($diff/60);
100 if ($diff<24)
101 return $diff . " hour" . plural($diff);
102 $diff = round($diff/24);
103
104 return $diff . " day" . plural($diff);
105 }
106
107 $g_user = isset($_GET['user']) ? htmlentities(strip_tags($_GET['user'])) : null;
108
109 $upload_dir = '/home/schedbot/uploads';
110 $max_modified = 2;
111 $title = '<a href="http://mageia.org/">Mageia</a> build system status';
112 $robots = 'index,nofollow,nosnippet,noarchive';
113 if ($g_user) {
114 $title .= ' for ' . $g_user . "'s packages";
115 $robots = 'no' . $robots;
116 }
117 $tz = new DateTimeZone('UTC');
118 $date_gen = date('c');
119
120 # Temporary until initial mirror is ready
121 chdir("data");
122 $missing_deps = file("missing-deps.i586.txt");
123 #########################################
124
125 chdir($upload_dir);
126
127 $all_files = shell_exec("find \( -name '*.rpm' -o -name '*.src.rpm.info' -o -name '*.youri' -o -name '*.lock' -o -name '*.done' \) -ctime -$max_modified -printf \"%p\t%T@\\n\"");
128 $re = "!^\./(\w+)/((\w+)/(\w+)/(\w+)/(\d+)\.(\w+)\.(\w+)\.(\d+))_?(.*)(\.src\.rpm(?:\.info)?|\.youri|\.lock|\.done)\s+(\d+\.\d+)$!m";
129 $r = preg_match_all($re,
130 $all_files,
131 $matches,
132 PREG_SET_ORDER);
133
134 $pkgs = array();
135
136 $buildtime_total = array();
137 $buid_dates = array();
138
139 foreach ($matches as $val) {
140
141 if ($_GET['user'] && ($_GET['user'] != $val[7])) {
142 continue;
143 }
144 $key = $val[6] . $val[7];
145 if (!is_array($pkgs[$key])) {
146
147 $pkgs[$key] = array(
148 'status' => array(),
149 'path' => $val[2],
150 'version' => $val[3],
151 'media' => $val[4],
152 'section' => $val[5],
153 'user' => $val[7],
154 'host' => $val[8],
155 'job' => $val[9]
156 );
157 }
158 $status = $val[1];
159 $data = $val[10];
160 if (preg_match("/@(\d+):/", $data, $revision)) {
161 $pkgs[$key]['revision'] = $revision[1];
162 }
163 $pkgs[$key]['status'][$status] = 1;
164 $ext = $val[11];
165 if ($ext == '.src.rpm.info') {
166 preg_match("!^(?:@\d+:)?(.*)!", $data, $name);
167 $pkgs[$key]['package'] = $name[1];
168 } else if ($ext == '.src') {
169 $pkgs[$key]['status']['src'] = 1;
170 } else if ($ext == '.youri') {
171 $pkgs[$key]['status']['youri'] = 1;
172 } else if ($ext == '.lock') {
173 preg_match("!.*\.iurt\.(.*)\.\d+\.\d+!", $data, $buildhost);
174 if ($pkgs[$key]['status']['build'])
175 array_push($pkgs[$key]['status']['build'], $buildhost[1]);
176 else
177 $pkgs[$key]['status']['build'] = array($buildhost[1]);
178 } else if ($ext == '.done') {
179 // beware! this block is called twice for a given $key
180
181 $pkgs[$key]['buildtime']['start'] = key2timestamp($val[6]);
182 $pkgs[$key]['buildtime']['end'] = round($val[12]);
183 $pkgs[$key]['buildtime']['diff'] = $pkgs[$key]['buildtime']['end'] - $pkgs[$key]['buildtime']['start'];
184
185 @$build_dates[date('H', $pkgs[$key]['buildtime']['start'])] += 1;
186
187 // keep obviously dubious values out of there
188 // 12 hours is be an acceptable threshold given current BS global perfs
189 // as of April 2011
190 if ($pkgs[$key]['buildtime']['diff'] < 43200) {
191 $buildtime_total[$key] = $pkgs[$key]['buildtime']['diff'];
192 }
193 }
194 }
195 // sort by key in reverse order to have more recent pkgs first
196 krsort($pkgs);
197 ksort($build_dates);
198
199 $build_count = count($buildtime_total);
200 $buildtime_total = array_sum($buildtime_total);
201
202 // count all packages statuses
203 $stats = array(
204 'uploaded' => 0,
205 'failure' => 0,
206 'todo' => 0,
207 'building' => 0,
208 'partial' => 0,
209 'built' => 0,
210 );
211 $total = count($pkgs);
212
213 // count users' packages
214 $users = array();
215
216 if ($total > 0) {
217 foreach ($pkgs as $key => $p) {
218 $pkgs[$key]['type'] = pkg_gettype($p);
219
220 $stats[$pkgs[$key]['type']] += 1;
221
222 if (!array_key_exists($p['user'], $users))
223 $users[$p['user']] = 1;
224 else
225 $users[$p['user']] += 1;
226 }
227 }
228
229 foreach ($stats as $k => $v) {
230 Header("X-BS-Queue-$k: $v");
231 }
232
233 $w = $stats['todo'] - 10;
234 if($w < 0)
235 $w = 0;
236 $w = $w * 60;
237 Header("X-BS-Throttle: $w");
238
239 $buildtime_total = $buildtime_total / 60;
240 header(sprintf('X-BS-Buildtime: %d', round($buildtime_total)));
241 $buildtime_avg = round($buildtime_total / $build_count, 2);
242 header(sprintf('X-BS-Buildtime-Average: %5.2f', $buildtime_avg));
243 ?>
244 <!DOCTYPE html>
245 <html lang="en">
246 <head>
247 <meta charset="utf-8">
248 <title><?php echo strip_tags($title); ?></title>
249 <meta name="robots" content="<?php echo $robots; ?>">
250 <link rel="icon" type="image/png" href="favicon.png" />
251 <style type="text/css">
252 .clear { clear: both; }
253 table {
254 border-spacing: 0;
255 font-family: Helvetica, Verdana, Arial, sans-serif; font-size: 80%;
256 border: 1px solid #ccc;
257 float: left;
258 }
259 table tr { padding: 0; margin: 0; }
260 table th { padding: 0.2em 0.5em; margin: 0; border-bottom: 2px solid #ccc; border-right: 1px solid #ccc; }
261 table td { padding: 0; margin: 0; padding: 0.2em 0.5em; border-bottom: 1px solid #ccc; }
262
263 tr { background: transparent; }
264 tr.uploaded { background: #bbffbb; }
265 tr.failure, tr.rejected { background: #ffbbbb; }
266 tr.todo { background: white; }
267 tr.building { background: #ffff99; }
268 tr.partial { background: #bbbbff; }
269 tr.built { background: #cceeff; }
270 tr.youri { background: #aacc66; }
271
272 td.status-box { width: 1em; height: 1em; }
273 tr.uploaded td.status-box { background: green; }
274 tr.failure td.status-box, tr.rejected td.status-box { background: red; }
275 tr.todo td.status-box { background: white; }
276 tr.building td.status-box { background: yellow; }
277 tr.partial td.status-box { background: blue; }
278 tr.built td.status-box { background: #00ccff; }
279 tr.youri td.status-box { background: olive; }
280
281 #stats { float: right; }
282 #score { margin-bottom: 2em; font-family: Helvetica, Verdana, Arial, sans-serif; }
283 #score-box { width: 100px; height: 100px; background: #faa; }
284 #score-meter { width: 100px; background: #afa; }
285 </style>
286 </head>
287 <body>
288 <h1><?php echo $title ?></h1>
289
290 <?php
291 if (!is_null($g_user))
292 echo '<a href="/">&laquo;&nbsp;Back to full list</a>';
293
294 # Temporary until initial mirror is ready
295 echo sprintf(
296 '<p><a href="%s">%s broken dependencies</a>. <strong><a href="%s">You can help!</a></strong></p>',
297 'data/missing-deps.i586.txt', count($missing_deps) == 0 ? 'no' : count($missing_deps),
298 'http://www.mageia.org/wiki/doku.php?id=packaging#starting_package_import'
299 );
300
301 #########################################
302
303 $buildtime_stats = array();
304
305 $s = '';
306 $tmpl = <<<T
307 <tr class="%s">
308 <td>%s</td>
309 <td><a href="?user=%s">%s</a></td>
310 <td><a href="http://svnweb.mageia.org/packages?view=revision&revision=%d" title="%s">%s</a></td>
311 <td>%s</td>
312 <td>%s/%s</td>
313 <td class="status-box"></td>
314 T;
315
316 if ($total > 0) {
317 foreach ($pkgs as $key => $p) {
318 $s .= sprintf($tmpl,
319 $p['type'],
320 timediff(key2timestamp($key)) . ' ago',
321 $p['user'], $p['user'],
322 $p['revision'],
323 addslashes($p['summary']),
324 $p['package'],
325 $p['version'],
326 $p['media'], $p['section']
327 );
328
329 $typelink = '';
330 if ($p['type'] == 'failure') {
331 $typelink = '/uploads/' . $p['type'] . '/' . $p['path'];
332 } elseif ($p['type'] == 'rejected') {
333 $typelink = '/uploads/' . $p['type'] . '/' . $p['path'] . '.youri';
334 }
335 $typestr = $p['type'];
336 if ($p['status']['build']) {
337 $typealt = 'Building on';
338 foreach ($p['status']['build'] as $h)
339 $typealt .= " $h";
340 $typestr = "<span title='$typealt'>$typestr</a>";
341 }
342
343 $s .= '<td>';
344 $s .= ($typelink != '') ?
345 sprintf('<a href="%s">%s</a>', $typelink, $typestr) :
346 $typestr;
347
348 $s .= '</td><td>';
349 if ($p['type'] == 'uploaded') {
350 $tdiff = timediff($p['buildtime']['start'], $p['buildtime']['end']); // use $p['buildtime']['diff']; instead?
351 $s .= $tdiff;
352 @$buildtime_stats[$tdiff] += 1;
353 }
354 $s .= '</td>';
355 $s .= '</tr>';
356 }
357 // Table
358 echo '<table>',
359 '<caption>', $total, ' packages submitted in the past ', $max_modified * 24, '&nbsp;hours.</caption>',
360 '<tr><th>Submitted</th><th>User</th>
361 <th>Package</th><th>Target</th><th>Media</th>
362 <th colspan="2">Status</th><th>Build time</th></tr>',
363 $s,
364 '</table>';
365
366 // Stats
367 $s = '<div id="stats">';
368 $score = round($stats['uploaded']/$total * 100);
369 $s .= sprintf('<div id="score"><h3>Score: %d/100</h3>
370 <div id="score-box"><div id="score-meter" style="height: %dpx;"></div></div></div>',
371 $score, $score);
372
373 $s .= '<table style="width: 100%"><caption>Stats.</caption><tr><th colspan="2">Status</th><th>Count</th><th>%</th></tr>';
374 foreach ($stats as $k => $v) {
375 $s .= sprintf('<tr class="%s"><td class="status-box"></td><td>%s</td><td>%d</td><td>%d%%</td></tr>',
376 $k, $k, $v, round($v/$total*100));
377 }
378
379 $s .= '</table><br /><br />';
380
381 $s .= '<table style="width: 100%"><caption>Packagers</caption><tr><th>User</th><th>Packages</th></tr>';
382 arsort($users);
383 foreach ($users as $k => $v)
384 $s .= sprintf('<tr><td><a href="/?user=%s">%s</a></td><td>%d</td></tr>',
385 $k, $k, $v);
386
387 $s .= '</table><br /><br />';
388
389 /**
390 */
391 function timesort($a, $b)
392 {
393 $a = explode(' ', trim($a));
394 $b = explode(' ', trim($b));
395
396 if ($a[1] == 'hour' || $a[1] == 'hours')
397 $a[0] *= 3600;
398
399 if ($b[1] == 'hour' || $a[1] == 'hours')
400 $b[0] *= 3600;
401
402 if ($a[0] > $b[0])
403 return 1;
404 elseif ($a[0] < $b[0])
405 return -1;
406
407 return 0;
408 }
409 uksort($buildtime_stats, "timesort");
410
411 $bts = '';
412 $max = max($buildtime_stats);
413 foreach ($buildtime_stats as $time => $count) {
414 $bts .= sprintf('<tr><td>%s</td><td><span style="width: %dpx; height: 10px; background: #aaa; display: block;" title="%d"></span></td></tr>',
415 $time,
416 round($count/$max*100),
417 $count);
418
419 $tmp = explode(' ', $time);
420 }
421
422 $s .= '<table style="width: 100%;"><caption>Build time</caption>';
423
424 $s .= sprintf('<tr><td>Total time</td><td>%s hours</td></tr>
425 <tr><td>Average</td><td>%s minutes</td></tr>
426 <tr><td>Builds count</td><td>%s</td></tr>',
427 round($buildtime_total / 60, 2),
428 $buildtime_avg,
429 $buildtime_cnt);
430
431 $s .= '<tr><th title="Build time">Duration</th><th title="Packages number">Pack. nb.</th></tr>';
432 $s .= $bts;
433 $s .= '</table><span style="font-size: 85%;">Does not take<br />build failures<br />into account.</span>';
434
435 $s .= '<table><caption>Build times</caption>';
436 $max = max($build_dates);
437 foreach ($build_dates as $time => $count)
438 $s .= sprintf('<tr><td>%d</td><td><span style="width: %dpx; height: 10px; background: #aaa; display: block;" title="%d"></span></td></tr>',
439 $time,
440 round($count / $max * 100),
441 $count);
442 $s .= '</table>';
443
444 $s .= '</div>';
445
446 echo $s;
447 }
448 else
449 {
450 echo sprintf('<p>No package has been submitted in the past %d&nbsp;hours.</p>',
451 $max_modified * 24);
452 }
453
454 ?>
455 <div class="clear"></div>
456 <hr />
457 <p>Generated at <?php echo $date_gen; ?>.</p>
458 </body>
459 </html>

  ViewVC Help
Powered by ViewVC 1.1.28