2008-11-25 11:01:42 +00:00
|
|
|
package Hydra::Controller::Root;
|
2008-10-28 10:19:31 +00:00
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
use parent 'Catalyst::Controller';
|
2008-11-25 11:01:42 +00:00
|
|
|
use Hydra::Helper::Nix;
|
2008-10-28 10:19:31 +00:00
|
|
|
|
|
|
|
#
|
|
|
|
# Sets the actions in this controller to be registered with no prefix
|
|
|
|
# so they function identically to actions created in MyApp.pm
|
|
|
|
#
|
|
|
|
__PACKAGE__->config->{namespace} = '';
|
|
|
|
|
|
|
|
|
2008-11-14 13:57:17 +00:00
|
|
|
# Security checking of filenames.
|
|
|
|
my $pathCompRE = "(?:[A-Za-z0-9-\+][A-Za-z0-9-\+\._]*)";
|
|
|
|
my $relPathRE = "(?:$pathCompRE(?:\/$pathCompRE)*)";
|
|
|
|
|
|
|
|
|
2008-11-13 09:25:38 +00:00
|
|
|
sub begin :Private {
|
2008-11-26 17:43:45 +00:00
|
|
|
my ($self, $c) = @_;
|
2008-11-13 09:25:38 +00:00
|
|
|
$c->stash->{projects} = [$c->model('DB::Projects')->search({}, {order_by => 'displayname'})];
|
2008-11-13 09:48:10 +00:00
|
|
|
$c->stash->{curUri} = $c->request->uri;
|
2008-11-13 09:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-28 10:19:31 +00:00
|
|
|
sub error {
|
|
|
|
my ($c, $msg) = @_;
|
|
|
|
$c->stash->{template} = 'error.tt';
|
|
|
|
$c->stash->{error} = $msg;
|
|
|
|
$c->response->status(404);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-18 12:48:58 +00:00
|
|
|
sub trim {
|
|
|
|
my $s = shift;
|
|
|
|
$s =~ s/^\s+|\s+$//g;
|
|
|
|
return $s;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-28 10:19:31 +00:00
|
|
|
sub getBuild {
|
|
|
|
my ($c, $id) = @_;
|
|
|
|
(my $build) = $c->model('DB::Builds')->search({ id => $id });
|
|
|
|
return $build;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-26 23:49:51 +00:00
|
|
|
sub getBuildStats {
|
|
|
|
my ($c, $builds) = @_;
|
|
|
|
|
|
|
|
$c->stash->{finishedBuilds} = $builds->search({finished => 1}) || 0;
|
|
|
|
|
|
|
|
$c->stash->{succeededBuilds} = $builds->search(
|
|
|
|
{finished => 1, buildStatus => 0},
|
|
|
|
{join => 'resultInfo'}) || 0;
|
|
|
|
|
|
|
|
$c->stash->{scheduledBuilds} = $builds->search({finished => 0}) || 0;
|
|
|
|
|
|
|
|
$c->stash->{busyBuilds} = $builds->search(
|
|
|
|
{finished => 0, busy => 1},
|
|
|
|
{join => 'schedulingInfo'}) || 0;
|
|
|
|
|
|
|
|
$c->stash->{totalBuildTime} = $builds->search({},
|
|
|
|
{join => 'resultInfo', select => {sum => 'stoptime - starttime'}, as => ['sum']})
|
|
|
|
->first->get_column('sum') || 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-28 10:19:31 +00:00
|
|
|
sub index :Path :Args(0) {
|
2008-11-26 17:43:45 +00:00
|
|
|
my ($self, $c) = @_;
|
2008-10-28 10:19:31 +00:00
|
|
|
$c->stash->{template} = 'index.tt';
|
2008-11-26 23:49:51 +00:00
|
|
|
|
|
|
|
getBuildStats($c, $c->model('DB::Builds'));
|
2008-10-28 10:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-26 19:48:04 +00:00
|
|
|
sub login :Local {
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
|
|
|
|
my $username = $c->request->params->{username} || "";
|
|
|
|
my $password = $c->request->params->{password} || "";
|
|
|
|
|
|
|
|
if ($username && $password) {
|
|
|
|
if ($c->authenticate({username => $username, password => $password})) {
|
|
|
|
$c->response->redirect(
|
|
|
|
defined $c->flash->{afterLogin}
|
|
|
|
? $c->flash->{afterLogin}
|
|
|
|
: $c->uri_for('/'));
|
|
|
|
return;
|
2008-11-26 23:25:24 +00:00
|
|
|
}
|
2008-11-26 19:48:04 +00:00
|
|
|
$c->stash->{errorMsg} = "Bad username or password.";
|
|
|
|
}
|
|
|
|
|
|
|
|
$c->stash->{template} = 'login.tt';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub logout :Local {
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
$c->logout;
|
|
|
|
$c->response->redirect($c->uri_for('/'));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub requireLogin {
|
|
|
|
my ($c) = @_;
|
|
|
|
$c->flash->{afterLogin} = $c->request->uri;
|
|
|
|
$c->response->redirect($c->uri_for('/login'));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-26 17:43:45 +00:00
|
|
|
sub queue :Local {
|
2008-11-26 19:48:04 +00:00
|
|
|
my ($self, $c) = @_;
|
2008-11-26 17:43:45 +00:00
|
|
|
$c->stash->{template} = 'queue.tt';
|
|
|
|
$c->stash->{queue} = [$c->model('DB::Builds')->search(
|
|
|
|
{finished => 0}, {join => 'schedulingInfo', order_by => ["priority DESC", "timestamp"]})];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-27 00:38:09 +00:00
|
|
|
sub jobstatus :Local {
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
$c->stash->{template} = 'jobstatus.tt';
|
|
|
|
|
|
|
|
# Get the latest finished build for each unique job.
|
|
|
|
$c->stash->{latestBuilds} = [$c->model('DB::Builds')->search(undef,
|
|
|
|
{ join => 'resultInfo'
|
|
|
|
, where => "finished != 0 and timestamp = (select max(timestamp) from Builds " .
|
|
|
|
"where project == me.project and attrName == me.attrName and finished != 0 and system == me.system)"
|
|
|
|
, order_by => "project, attrname, system"
|
|
|
|
})];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub all :Local {
|
|
|
|
my ($self, $c, $page) = @_;
|
|
|
|
$c->stash->{template} = 'all.tt';
|
|
|
|
|
|
|
|
$page = int($page) || 1;
|
|
|
|
|
|
|
|
my $resultsPerPage = 50;
|
|
|
|
|
|
|
|
my $nrBuilds = scalar($c->model('DB::Builds')->search({finished => 1}));
|
|
|
|
|
|
|
|
$c->stash->{page} = $page;
|
|
|
|
$c->stash->{resultsPerPage} = $resultsPerPage;
|
|
|
|
$c->stash->{totalBuilds} = $nrBuilds;
|
|
|
|
|
|
|
|
$c->stash->{builds} = [$c->model('DB::Builds')->search(
|
|
|
|
{finished => 1}, {order_by => "timestamp DESC", rows => $resultsPerPage, page => $page})];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-13 14:54:50 +00:00
|
|
|
sub updateProject {
|
|
|
|
my ($c, $project) = @_;
|
|
|
|
|
2008-11-18 12:48:58 +00:00
|
|
|
my $projectName = trim $c->request->params->{name};
|
2008-11-13 14:54:50 +00:00
|
|
|
die "Invalid project name: $projectName" unless $projectName =~ /^[[:alpha:]]\w*$/;
|
|
|
|
|
2008-11-18 12:48:58 +00:00
|
|
|
my $displayName = trim $c->request->params->{displayname};
|
|
|
|
die "Invalid display name: $displayName" if $displayName eq "";
|
2008-11-13 14:54:50 +00:00
|
|
|
|
2008-11-26 23:31:07 +00:00
|
|
|
my $owner = trim $c->request->params->{owner};
|
|
|
|
die "Invalid owner: $owner"
|
|
|
|
unless defined $c->model('DB::Users')->find({username => $owner});
|
|
|
|
|
2008-11-13 14:54:50 +00:00
|
|
|
$project->name($projectName);
|
|
|
|
$project->displayname($displayName);
|
2008-11-18 12:48:58 +00:00
|
|
|
$project->description(trim $c->request->params->{description});
|
|
|
|
$project->enabled(trim($c->request->params->{enabled}) eq "1" ? 1 : 0);
|
2008-11-26 23:31:07 +00:00
|
|
|
$project->owner($owner) if $c->check_user_roles('admin');
|
2008-11-13 17:55:40 +00:00
|
|
|
|
2008-11-13 14:54:50 +00:00
|
|
|
$project->update;
|
2008-11-13 17:55:40 +00:00
|
|
|
|
|
|
|
my %jobsetNames;
|
|
|
|
|
|
|
|
foreach my $param (keys %{$c->request->params}) {
|
|
|
|
next unless $param =~ /^jobset-(\w+)-name$/;
|
|
|
|
my $baseName = $1;
|
|
|
|
next if $baseName eq "template";
|
|
|
|
|
2008-11-18 12:48:58 +00:00
|
|
|
my $jobsetName = trim $c->request->params->{"jobset-$baseName-name"};
|
2008-11-13 17:55:40 +00:00
|
|
|
die "Invalid jobset name: $jobsetName" unless $jobsetName =~ /^[[:alpha:]]\w*$/;
|
|
|
|
|
2008-11-14 13:57:17 +00:00
|
|
|
# The Nix expression path must be relative and can't contain ".." elements.
|
2008-11-18 12:48:58 +00:00
|
|
|
my $nixExprPath = trim $c->request->params->{"jobset-$baseName-nixexprpath"};
|
2008-11-14 13:57:17 +00:00
|
|
|
die "Invalid Nix expression path: $nixExprPath" if $nixExprPath !~ /^$relPathRE$/;
|
2008-11-13 17:55:40 +00:00
|
|
|
|
2008-11-18 12:48:58 +00:00
|
|
|
my $nixExprInput = trim $c->request->params->{"jobset-$baseName-nixexprinput"};
|
2008-11-13 17:55:40 +00:00
|
|
|
die "Invalid Nix expression input name: $nixExprInput" unless $nixExprInput =~ /^\w+$/;
|
|
|
|
|
|
|
|
$jobsetNames{$jobsetName} = 1;
|
|
|
|
|
2008-11-17 11:44:51 +00:00
|
|
|
my $jobset;
|
|
|
|
|
2008-11-25 16:35:33 +00:00
|
|
|
my $description = trim $c->request->params->{"jobset-$baseName-description"};
|
|
|
|
|
2008-11-13 17:55:40 +00:00
|
|
|
if ($baseName =~ /^\d+$/) { # numeric base name is auto-generated, i.e. a new entry
|
2008-11-17 11:44:51 +00:00
|
|
|
$jobset = $project->jobsets->create(
|
|
|
|
{ name => $jobsetName
|
2008-11-25 16:35:33 +00:00
|
|
|
, description => $description
|
2008-11-13 17:55:40 +00:00
|
|
|
, nixexprpath => $nixExprPath
|
|
|
|
, nixexprinput => $nixExprInput
|
|
|
|
});
|
|
|
|
} else { # it's an existing jobset
|
2008-11-17 11:44:51 +00:00
|
|
|
$jobset = ($project->jobsets->search({name => $baseName}))[0];
|
2008-11-13 17:55:40 +00:00
|
|
|
die unless defined $jobset;
|
|
|
|
$jobset->name($jobsetName);
|
2008-11-25 16:35:33 +00:00
|
|
|
$jobset->description($description);
|
2008-11-13 17:55:40 +00:00
|
|
|
$jobset->nixexprpath($nixExprPath);
|
|
|
|
$jobset->nixexprinput($nixExprInput);
|
|
|
|
$jobset->update;
|
|
|
|
}
|
2008-11-17 15:31:19 +00:00
|
|
|
|
|
|
|
my %inputNames;
|
2008-11-17 11:44:51 +00:00
|
|
|
|
|
|
|
# Process the inputs of this jobset.
|
|
|
|
foreach my $param (keys %{$c->request->params}) {
|
|
|
|
next unless $param =~ /^jobset-$baseName-input-(\w+)-name$/;
|
|
|
|
my $baseName2 = $1;
|
|
|
|
next if $baseName2 eq "template";
|
|
|
|
print STDERR "GOT INPUT: $baseName2\n";
|
|
|
|
|
2008-11-18 12:48:58 +00:00
|
|
|
my $inputName = trim $c->request->params->{"jobset-$baseName-input-$baseName2-name"};
|
2008-11-17 11:44:51 +00:00
|
|
|
die "Invalid input name: $inputName" unless $inputName =~ /^[[:alpha:]]\w*$/;
|
|
|
|
|
2008-11-18 12:48:58 +00:00
|
|
|
my $inputType = trim $c->request->params->{"jobset-$baseName-input-$baseName2-type"};
|
2008-11-17 11:44:51 +00:00
|
|
|
die "Invalid input type: $inputType" unless
|
|
|
|
$inputType eq "svn" || $inputType eq "cvs" || $inputType eq "tarball" ||
|
2008-11-25 18:34:24 +00:00
|
|
|
$inputType eq "string" || $inputType eq "path" || $inputType eq "boolean";
|
2008-11-17 11:44:51 +00:00
|
|
|
|
2008-11-17 15:31:19 +00:00
|
|
|
$inputNames{$inputName} = 1;
|
|
|
|
|
2008-11-17 11:44:51 +00:00
|
|
|
my $input;
|
|
|
|
if ($baseName2 =~ /^\d+$/) { # numeric base name is auto-generated, i.e. a new entry
|
2008-11-17 15:31:19 +00:00
|
|
|
$input = $jobset->jobsetinputs->create(
|
|
|
|
{ name => $inputName
|
|
|
|
, type => $inputType
|
|
|
|
});
|
2008-11-17 11:44:51 +00:00
|
|
|
} else { # it's an existing jobset
|
|
|
|
$input = ($jobset->jobsetinputs->search({name => $baseName2}))[0];
|
|
|
|
die unless defined $input;
|
|
|
|
$input->name($inputName);
|
|
|
|
$input->type($inputType);
|
|
|
|
$input->update;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Update the values for this input. Just delete all the
|
|
|
|
# current ones, then create the new values.
|
|
|
|
$input->jobsetinputalts->delete_all;
|
|
|
|
my $values = $c->request->params->{"jobset-$baseName-input-$baseName2-values"};
|
2008-11-17 13:39:01 +00:00
|
|
|
$values = [] unless defined $values;
|
2008-11-17 11:44:51 +00:00
|
|
|
$values = [$values] unless ref($values) eq 'ARRAY';
|
|
|
|
my $altnr = 0;
|
|
|
|
foreach my $value (@{$values}) {
|
|
|
|
print STDERR "VALUE: $value\n";
|
2008-11-25 18:34:24 +00:00
|
|
|
my $value = trim $value;
|
|
|
|
die "Invalid Boolean value: $value" if
|
|
|
|
$inputType eq "boolean" && !($value eq "true" || $value eq "false");
|
|
|
|
$input->jobsetinputalts->create({altnr => $altnr++, value => $value});
|
2008-11-17 11:44:51 +00:00
|
|
|
}
|
|
|
|
}
|
2008-11-17 15:31:19 +00:00
|
|
|
|
2008-11-17 15:31:30 +00:00
|
|
|
# Get rid of deleted inputs.
|
2008-11-17 15:31:19 +00:00
|
|
|
my @inputs = $jobset->jobsetinputs->all;
|
|
|
|
foreach my $input (@inputs) {
|
|
|
|
$input->delete unless defined $inputNames{$input->name};
|
|
|
|
}
|
2008-11-13 17:55:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
# Get rid of deleted jobsets, i.e., ones that are no longer submitted in the parameters.
|
|
|
|
my @jobsets = $project->jobsets->all;
|
|
|
|
foreach my $jobset (@jobsets) {
|
|
|
|
$jobset->delete unless defined $jobsetNames{$jobset->name};
|
|
|
|
}
|
2008-11-13 14:54:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-06 23:17:46 +00:00
|
|
|
sub project :Local {
|
2008-11-26 17:43:45 +00:00
|
|
|
my ($self, $c, $projectName, $subcommand) = @_;
|
2008-11-06 23:17:46 +00:00
|
|
|
$c->stash->{template} = 'project.tt';
|
2008-11-12 11:09:21 +00:00
|
|
|
|
2008-11-06 23:17:46 +00:00
|
|
|
(my $project) = $c->model('DB::Projects')->search({ name => $projectName });
|
2008-11-18 12:48:58 +00:00
|
|
|
return error($c, "Project $projectName doesn't exist.") if !defined $project;
|
2008-11-13 14:54:50 +00:00
|
|
|
|
|
|
|
my $isPosted = $c->request->method eq "POST";
|
|
|
|
|
|
|
|
$subcommand = "" unless defined $subcommand;
|
|
|
|
|
2008-11-26 23:25:24 +00:00
|
|
|
if ($subcommand ne "") {
|
|
|
|
|
|
|
|
return requireLogin($c) if !$c->user_exists;
|
2008-11-26 23:31:07 +00:00
|
|
|
|
|
|
|
return error($c, "Only the project owner or the administrator can perform this operation.")
|
|
|
|
unless $c->check_user_roles('admin') || $c->user->username eq $project->owner;
|
2008-11-26 23:25:24 +00:00
|
|
|
|
|
|
|
if ($subcommand eq "edit") {
|
|
|
|
$c->stash->{edit} = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
elsif ($subcommand eq "submit" && $isPosted) {
|
|
|
|
$c->model('DB')->schema->txn_do(sub {
|
|
|
|
updateProject($c, $project);
|
|
|
|
});
|
|
|
|
return $c->res->redirect($c->uri_for("/project", trim $c->request->params->{name}));
|
|
|
|
}
|
|
|
|
|
|
|
|
elsif ($subcommand eq "delete" && $isPosted) {
|
|
|
|
$c->model('DB')->schema->txn_do(sub {
|
|
|
|
$project->delete;
|
|
|
|
});
|
|
|
|
return $c->res->redirect($c->uri_for("/"));
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
return error($c, "Unknown subcommand $subcommand.");
|
|
|
|
}
|
2008-11-13 14:54:50 +00:00
|
|
|
}
|
|
|
|
|
2008-11-13 00:01:19 +00:00
|
|
|
$c->stash->{curProject} = $project;
|
2008-11-12 11:09:21 +00:00
|
|
|
|
2008-11-26 23:49:51 +00:00
|
|
|
getBuildStats($c, scalar $project->builds);
|
2008-11-12 11:09:21 +00:00
|
|
|
|
2008-11-06 23:17:46 +00:00
|
|
|
$c->stash->{jobNames} =
|
|
|
|
[$c->model('DB::Builds')->search({project => $projectName}, {select => [{distinct => 'attrname'}], as => ['attrname']})];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-13 14:54:50 +00:00
|
|
|
sub createproject :Local {
|
2008-11-26 17:43:45 +00:00
|
|
|
my ($self, $c, $subcommand) = @_;
|
2008-11-13 14:54:50 +00:00
|
|
|
|
2008-11-26 23:25:24 +00:00
|
|
|
return requireLogin($c) if !$c->user_exists;
|
2008-11-26 23:31:07 +00:00
|
|
|
|
|
|
|
return error($c, "Only administrators can create projects.")
|
|
|
|
unless $c->check_user_roles('admin');
|
2008-11-26 23:25:24 +00:00
|
|
|
|
2008-11-13 14:54:50 +00:00
|
|
|
if (defined $subcommand && $subcommand eq "submit") {
|
|
|
|
eval {
|
|
|
|
my $projectName = $c->request->params->{name};
|
|
|
|
$c->model('DB')->schema->txn_do(sub {
|
|
|
|
# Note: $projectName is validated in updateProject,
|
|
|
|
# which will abort the transaction if the name isn't
|
|
|
|
# valid.
|
|
|
|
my $project = $c->model('DB::Projects')->create({name => $projectName, displayname => ""});
|
|
|
|
updateProject($c, $project);
|
|
|
|
});
|
|
|
|
return $c->res->redirect($c->uri_for("/project", $projectName));
|
|
|
|
};
|
|
|
|
if ($@) {
|
|
|
|
return error($c, $@);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$c->stash->{template} = 'project.tt';
|
|
|
|
$c->stash->{create} = 1;
|
|
|
|
$c->stash->{edit} = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-28 15:34:29 +00:00
|
|
|
sub job :Local {
|
2008-11-26 17:43:45 +00:00
|
|
|
my ($self, $c, $projectName, $jobName) = @_;
|
2008-10-28 15:34:29 +00:00
|
|
|
$c->stash->{template} = 'job.tt';
|
2008-11-13 14:54:50 +00:00
|
|
|
|
|
|
|
(my $project) = $c->model('DB::Projects')->search({ name => $projectName });
|
2008-11-18 12:48:58 +00:00
|
|
|
return error($c, "Project $projectName doesn't exist.") if !defined $project;
|
2008-11-13 14:54:50 +00:00
|
|
|
$c->stash->{curProject} = $project;
|
|
|
|
|
2008-10-28 15:34:29 +00:00
|
|
|
$c->stash->{jobName} = $jobName;
|
2008-11-11 12:54:37 +00:00
|
|
|
$c->stash->{builds} = [$c->model('DB::Builds')->search(
|
2008-11-13 14:54:50 +00:00
|
|
|
{finished => 1, project => $projectName, attrName => $jobName},
|
2008-11-11 12:54:37 +00:00
|
|
|
{order_by => "timestamp DESC"})];
|
2008-10-28 15:34:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-28 10:19:31 +00:00
|
|
|
sub default :Path {
|
2008-11-26 17:43:45 +00:00
|
|
|
my ($self, $c) = @_;
|
2008-10-28 10:19:31 +00:00
|
|
|
error($c, "Page not found.");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub build :Local {
|
2008-11-26 17:43:45 +00:00
|
|
|
my ($self, $c, $id) = @_;
|
2008-10-28 10:19:31 +00:00
|
|
|
|
|
|
|
my $build = getBuild($c, $id);
|
|
|
|
return error($c, "Build with ID $id doesn't exist.") if !defined $build;
|
2008-10-28 12:44:36 +00:00
|
|
|
|
2008-11-13 00:01:19 +00:00
|
|
|
$c->stash->{curProject} = $build->project;
|
|
|
|
|
2008-10-28 10:19:31 +00:00
|
|
|
$c->stash->{template} = 'build.tt';
|
|
|
|
$c->stash->{build} = $build;
|
|
|
|
$c->stash->{id} = $id;
|
2008-11-11 14:45:33 +00:00
|
|
|
|
2008-11-12 11:09:21 +00:00
|
|
|
$c->stash->{curTime} = time;
|
|
|
|
|
2008-11-11 14:45:33 +00:00
|
|
|
if (!$build->finished && $build->schedulingInfo->busy) {
|
|
|
|
my $logfile = $build->schedulingInfo->logfile;
|
|
|
|
$c->stash->{logtext} = `cat $logfile`;
|
|
|
|
}
|
2008-10-28 10:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub log :Local {
|
2008-11-26 17:43:45 +00:00
|
|
|
my ($self, $c, $id) = @_;
|
2008-10-28 10:19:31 +00:00
|
|
|
|
|
|
|
my $build = getBuild($c, $id);
|
2008-11-25 00:38:16 +00:00
|
|
|
return error($c, "Build $id doesn't exist.") if !defined $build;
|
|
|
|
|
|
|
|
return error($c, "Build $id didn't produce a log.") if !defined $build->resultInfo->logfile;
|
2008-10-28 10:19:31 +00:00
|
|
|
|
|
|
|
$c->stash->{template} = 'log.tt';
|
2008-11-25 00:38:16 +00:00
|
|
|
$c->stash->{build} = $build;
|
2008-10-28 10:19:31 +00:00
|
|
|
|
|
|
|
# !!! should be done in the view (as a TT plugin).
|
2008-11-25 00:38:16 +00:00
|
|
|
$c->stash->{logtext} = loadLog($build->resultInfo->logfile);
|
2008-10-28 10:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-11 17:49:50 +00:00
|
|
|
sub nixlog :Local {
|
2008-11-26 17:43:45 +00:00
|
|
|
my ($self, $c, $id, $stepnr) = @_;
|
2008-11-11 17:49:50 +00:00
|
|
|
|
|
|
|
my $build = getBuild($c, $id);
|
|
|
|
return error($c, "Build with ID $id doesn't exist.") if !defined $build;
|
|
|
|
|
|
|
|
my $step = $build->buildsteps->find({stepnr => $stepnr});
|
|
|
|
return error($c, "Build $id doesn't have a build step $stepnr.") if !defined $step;
|
2008-11-12 13:00:56 +00:00
|
|
|
|
|
|
|
return error($c, "Build step $stepnr of build $id does not have a log file.") if $step->logfile eq "";
|
2008-11-11 17:49:50 +00:00
|
|
|
|
|
|
|
$c->stash->{template} = 'log.tt';
|
2008-11-25 01:22:47 +00:00
|
|
|
$c->stash->{build} = $build;
|
2008-11-11 17:49:50 +00:00
|
|
|
$c->stash->{step} = $step;
|
|
|
|
|
|
|
|
# !!! should be done in the view (as a TT plugin).
|
|
|
|
$c->stash->{logtext} = loadLog($step->logfile);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-28 10:19:31 +00:00
|
|
|
sub loadLog {
|
|
|
|
my ($path) = @_;
|
2008-11-18 16:45:23 +00:00
|
|
|
|
2008-11-25 00:38:16 +00:00
|
|
|
die unless defined $path;
|
|
|
|
|
2008-11-18 16:45:23 +00:00
|
|
|
# !!! quick hack
|
|
|
|
my $pipeline = ($path =~ /.bz2$/ ? "cat $path | bzip2 -d" : "cat $path")
|
|
|
|
. " | nix-log2xml | xsltproc xsl/mark-errors.xsl - | xsltproc xsl/log2html.xsl - | tail -n +2";
|
|
|
|
|
|
|
|
return `$pipeline`;
|
2008-10-28 10:19:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-12 14:41:51 +00:00
|
|
|
sub download :Local {
|
2008-11-26 17:43:45 +00:00
|
|
|
my ($self, $c, $id, $productnr, $filename, @path) = @_;
|
2008-11-12 14:41:51 +00:00
|
|
|
|
|
|
|
my $build = getBuild($c, $id);
|
|
|
|
return error($c, "Build with ID $id doesn't exist.") if !defined $build;
|
|
|
|
|
|
|
|
my $product = $build->buildproducts->find({productnr => $productnr});
|
|
|
|
return error($c, "Build $id doesn't have a product $productnr.") if !defined $product;
|
|
|
|
|
2008-11-12 15:36:50 +00:00
|
|
|
return error($c, "Product " . $product->path . " has disappeared.") unless -e $product->path;
|
2008-11-12 14:41:51 +00:00
|
|
|
|
2008-11-12 15:36:50 +00:00
|
|
|
# Security paranoia.
|
|
|
|
foreach my $elem (@path) {
|
2008-11-14 13:57:17 +00:00
|
|
|
return error($c, "Invalid filename $elem.") if $elem !~ /^$pathCompRE$/;
|
2008-11-12 15:36:50 +00:00
|
|
|
}
|
|
|
|
|
2008-11-12 16:42:07 +00:00
|
|
|
my $path = $product->path;
|
|
|
|
$path .= "/" . join("/", @path) if scalar @path > 0;
|
2008-11-12 15:36:50 +00:00
|
|
|
|
|
|
|
# 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);
|
2008-11-12 14:41:51 +00:00
|
|
|
}
|
2008-11-17 23:59:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
sub closure :Local {
|
2008-11-26 17:43:45 +00:00
|
|
|
my ($self, $c, $buildId, $productnr) = @_;
|
2008-11-17 23:59:20 +00:00
|
|
|
|
|
|
|
my $build = getBuild($c, $buildId);
|
|
|
|
return error($c, "Build with ID $buildId doesn't exist.") if !defined $build;
|
|
|
|
|
|
|
|
my $product = $build->buildproducts->find({productnr => $productnr});
|
|
|
|
return error($c, "Build $buildId doesn't have a product $productnr.") if !defined $product;
|
|
|
|
|
2008-11-18 14:48:40 +00:00
|
|
|
return error($c, "Product is not a Nix build.") if $product->type ne "nix-build";
|
|
|
|
|
2008-11-25 11:01:42 +00:00
|
|
|
return error($c, "Path " . $product->path . " is no longer available.") unless Hydra::Helper::Nix::isValidPath($product->path);
|
2008-11-18 14:48:40 +00:00
|
|
|
|
2008-11-25 11:01:42 +00:00
|
|
|
$c->stash->{current_view} = 'Hydra::View::NixClosure';
|
2008-11-18 14:48:40 +00:00
|
|
|
$c->stash->{storePath} = $product->path;
|
|
|
|
$c->stash->{name} = $build->nixname;
|
2008-11-17 23:59:20 +00:00
|
|
|
}
|
2008-11-18 16:45:23 +00:00
|
|
|
|
|
|
|
|
2008-10-28 10:19:31 +00:00
|
|
|
sub end : ActionClass('RenderView') {}
|
|
|
|
|
|
|
|
|
|
|
|
1;
|