* Build the /build stuff in a separate controller.
[?]
Feb 25, 2009, 12:03 PM
LBNVQXUBEZ45SOTGVXK5UEZXIAIZTJLWZNUYFI4JZ6J65N3KPDVQCDependencies
- [2]
SMM4HQTP* Put actions related to builds under /build (e.g. /log/<buildid> - [3]
KOTB7BKV - [4]
WZ3AEJ67* hydra_update_gc_roots.pl registers build outputs that should be kept - [5]
AS5PAYLI - [6]
JD27RBKM - [7]
6JGCGK5X - [8]
BA46C5LN* Pretty-print the logs. - [9]
BD3GRK4B* Get rid of "positive failures" and separate log phases. - [10]
MOCEUXZA* Support serving products that are directories (such as manuals or - [11]
W6DC6K4I* Happy Javascript hacking. - [12]
WHAFVCEI - [13]
L2E6EVE2* Merged the Build and Job tables. - [14]
GCHNNFZP - [15]
UAPS46BQ - [16]
IK53RV4V - [17]
UHANDRAR* Closure downloads: don't include the product number; it's not needed. - [18]
J5UVLXOK* Start of a basic Catalyst web interface. - [19]
IE3SRMWZ* Show global and per-project statistics. - [20]
LQNBKF3D - [21]
ELCI5T2A* Show the latest build for each job. - [22]
CLJQCY2X* Store info about all the build actions and allow them to be - [23]
L7LFU6IQ* Make build steps clickable as well. - [24]
HK32XC42 - [25]
Y35C6GHH* One-click installs. - [26]
PBFZEQLZ - [27]
JLDUSNUO* Unify rendering of finished and scheduled builds. - [28]
FHF6IZJQ* Basic release management: releases are now dynamically computed as - [29]
IW6XNRL7 - [30]
L5VIEXSC* Allow downloading of build products. - [31]
JFZNAYJX* Showing releases. - [32]
CMU3YKOU* Store the release name. - [*]
PKPWUHUX* Idem. - [*]
SHBLLAVH* More global substitution.
Change contents
- file addition: Build.pm[34.54]
package Hydra::Controller::Build;use strict;use warnings;use parent 'Catalyst::Controller';use Hydra::Helper::Nix;use Hydra::Helper::CatalystUtils;# Security checking of filenames.my $pathCompRE = "(?:[A-Za-z0-9-\+][A-Za-z0-9-\+\._]*)";my $relPathRE = "(?:$pathCompRE(?:\/$pathCompRE)*)";sub build : Chained('/') PathPart CaptureArgs(1) {my ($self, $c, $id) = @_;$c->stash->{id} = $id;$c->stash->{build} = getBuild($c, $id);if (!defined $c->stash->{build}) {error($c, "Build with ID $id doesn't exist.");$c->response->status(404);return;}$c->stash->{curProject} = $c->stash->{build}->project;}sub view_build : Chained('build') PathPart('') Args(0) {my ($self, $c) = @_;my $build = $c->stash->{build};$c->stash->{template} = 'build.tt';$c->stash->{curTime} = time;$c->stash->{available} = isValidPath $build->outpath;if (!$build->finished && $build->schedulingInfo->busy) {my $logfile = $build->schedulingInfo->logfile;$c->stash->{logtext} = `cat $logfile`;}}sub view_nixlog : Chained('build') PathPart('nixlog') Args(1) {my ($self, $c, $stepnr) = @_;my $step = $c->stash->{build}->buildsteps->find({stepnr => $stepnr});return error($c, "Build doesn't have a build step $stepnr.") if !defined $step;$c->stash->{template} = 'log.tt';$c->stash->{step} = $step;# !!! should be done in the view (as a TT plugin).$c->stash->{logtext} = loadLog($c, $step->logfile);}sub view_log : Chained('build') PathPart('log') Args(0) {my ($self, $c) = @_;return error($c, "Build didn't produce a log.") if !defined $c->stash->{build}->resultInfo->logfile;$c->stash->{template} = 'log.tt';# !!! should be done in the view (as a TT plugin).$c->stash->{logtext} = loadLog($c, $c->stash->{build}->resultInfo->logfile);}sub loadLog {my ($c, $path) = @_;die unless defined $path;# !!! quick hackmy $pipeline = ($path =~ /.bz2$/ ? "cat $path | bzip2 -d" : "cat $path"). " | nix-log2xml | xsltproc " . $c->path_to("xsl/mark-errors.xsl") . " -". " | xsltproc " . $c->path_to("xsl/log2html.xsl") . " - | tail -n +2";return `$pipeline`;}sub download : Chained('build') PathPart('download') {my ($self, $c, $productnr, $filename, @path) = @_;my $product = $c->stash->{build}->buildproducts->find({productnr => $productnr});return error($c, "Build doesn't have a product $productnr.") if !defined $product;return error($c, "Product " . $product->path . " has disappeared.") unless -e $product->path;# Security paranoia.foreach my $elem (@path) {return error($c, "Invalid filename $elem.") if $elem !~ /^$pathCompRE$/;}my $path = $product->path;$path .= "/" . join("/", @path) if scalar @path > 0;# If this is a directory but no "/" is attached, then redirect.if (-d $path && substr($c->request->uri, -1) ne "/") {return $c->res->redirect($c->request->uri . "/");}$path = "$path/index.html" if -d $path && -e "$path/index.html";if (!-e $path) {return error($c, "File $path does not exist.");}$c->serve_static_file($path);}1; - edit in src/Hydra/lib/Hydra/Controller/Root.pm at line 7[35.110][3.908]
use Hydra::Helper::CatalystUtils; - edit in src/Hydra/lib/Hydra/Controller/Root.pm at line 12
# Security checking of filenames.my $pathCompRE = "(?:[A-Za-z0-9-\+][A-Za-z0-9-\+\._]*)";my $relPathRE = "(?:$pathCompRE(?:\/$pathCompRE)*)"; - edit in src/Hydra/lib/Hydra/Controller/Root.pm at line 18[3.44]→[3.148:150](∅→∅),[3.148]→[3.148:150](∅→∅),[3.150]→[3.1084:1122](∅→∅),[3.1084]→[3.1084:1122](∅→∅),[3.1122]→[2.38:75](∅→∅)
}sub error {my ($c, $msg) = @_;$c->error($msg);$c->detach; - edit in src/Hydra/lib/Hydra/Controller/Root.pm at line 25[3.73]→[3.73:75](∅→∅),[3.75]→[3.1227:1228](∅→∅),[3.1227]→[3.1227:1228](∅→∅),[3.1228]→[3.76:77](∅→∅),[3.77]→[3.1228:1266](∅→∅),[3.1228]→[3.1228:1266](∅→∅),[3.1266]→[3.0:52](∅→∅),[3.52]→[3.1332:1351](∅→∅),[3.1332]→[3.1332:1351](∅→∅)
}sub getBuild {my ($c, $id) = @_;my $build = $c->model('DB::Builds')->find($id);return $build; - replacement in src/Hydra/lib/Hydra/Controller/Root.pm at line 353
die "Invalid Nix expression path: $nixExprPath" if $nixExprPath !~ /^$relPathRE$/;die "Invalid Nix expression path: $nixExprPath" if $nixExprPath !~ /^$Build::relPathRE$/; - edit in src/Hydra/lib/Hydra/Controller/Root.pm at line 555[2.107]→[3.1592:1596](∅→∅),[3.1592]→[3.1592:1596](∅→∅),[3.1596]→[2.108:159](∅→∅),[2.159]→[3.468:498](∅→∅),[3.1615]→[3.468:498](∅→∅),[3.498]→[2.160:397](∅→∅),[2.397]→[3.1647:1648](∅→∅),[3.498]→[3.1647:1648](∅→∅),[3.1647]→[3.1647:1648](∅→∅),[3.1648]→[2.398:459](∅→∅),[2.459]→[3.249:250](∅→∅),[3.1760]→[3.249:250](∅→∅),[3.297]→[3.398:399](∅→∅),[3.1760]→[3.398:399](∅→∅),[3.399]→[2.460:542](∅→∅),[2.542]→[3.0:1](∅→∅),[3.1865]→[3.0:1](∅→∅),[3.1]→[2.543:624](∅→∅),[2.624]→[3.978:1011](∅→∅),[3.1]→[3.978:1011](∅→∅),[3.1]→[3.1:59](∅→∅),[3.59]→[3.1011:1012](∅→∅),[3.1011]→[3.1011:1012](∅→∅),[3.1012]→[3.1:170](∅→∅),[3.1]→[3.1:170](∅→∅),[3.170]→[3.1865:1869](∅→∅),[3.1865]→[3.1865:1869](∅→∅),[3.1869]→[2.625:723](∅→∅),[3.32]→[3.1929:1930](∅→∅),[3.529]→[3.1929:1930](∅→∅),[2.723]→[3.1929:1930](∅→∅),[3.1929]→[3.1929:1930](∅→∅),[3.1930]→[2.724:882](∅→∅),[2.882]→[3.200:201](∅→∅),[3.200]→[3.200:201](∅→∅),[3.201]→[3.2215:2253](∅→∅),[3.2215]→[3.2215:2253](∅→∅),[3.2253]→[2.883:914](∅→∅),[3.235]→[3.2309:2365](∅→∅),[2.914]→[3.2309:2365](∅→∅),[3.2309]→[3.2309:2365](∅→∅),[3.2365]→[2.915:971](∅→∅),[3.69]→[3.162:166](∅→∅),[3.301]→[3.162:166](∅→∅),[2.971]→[3.162:166](∅→∅),[3.2413]→[3.162:166](∅→∅),[3.166]→[2.972:1055](∅→∅),[2.1055]→[3.340:341](∅→∅),[3.340]→[3.340:341](∅→∅),[3.341]→[2.1056:1161](∅→∅),[2.1161]→[3.0:1](∅→∅),[3.491]→[3.0:1](∅→∅),[3.496]→[3.496:534](∅→∅),[3.592]→[3.592:648](∅→∅),[3.648]→[2.1162:1243](∅→∅),[3.126]→[3.2413:2431](∅→∅),[3.700]→[3.2413:2431](∅→∅),[2.1243]→[3.2413:2431](∅→∅),[3.2413]→[3.2413:2431](∅→∅),[3.2431]→[3.127:152](∅→∅),[3.152]→[3.302:333](∅→∅),[3.2452]→[3.302:333](∅→∅),[3.333]→[3.0:99](∅→∅),[3.2452]→[3.0:99](∅→∅),[3.99]→[3.153:316](∅→∅),[3.316]→[3.203:228](∅→∅),[3.203]→[3.203:228](∅→∅)
}sub build : Chained('/') PathPart CaptureArgs(1) {my ($self, $c, $id) = @_;$c->stash->{id} = $id;$c->stash->{build} = getBuild($c, $id);if (!defined $c->stash->{build}) {error($c, "Build with ID $id doesn't exist.");$c->response->status(404);return;}$c->stash->{curProject} = $c->stash->{build}->project;}sub view_build : Chained('build') PathPart('') Args(0) {my ($self, $c) = @_;my $build = $c->stash->{build};$c->stash->{template} = 'build.tt';$c->stash->{curTime} = time;$c->stash->{available} = isValidPath $build->outpath;if (!$build->finished && $build->schedulingInfo->busy) {my $logfile = $build->schedulingInfo->logfile;$c->stash->{logtext} = `cat $logfile`;}}sub view_nixlog : Chained('build') PathPart('nixlog') Args(1) {my ($self, $c, $stepnr) = @_;my $step = $c->stash->{build}->buildsteps->find({stepnr => $stepnr});return error($c, "Build doesn't have a build step $stepnr.") if !defined $step;$c->stash->{template} = 'log.tt';$c->stash->{step} = $step;# !!! should be done in the view (as a TT plugin).$c->stash->{logtext} = loadLog($c, $step->logfile);}sub view_log : Chained('build') PathPart('log') Args(0) {my ($self, $c) = @_;return error($c, "Build didn't produce a log.") if !defined $c->stash->{build}->resultInfo->logfile;$c->stash->{template} = 'log.tt';# !!! should be done in the view (as a TT plugin).$c->stash->{logtext} = loadLog($c, $c->stash->{build}->resultInfo->logfile);}sub loadLog {my ($c, $path) = @_;die unless defined $path;# !!! quick hackmy $pipeline = ($path =~ /.bz2$/ ? "cat $path | bzip2 -d" : "cat $path"). " | nix-log2xml | xsltproc " . $c->path_to("xsl/mark-errors.xsl") . " -". " | xsltproc " . $c->path_to("xsl/log2html.xsl") . " - | tail -n +2";return `$pipeline`; - edit in src/Hydra/lib/Hydra/Controller/Root.pm at line 558[3.2]→[2.1244:1354](∅→∅),[3.62]→[3.2595:2596](∅→∅),[3.79]→[3.2595:2596](∅→∅),[3.630]→[3.2595:2596](∅→∅),[2.1354]→[3.2595:2596](∅→∅),[3.2595]→[3.2595:2596](∅→∅),[3.2596]→[2.1355:1528](∅→∅),[2.1528]→[3.358:359](∅→∅),[3.358]→[3.358:359](∅→∅),[3.359]→[3.63:161](∅→∅),[3.161]→[3.454:455](∅→∅),[3.454]→[3.454:455](∅→∅),[3.455]→[3.162:218](∅→∅),[3.218]→[3.324:405](∅→∅),[3.405]→[3.356:367](∅→∅),[3.356]→[3.356:367](∅→∅),[3.367]→[3.0:88](∅→∅),[3.88]→[3.423:808](∅→∅),[3.423]→[3.423:808](∅→∅),[3.808]→[3.71:75](∅→∅)
sub download : Chained('build') PathPart('download') {my ($self, $c, $productnr, $filename, @path) = @_;my $product = $c->stash->{build}->buildproducts->find({productnr => $productnr});return error($c, "Build doesn't have a product $productnr.") if !defined $product;return error($c, "Product " . $product->path . " has disappeared.") unless -e $product->path;# Security paranoia.foreach my $elem (@path) {return error($c, "Invalid filename $elem.") if $elem !~ /^$pathCompRE$/;}my $path = $product->path;$path .= "/" . join("/", @path) if scalar @path > 0;# If this is a directory but no "/" is attached, then redirect.if (-d $path && substr($c->request->uri, -1) ne "/") {return $c->res->redirect($c->request->uri . "/");}$path = "$path/index.html" if -d $path && -e "$path/index.html";if (!-e $path) {return error($c, "File $path does not exist.");}$c->serve_static_file($path);} - file addition: CatalystUtils.pm[34.109]
package Hydra::Helper::CatalystUtils;use strict;use Exporter;our @ISA = qw(Exporter);our @EXPORT = qw(getBuild error);sub getBuild {my ($c, $id) = @_;my $build = $c->model('DB::Builds')->find($id);return $build;}sub error {my ($c, $msg) = @_;$c->error($msg);$c->detach;}1; - replacement in src/Hydra/root/build.tt at line 124
<a href="[% c.uri_for('build' build.id 'log') %]"><strong>Available</strong></a><a href="[% c.uri_for('/build' build.id 'log') %]"><strong>Available</strong></a> - replacement in src/Hydra/root/build.tt at line 181
[% log = c.uri_for('build' build.id 'nixlog' step.stepnr) %][% log = c.uri_for('/build' build.id 'nixlog' step.stepnr) %] - replacement in src/Hydra/root/product-list.tt at line 9
[% uri = c.uri_for('build' build.id 'download' product.productnr product.name) %][% uri = c.uri_for('/build' build.id 'download' product.productnr product.name) %] - replacement in src/Hydra/root/product-list.tt at line 16
[% uri = c.uri_for('nixpkg' build.id) %][% uri = c.uri_for('/nixpkg' build.id) %]