diff options
author | Eudyptula <eitan@mosenkis.net> | 2009-07-20 16:13:47 -0400 |
---|---|---|
committer | Eudyptula <eitan@mosenkis.net> | 2009-07-20 16:13:47 -0400 |
commit | 6f995b43606448b7f615353a542a08e23613919b (patch) | |
tree | 6c8dcd8637124ffd83594f35b7c352a402b77128 | |
parent | Added metadata back to log viewer (diff) | |
download | ingenue-6f995b43606448b7f615353a542a08e23613919b.tar.gz ingenue-6f995b43606448b7f615353a542a08e23613919b.tar.bz2 ingenue-6f995b43606448b7f615353a542a08e23613919b.zip |
Converted backend status to TINYINT; First try at logging non-execution messages; Added download history page; Only show links from builds that the user has perms to access
-rwxr-xr-x | backend/backend.php | 15 | ||||
-rw-r--r-- | backend/functions/color.php | 16 | ||||
-rw-r--r-- | backend/functions/execution.php | 67 | ||||
-rw-r--r-- | backend/functions/log.php | 24 | ||||
-rw-r--r-- | backend/functions/signals.php | 2 | ||||
-rw-r--r-- | frontend/include/header.php | 4 | ||||
-rw-r--r-- | frontend/pages/builds/download.php | 50 | ||||
-rw-r--r-- | frontend/pages/builds/history.php | 27 | ||||
-rw-r--r-- | frontend/pages/downloadimage.php | 49 | ||||
-rw-r--r-- | frontend/routing.csv | 3 | ||||
-rw-r--r-- | shared/classes/build.php | 80 | ||||
-rw-r--r-- | shared/classes/configuration.php | 2 | ||||
-rw-r--r-- | shared/classes/gentoo_profile.php | 12 | ||||
-rw-r--r-- | shared/classes/task.php | 10 | ||||
-rw-r--r-- | todo | 3 |
15 files changed, 224 insertions, 140 deletions
diff --git a/backend/backend.php b/backend/backend.php index 1f05d6f..0750d49 100755 --- a/backend/backend.php +++ b/backend/backend.php @@ -50,11 +50,11 @@ require_once(SHARED.'/include/dbinit.php'); while (true) { // TODO check first for builds that need to be resumed (and figure out how to resume things) $S['pdo']->query('LOCK TABLES `builds` WRITE'); - $r=$S['pdo']->query('SELECT * FROM `builds` WHERE `status`="build/ready" ORDER BY `ctime` ASC LIMIT 1'); + $r=$S['pdo']->query('SELECT * FROM `builds` WHERE `status`=-128 ORDER BY `ctime` ASC LIMIT 1'); if ($r->rowCount()) { $build=new sql_build($r->fetch(PDO::FETCH_ASSOC)); $build->start=time(); - $build->status='build/running'; + $build->status=-1; $build->write(); $S['pdo']->query('UNLOCK TABLES'); log_msg('Starting build id='.$build->id); @@ -79,8 +79,9 @@ while (true) { execute_command('Delete work directory', 'rm -rf "'.$workdir.'"'); } } catch (Exception $e) { + end_internal_task(1); log_msg('Caught exception: '.$e->getMessage()); - $build->status='finished/failed: '.$e->getMessage(); + $build->status=126; $owner=$build->get_owner(); xhtmlemail('"'.$owner->name.'" <'.$owner->email.'>', null, $conf['title'].' build failed', 'Your build has failed. You can find more information at <a href="'.url('logs/'.$build->id).'">'.url('logs/'.$build->id).'</a>'); } @@ -89,7 +90,7 @@ while (true) { if (isset($file)) { log_msg("Completed build successfully"); if ($conf['split_setup']) { - $build->status='finished/uploading'; + $build->status=-127; $build->write(); $key=randstring(30); $opt=new sql_buildopt($build->id, 'uploadkey', $key); @@ -107,14 +108,14 @@ while (true) { debug($result); } if ($result === false || strpos($result, 'Upload successful') === false) { - $build->status='finished/failed: upload failed'.($result === false?' - '.curl_error($c):''); + $build->status=127; } else { debug("Transferred $file... unlinking it"); unlink($file); - $build->status='finished/success'; + $build->status=0; } } else { - $build->status='finished/success'; + $build->status=0; $base=basename($file); $ext=substr($base, strpos($base, '.')); rename($file, COMPLETED.'/build-'.$build->id.$ext); diff --git a/backend/functions/color.php b/backend/functions/color.php new file mode 100644 index 0000000..cd4910c --- /dev/null +++ b/backend/functions/color.php @@ -0,0 +1,16 @@ +<?php +function color($msg, $color) { + $i=30; + $colors=array( + 'black' => $i++, + 'red' => $i++, + 'green' => $i++, + 'yellow' => $i++, + 'blue' => $i++, + 'magenta' => $i++, + 'cyan' => $i++, + 'white' => $i++ + ); + return "\033[".$colors[$color]."m$msg\033[0m"; +} +?> diff --git a/backend/functions/execution.php b/backend/functions/execution.php index dd6dd88..773e6ee 100644 --- a/backend/functions/execution.php +++ b/backend/functions/execution.php @@ -1,18 +1,25 @@ <?php +function task_get_order() { + global $build; + static $buildid=null; + static $order=null; + if ($build->id === $buildid) { + $order++; + } else { + $buildid=$build->id; + $order=0; + } + return $order; +} function execute_command_with_all($description, $command, $fatal=true, $path=null, $env=null) { global $build, $task; + if (isset($task)) + end_internal_task(); $default_env=array( 'PATH' => $_ENV['PATH'] ); $env=is_array($env)?array_merge($default_env, $env):$default_env; - // TODO this won't work once we have internal tasks too - we need to use a common function for tracking order - static $buildid=null; - static $order=0; - if ($build->id !== $buildid) { - $buildid=$build->id; - $order=0; - } - $task=new sql_task($build->id, $order++, null, $description, $command); + $task=new sql_task($build->id, task_get_order(), 'exec', $description, $command); $result=$task->execute($path, $env); unset($task); if ($result != 0 && $fatal) { @@ -37,4 +44,48 @@ function execute_command_with_path($desc, $cmd, $path) { function execute_non_fatal_command($desc, $cmd, $path=null, $env=null) { return execute_command_with_all($desc, $cmd, false, $path, $env); } +function start_internal_task($desc) { + global $build, $task; + if (isset($task)) + end_internal_task(); + debug($desc); + $task=new sql_task($build->id, task_get_order(), 'internal', $desc); + $task->start=time(); + $task->write(); +} +function end_internal_task($status=0) { + global $task; + if (isset($task)) { + $task->finish=time(); + $task->exit=$status; + $task->write(); + unset($task); + } +} +function log_msg($msg, $nl=true) { + global $build, $task; + static $order, $buildid, $taskorder; + if (!isset($task)) { + start_internal_task($msg); + return; + } + $msg.=$nl?"\n":''; + debug($msg); + if ($buildid === $build->id && $taskorder === $task->order) { + $order++; + } else { + $buildid=$build->id; + $taskorder=$task->order; + $order=0; + } + $entry=new sql_buildlog_entry($build->id, $task->order, $order, time(), 'system', $msg); + $entry->write(); +} +function log_status($msg, $cmd) { + start_internal_task($msg); + $status=is_bool($cmd)?$cmd:eval((strpos($cmd, 'return') === false?'return ':'').$cmd); + end_internal_task($status?0:1); + debug("... ".($status?color('[success]', 'green'):color('[failure]', 'red'))); + return $status; +} ?> diff --git a/backend/functions/log.php b/backend/functions/log.php deleted file mode 100644 index 251e526..0000000 --- a/backend/functions/log.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -// In the future, this will log to the database and require a build ID, but for now it just prints -function log_msg($msg, $nl=true) { // This is a quick hack, we need to do something smarter - debug($msg.($nl?"\n":'')); -} -function log_status($msg, $result) { - log_msg($msg."... ".($result?color('[success]', 'green'):color('[failure]', 'red'))); - return $result; -} -function color($msg, $color) { - $i=30; - $colors=array( - 'black' => $i++, - 'red' => $i++, - 'green' => $i++, - 'yellow' => $i++, - 'blue' => $i++, - 'magenta' => $i++, - 'cyan' => $i++, - 'white' => $i++ - ); - return "\033[".$colors[$color]."m$msg\033[0m"; -} -?> diff --git a/backend/functions/signals.php b/backend/functions/signals.php index effea2f..2a895e5 100644 --- a/backend/functions/signals.php +++ b/backend/functions/signals.php @@ -13,7 +13,7 @@ function handle_signal($sig=null) { debug('$task not set'); } $build->finish=time(); - $build->status='finished/failed: got signal '.$sig; + $build->status=$sig; $build->write(); debug("build $build->id given status $build->status"); } diff --git a/frontend/include/header.php b/frontend/include/header.php index d4f9bea..c11c480 100644 --- a/frontend/include/header.php +++ b/frontend/include/header.php @@ -35,9 +35,9 @@ if (isset($S['head'])) { <h1><a href="<?php echo url(); ?>"><img src="<?php echo url('images/ingenue.png'); ?>" alt="Ingenue" /></a></h1> <ul> <?php -echo '<li><a href="'.url().'">Home</a></li>'; +//echo '<li><a href="'.url().'">Home</a></li>'; echo '<li><a href="'.url('create').'">New configuration</a></li>'; -echo '<li><a href="'.url('configurations').'">Manage configurations</a></li>'; +echo '<li><a href="'.url('configurations').'">My configurations</a></li>'; echo '<li><a href="'.url('builds').'">My builds</a></li>'; if (isset($S['user'])) { if ($conf['invite'] && ($S['user']->has_flag('a') || $conf['invite'] != 'admin')) diff --git a/frontend/pages/builds/download.php b/frontend/pages/builds/download.php new file mode 100644 index 0000000..8ce338c --- /dev/null +++ b/frontend/pages/builds/download.php @@ -0,0 +1,50 @@ +<?php +function init_builds_download() { + global $S, $request; + if (!isset($S['user'])) { + return 'login'; + } + if (!(isset($request['build']) && strlen($request['build']) == 6 && ctype_alnum($request['build']))) { + debug('builds_download', 'No build or badly formatted build requested'); + return '404'; + } + $r=$S['pdo']->query('SELECT * FROM `builds` WHERE `id`="'.$request['build'].'"'); + if ($r->rowCount() == 0) { + debug('builds_download', 'build not found or not owned by user'); + return '404'; + } + $build=new sql_build($r->fetch(PDO::FETCH_ASSOC)); + if (!owner_or_admin($build->owner)) { + debug('builds_download', 'Permission denied'); + return '404'; + } + $files=glob(COMPLETED.'/build-'.$build->id.'.*'); + if (count($files)) { + if (count($files) > 1) { + debug('builds_download', 'extraneous file(s) found - don\'t know which to give them'); + return '404'; + } + $S['builds_download']['file']=$files[0]; + if (!is_readable($S['builds_download']['file'])) { + debug('builds_download', 'found file, but no read perms'); + return '404'; + } + $ext=substr($S['builds_download']['file'], strpos($S['builds_download']['file'], '.')); + } else { + debug('builds_download', 'image file not found'); + return '404'; + } + $S['builds_download']['dl']=new sql_download($build->id, $S['user']->id, time()); + contenttype('application/octet-stream'); + header('Content-Length: '.filesize($S['builds_download']['file'])); + header('Content-Description: File Transfer'); + header('Content-Transfer-Encoding: binary'); + header('Content-Disposition: attachment; filename="'.(isset($build->name) && strlen($build->name)?str_replace('"', '\"', $build->name):'ingenue-'.$build->id).$ext); +} +function body_builds_download() { + global $S; + readfile($S['file']); + // Log the download to db after sending data so hopefully HEAD requests won't artificially inflate the # of dls + $S['builds_download']['dl']->write(); +} +?> diff --git a/frontend/pages/builds/history.php b/frontend/pages/builds/history.php new file mode 100644 index 0000000..0ddcbff --- /dev/null +++ b/frontend/pages/builds/history.php @@ -0,0 +1,27 @@ +<?php +function init_builds_history() { + global $S, $request; + if (!isset($S['user'])) return 'login'; + if (!(isset($request['build']) && strlen($request['build']) == 6 && ctype_alnum($request['build']))) { + return '404'; + } + $r=$S['pdo']->query('SELECT * FROM `builds` WHERE `id`="'.$request['build'].'"'); + if (!$r->rowCount()) return '404'; + $S['builds_history']['build']=new sql_build($r->fetch(PDO::FETCH_ASSOC)); + if (!owner_or_admin($S['builds_history']['build']->id)) { + return '404'; + } + return array('title' => 'Download History'); +} +function body_builds_history() { + global $S; + $build=&$S['builds_history']['build']; + echo $build->display(); + $r=$S['pdo']->query('SELECT * FROM `downloads` WHERE `build`="'.$build->id.'" ORDER BY `time` DESC'); + while ($download=$r->fetch(PDO::FETCH_ASSOC)) { + $download=new sql_download($download); + $user=$download->get_user(); + echo '<p>Downloaded <code>'.date('D j M Y G:i:s T', $download->time).'</code> by <b>'.$user->name.'</b></p>'."\n"; + } +} +?> diff --git a/frontend/pages/downloadimage.php b/frontend/pages/downloadimage.php deleted file mode 100644 index 2234a10..0000000 --- a/frontend/pages/downloadimage.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php -function init_downloadimage() { - global $S, $request; - if (!isset($S['user'])) { - return 'login'; - } - if (!(isset($request['build']) && strlen($request['build']) == 6 && ctype_alnum($request['build']))) { - debug('downlaodimage', 'No build or badly formatted build requested'); - return '404'; - } - $r=$S['pdo']->query('SELECT * FROM `builds` WHERE `id`="'.$request['build'].'"'); - if ($r->rowCount() == 0) { - debug('downloadimage', 'build not found or not owned by user'); - return '404'; - } - $build=new sql_build($r->fetch(PDO::FETCH_ASSOC)); - if (!owner_or_admin($build->owner)) { - debug('downloadimage', 'Permission denied'); - return '404'; - } - $files=glob(COMPLETED.'/build-'.$build->id.'.*'); - if (count($files)) { - if (count($files) > 1) { - debug('downloadimage', 'extraneous file(s) found - don\'t know which to give them'); - return '404'; - } - $S['file']=$files[0]; - if (!is_readable($S['file'])) { - debug('downloadimage', 'found file, but no read perms'); - return '404'; - } - $ext=substr($S['file'], strpos($S['file'], '.')); - } else { - debug('downloadimage', 'image file not found'); - return '404'; - } - $dl=new sql_download($build->id, $S['user']->id, time()); - $dl->write(); - contenttype('application/octet-stream'); - header('Content-Length: '.filesize($S['file'])); - header('Content-Description: File Transfer'); - header('Content-Transfer-Encoding: binary'); - header('Content-Disposition: attachment; filename="'.(isset($build->name) && strlen($build->name)?str_replace('"', '\"', $build->name):'ingenue-'.$build->id).$ext); -} -function body_downloadimage() { - global $S; - readfile($S['file']); -} -?> diff --git a/frontend/routing.csv b/frontend/routing.csv index 7312364..3a93c19 100644 --- a/frontend/routing.csv +++ b/frontend/routing.csv @@ -24,7 +24,8 @@ ^config/([a-zA-Z0-9]{6})/status$ configurations/status configuration ^configurations$ configurations/manager # Download finished image -^download/([a-zA-Z0-9]{6})$ downloadimage build +^download/([a-zA-Z0-9]{6})$ builds/download build +^download/([a-zA-Z0-9]{6})/history$ builds/history build # Session ^login$ login ^login/(.+)$ login go diff --git a/shared/classes/build.php b/shared/classes/build.php index 8212d56..d14deb0 100644 --- a/shared/classes/build.php +++ b/shared/classes/build.php @@ -26,10 +26,9 @@ class sql_build extends conf_build_common { 'default' => '' ), 'status' => array ( - 'type' => 'VARCHAR', - 'length' => 255, - 'not_null' => true, - 'default' => '' + 'type' => 'TINYINT', + 'length' => 4, + 'not_null' => true ), 'ctime' => array ( 'type' => 'INT', @@ -51,39 +50,47 @@ class sql_build extends conf_build_common { function display() { global $S; $format='D j M Y G:i:s T'; + $OoA=owner_or_admin($this->id); $html='<div class="build"><span class="name">'.(isset($this->name) && strlen($this->name)?htmlentities($this->name):'Unnamed Build').'</span> '; - $status=explode('/', $this->status, 2); - if ($status[0] == 'config') { - $status[1]=substr($status[1], strpos($status[1], 'p')+1); - $html.='<span class="status config">[Configuration step '.$status[1].']</span><br/><span class="links"><a href="'.url('create/'.$this->id).'">Continue configuring</a></span>'; - } elseif ($status[0] == 'build') { - if ($status[1] == 'ready') { - $total=$S['pdo']->query('SELECT COUNT(*) FROM `builds` WHERE `status`="build/ready"')->fetch(PDO::FETCH_COLUMN); - $num=$S['pdo']->query('SELECT COUNT(*) FROM `builds` WHERE `status`="build/ready" AND `ctime` <= '.$this->ctime)->fetch(PDO::FETCH_COLUMN); - $html.="<span class=\"status queued\">[Queued ($num/$total)]</span>"; - } elseif ($status[1]='running') { - // Add link to regular log viewer? - // Build stage X - $html.='<span class="status building">[building]</span><br/><span class="links"><a href="'.url('logs/'.$this->id.'/live').'">Watch</a> • <a href="'.url('logs/'.$this->id).'"> Build log</a></span>'; - } else { - throw_exception('Unrecognized build status '.$this->status); + $links=array(); + if ($this->status == -128) { + $total=$S['pdo']->query('SELECT COUNT(*) FROM `builds` WHERE `status`=-128')->fetch(PDO::FETCH_COLUMN); + $num=$S['pdo']->query('SELECT COUNT(*) FROM `builds` WHERE `status`=-128 AND `ctime` <= '.$this->ctime)->fetch(PDO::FETCH_COLUMN); + $html.="<span class=\"status queued\">[Queued ($num/$total)]</span>"; + } elseif ($this->status == -127) { + $html.='<span class="status successful">[uploading]</span>'; + if ($OoA) $links['Build log']=url("logs/$this->id"); + } elseif ($this->status < 0) { + // TODO Build stage X + $html.='<span class="status building">[building]</span>'; + if ($OoA) { + //$links['Watch']=url("logs/$this->id/live"); + $links['Build Log']=url("logs/$this->id"); } - } elseif ($status[0] == 'finished') { - $status=explode(': ', $status[1], 2); - if ($status[0] == 'success') { - $r=$S['pdo']->query('SELECT COUNT(*) as `count`, MAX(`time`) as `time` FROM `downloads` WHERE `build`="'.$this->id.'"'); - $r=$r->fetch(PDO::FETCH_ASSOC); - $d=$r['count'].' download'.($r['count'] != 1?'s':'').($r['count']?'<br/><span class="time">(last at '.date($format, $r['time']).')</span>':''); - $html.='<span class="downloads">'.$d.'</span><span class="status successful">[successful]</span><br/><span class="links"><a href="'.url('download/'.$this->id).'">Download image</a> • <a href="'.url('logs/'.$this->id).'">Build log</a></span>'; - } elseif ($status[0] == 'failed') { - $html.='<span class="status failed">[failed: '.htmlentities($status[1]).']</span><br/><span class="links"><a href="'.url('logs/'.$this->id.'/failure').'">View output of failed command</a> • <a href="'.url('logs/'.$this->id).'">Build log</a></span>'; - } elseif ($status[0] == 'uploading') { - $html.='<span class="status successful">[uploading]</span><br/><span class="links"><a href="'.url('logs/'.$this->id).'">Build log</a></span>'; - } else { - throw_exception('Unrecognized build status '.$this->status); + } elseif ($this->status == 0) { + $r=$S['pdo']->query('SELECT COUNT(*) as `count`, MAX(`time`) as `time` FROM `downloads` WHERE `build`="'.$this->id.'"')->fetch(PDO::FETCH_ASSOC); + $d=($OoA && $r['count']?'<a href="'.url("download/$this->id/history").'">':'').$r['count'].' download'.($r['count'] != 1?'s':'').($r['count']?($OoA?'</a>':'').'<br/><span class="time">(last at '.date($format, $r['time']).')</span>':''); + $html.='<span class="downloads">'.$d.'</span><span class="status successful">[successful]</span>'; + $links['Download image']=url("download/$this->id"); + if ($OoA) $links['Build log']=url("logs/$this->id"); + } elseif ($this->status == 127) { + $html.='<span class="status failed">[upload failed]</span>'; + if ($OoA) $links['Build log']=url("logs/$this->id"); + } elseif ($this->status == 126) { + $html.='<span class="status failed">[failed]</span>'; + if ($OoA) { + //$links['View output of failed command']=url("logs/$this->id/failure"); + $links['Build log']=url("logs/$this->id"); } } else { - throw_exception('Unrecognized build status '.$this->status); + $html.='<span class="status failed">[failed: got signal '.$this->status.']</span>'; + if ($OoA) $links['Build log']=url('logs/'.$this->id); + } + if ($links) { + foreach ($links as $label => $url) { + $links[$label]='<a href="'.$url.'">'.htmlentities($label).'</a>'; + } + $html.='<br/><span class="links">'.implode(' • ', $links).'</span>'; } if (isset($this->ctime)) { $html.='<div class="time">Submitted for build at: <span class="time">'.date($format, $this->ctime).'</span><br/>'; @@ -102,5 +109,12 @@ class sql_build extends conf_build_common { $html.='</div>'; return $html; } + function queued_tasks() { + global $S; + static $cache; + if (!isset($cache)) + $cache=$S['pdo']->query('SELECT COUNT(`order`) FROM `tasks` WHERE `start` IS NULL AND `build`="'.$this->id.'"')->fetch(PDO::FETCH_COLUMN); + return $cache; + } } ?> diff --git a/shared/classes/configuration.php b/shared/classes/configuration.php index 157610b..e315047 100644 --- a/shared/classes/configuration.php +++ b/shared/classes/configuration.php @@ -53,7 +53,7 @@ class sql_configuration extends conf_build_common { $opt->write(); } $build->ctime=time(); - $build->status='build/ready'; + $build->status=1; $build->write(); return $build; } diff --git a/shared/classes/gentoo_profile.php b/shared/classes/gentoo_profile.php index 5657ed1..0cf071d 100644 --- a/shared/classes/gentoo_profile.php +++ b/shared/classes/gentoo_profile.php @@ -138,16 +138,17 @@ class sql_gentoo_profile extends sql_row_obj { $file=realpath(BACKEND.'/../gentoo_pkgsets.csv'); if (!is_readable($file)) return false; $file=fopen($file, 'r'); + if ($update) $exists=array(); while (!feof($file)) { $add=array(); - $pkgs=explode("\t", rtrim(fgets($file))); + $pkgs=explode("\t", trim(fgets($file))); + if (substr($pkgs[0], 0, 1) == '#') continue; $name=array_shift($pkgs); $obj=new sql_gentoo_pkgset(); if ($update) { $r=$S['pdo']->query('SELECT * FROM `gentoo_pkgsets` WHERE `profile`='.$this->id.' AND `name`="'.$name.'" LIMIT 1'); - if ($r->rowCount()) { + if ($r->rowCount()) $obj->from_array($r->fetch(PDO::FETCH_ASSOC), true); - } } foreach ($pkgs as $pkg) { if ($pkg=sql_gentoo_package::from_atom($pkg, $this)) @@ -158,10 +159,11 @@ class sql_gentoo_profile extends sql_row_obj { $obj->name=$name; $obj->packages=implode("\n", $add); $obj->write(); - } elseif (isset($obj->id)) { - $obj->delete(); + if ($update) $exists[]=$obj->id; } } + if ($update) + $S['pdo']->query('DELETE FROM `gentoo_pkgsets` WHERE `profile`='.$this->id.($exists?' AND `id` NOT IN ('.implode(',', $exists).')':'')); } public function &get_packages() { global $S; diff --git a/shared/classes/task.php b/shared/classes/task.php index 569d0fb..d5578b2 100644 --- a/shared/classes/task.php +++ b/shared/classes/task.php @@ -26,8 +26,7 @@ class sql_task extends sql_row_obj { 'default' => '' ), 'command' => array ( - 'type' => 'TEXT', - 'not_null' => true + 'type' => 'TEXT' ), 'start' => array ( 'type' => 'INT', @@ -46,7 +45,7 @@ class sql_task extends sql_row_obj { ); function display() { - $html='<div class="task"><div class="description">'.htmlentities($this->description).'</div><div class="info">[<a href="'.url('logs/'.$this->build.'/'.$this->order).'">log</a>] <span class="command">'.htmlentities($this->command).'</span> '; + $html='<div class="task"><div class="description">'.htmlentities($this->description).'</div><div class="info">[<a href="'.url('logs/'.$this->build.'/'.$this->order).'">log</a>] '.($this->command?'<span class="command">'.htmlentities($this->command).'</span> ':''); if (isset($this->start)) { if (isset($this->finish)) { $html.='<span class="status '; @@ -69,9 +68,8 @@ class sql_task extends sql_row_obj { $html.='<span class="status running">[running]</span> <span class="time">Running for <span class="time">'.display_time(time()-$this->start).'</span></span>'; } } else { - $total=$S['pdo']->query('SELECT COUNT(*) FROM `tasks` WHERE `build`="'.$this->build.'" AND `start` IS NULL')->fetch(PDO::FETCH_COLUMN); - $num=$S['pdo']->query('SELECT COUNT(*) FROM `tasks` WHERE `builds`="'.$this->build.'" AND `start` IS NULL AND `order` <= '.$this->order); - $html.="<span class=\"status queued\">[queued $num/$total]</span>"; + $num=$S['pdo']->query('SELECT COUNT(*) FROM `tasks` WHERE `builds`="'.$this->build.'" AND `start` IS NULL AND `order` <= '.$this->order)->fetch(PDO::FETCH_ASSOC); + $html.="<span class=\"status queued\">[queued $num/".$build->queued_tasks()."]</span>"; } $html.='</div></div>'; return $html; @@ -1,7 +1,6 @@ Write a live git ebuild *** Add logging besides just commands *** Have builds and tasks not give links to logs if we're already viewing the logs -Either make task status a TEXT or stop putting command name in the status (via thrown exception) - we can fetch this later anyway - just store the task id that failed (or use the last task) Consider logging env. passed to tasks, path if we ever use it Add a statistics page Add a profiles management page/backend utility @@ -10,11 +9,9 @@ Separate variables we got from the URL from the rest, stop using $request, inste Add masked indicator back to step2, support ~arch installation Allow backend to define bail-out functions to call when it dies (things like unmounting the ISO it was copying) Add STDERR (maybe STDOUT) only option to log viewer -Simplify status to numeric on builds - varchar isn't necessary Move bundler selection out of gentoo module and generalize it Improve the quality of base system creation (if necessary) Allow config viewing for builds, not just configurations Write basic command-line wrapper to bkisofs and replace the ISO mounter with it Fix emerge system cache to discard properly -Make sure that pkgsets updater in sql_gentoo_profile will remove empty and nonexistent pkgsets Add `flags` column to configurations, builds, use it to implement public and private things |