* Build the /build stuff in a separate controller.

[?]
Feb 25, 2009, 12:03 PM
LBNVQXUBEZ45SOTGVXK5UEZXIAIZTJLWZNUYFI4JZ6J65N3KPDVQC

Dependencies

  • [2] SMM4HQTP * Put actions related to builds under /build (e.g. /log/<buildid>
  • [3] ELCI5T2A * Show the latest build for each job.
  • [4] L7LFU6IQ * Make build steps clickable as well.
  • [5] WZ3AEJ67 * hydra_update_gc_roots.pl registers build outputs that should be kept
  • [6] PBFZEQLZ
  • [7] JLDUSNUO * Unify rendering of finished and scheduled builds.
  • [8] KOTB7BKV
  • [9] AS5PAYLI
  • [10] FHF6IZJQ * Basic release management: releases are now dynamically computed as
  • [11] UHANDRAR * Closure downloads: don't include the product number; it's not needed.
  • [12] GCHNNFZP
  • [13] J5UVLXOK * Start of a basic Catalyst web interface.
  • [14] MOCEUXZA * Support serving products that are directories (such as manuals or
  • [15] BA46C5LN * Pretty-print the logs.
  • [16] L2E6EVE2 * Merged the Build and Job tables.
  • [17] LQNBKF3D
  • [18] HK32XC42
  • [19] WHAFVCEI
  • [20] IK53RV4V
  • [21] JFZNAYJX * Showing releases.
  • [22] IW6XNRL7
  • [23] JD27RBKM
  • [24] W6DC6K4I * Happy Javascript hacking.
  • [25] CMU3YKOU * Store the release name.
  • [26] Y35C6GHH * One-click installs.
  • [27] L5VIEXSC * Allow downloading of build products.
  • [28] BD3GRK4B * Get rid of "positive failures" and separate log phases.
  • [29] CLJQCY2X * Store info about all the build actions and allow them to be
  • [30] UAPS46BQ
  • [31] 6JGCGK5X
  • [32] IE3SRMWZ * Show global and per-project statistics.
  • [*] 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 hack
    my $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
    [3.1084][3.0:146]()
    # 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
    [3.577][3.232:323](),[3.466][3.232:323]()
    die "Invalid Nix expression path: $nixExprPath" if $nixExprPath !~ /^$relPathRE$/;
    [3.577]
    [3.570]
    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 hack
    my $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
    [3.2530][2.1745:1834]()
    <a href="[% c.uri_for('build' build.id 'log') %]"><strong>Available</strong></a>
    [3.2530]
    [3.2612]
    <a href="[% c.uri_for('/build' build.id 'log') %]"><strong>Available</strong></a>
  • replacement in src/Hydra/root/build.tt at line 181
    [3.4155][2.1835:1902]()
    [% log = c.uri_for('build' build.id 'nixlog' step.stepnr) %]
    [3.4155]
    [3.0]
    [% log = c.uri_for('/build' build.id 'nixlog' step.stepnr) %]
  • replacement in src/Hydra/root/product-list.tt at line 9
    [2.2174][2.2174:2262]()
    [% uri = c.uri_for('build' build.id 'download' product.productnr product.name) %]
    [2.2174]
    [2.2262]
    [% uri = c.uri_for('/build' build.id 'download' product.productnr product.name) %]
  • replacement in src/Hydra/root/product-list.tt at line 16
    [3.951][2.2270:2319]()
    [% uri = c.uri_for('nixpkg' build.id) %]
    [3.951]
    [2.2319]
    [% uri = c.uri_for('/nixpkg' build.id) %]