* HydraFrontend -> Hydra.
This commit is contained in:
23
src/Hydra/lib/HydraFrontend.pm
Normal file
23
src/Hydra/lib/HydraFrontend.pm
Normal file
@ -0,0 +1,23 @@
|
||||
package HydraFrontend;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Catalyst::Runtime '5.70';
|
||||
|
||||
use parent qw/Catalyst/;
|
||||
use Catalyst qw/-Debug
|
||||
ConfigLoader
|
||||
Static::Simple
|
||||
StackTrace
|
||||
/;
|
||||
our $VERSION = '0.01';
|
||||
|
||||
__PACKAGE__->config(
|
||||
name => 'HydraFrontend',
|
||||
default_view => "TT"
|
||||
);
|
||||
|
||||
__PACKAGE__->setup();
|
||||
|
||||
1;
|
408
src/Hydra/lib/HydraFrontend/Controller/Root.pm
Normal file
408
src/Hydra/lib/HydraFrontend/Controller/Root.pm
Normal file
@ -0,0 +1,408 @@
|
||||
package HydraFrontend::Controller::Root;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use parent 'Catalyst::Controller';
|
||||
use HydraFrontend::Helper::Nix;
|
||||
|
||||
#
|
||||
# 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} = '';
|
||||
|
||||
|
||||
# Security checking of filenames.
|
||||
my $pathCompRE = "(?:[A-Za-z0-9-\+][A-Za-z0-9-\+\._]*)";
|
||||
my $relPathRE = "(?:$pathCompRE(?:\/$pathCompRE)*)";
|
||||
|
||||
|
||||
sub begin :Private {
|
||||
my ( $self, $c ) = @_;
|
||||
$c->stash->{projects} = [$c->model('DB::Projects')->search({}, {order_by => 'displayname'})];
|
||||
$c->stash->{curUri} = $c->request->uri;
|
||||
}
|
||||
|
||||
|
||||
sub error {
|
||||
my ($c, $msg) = @_;
|
||||
$c->stash->{template} = 'error.tt';
|
||||
$c->stash->{error} = $msg;
|
||||
$c->response->status(404);
|
||||
}
|
||||
|
||||
|
||||
sub trim {
|
||||
my $s = shift;
|
||||
$s =~ s/^\s+|\s+$//g;
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
sub getBuild {
|
||||
my ($c, $id) = @_;
|
||||
(my $build) = $c->model('DB::Builds')->search({ id => $id });
|
||||
return $build;
|
||||
}
|
||||
|
||||
|
||||
sub index :Path :Args(0) {
|
||||
my ( $self, $c ) = @_;
|
||||
$c->stash->{template} = 'index.tt';
|
||||
$c->stash->{scheduled} = [$c->model('DB::Builds')->search(
|
||||
{finished => 0}, {join => 'schedulingInfo'})]; # !!!
|
||||
$c->stash->{allBuilds} = [$c->model('DB::Builds')->search(
|
||||
{finished => 1}, {order_by => "timestamp DESC"})];
|
||||
# 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)"
|
||||
, order_by => "project, attrname"
|
||||
})];
|
||||
}
|
||||
|
||||
|
||||
sub updateProject {
|
||||
my ($c, $project) = @_;
|
||||
|
||||
my $projectName = trim $c->request->params->{name};
|
||||
die "Invalid project name: $projectName" unless $projectName =~ /^[[:alpha:]]\w*$/;
|
||||
|
||||
my $displayName = trim $c->request->params->{displayname};
|
||||
die "Invalid display name: $displayName" if $displayName eq "";
|
||||
|
||||
$project->name($projectName);
|
||||
$project->displayname($displayName);
|
||||
$project->description(trim $c->request->params->{description});
|
||||
$project->enabled(trim($c->request->params->{enabled}) eq "1" ? 1 : 0);
|
||||
|
||||
$project->update;
|
||||
|
||||
my %jobsetNames;
|
||||
|
||||
foreach my $param (keys %{$c->request->params}) {
|
||||
next unless $param =~ /^jobset-(\w+)-name$/;
|
||||
my $baseName = $1;
|
||||
next if $baseName eq "template";
|
||||
|
||||
my $jobsetName = trim $c->request->params->{"jobset-$baseName-name"};
|
||||
die "Invalid jobset name: $jobsetName" unless $jobsetName =~ /^[[:alpha:]]\w*$/;
|
||||
|
||||
# The Nix expression path must be relative and can't contain ".." elements.
|
||||
my $nixExprPath = trim $c->request->params->{"jobset-$baseName-nixexprpath"};
|
||||
die "Invalid Nix expression path: $nixExprPath" if $nixExprPath !~ /^$relPathRE$/;
|
||||
|
||||
my $nixExprInput = trim $c->request->params->{"jobset-$baseName-nixexprinput"};
|
||||
die "Invalid Nix expression input name: $nixExprInput" unless $nixExprInput =~ /^\w+$/;
|
||||
|
||||
$jobsetNames{$jobsetName} = 1;
|
||||
|
||||
my $jobset;
|
||||
|
||||
if ($baseName =~ /^\d+$/) { # numeric base name is auto-generated, i.e. a new entry
|
||||
$jobset = $project->jobsets->create(
|
||||
{ name => $jobsetName
|
||||
, description => trim $c->request->params->{"jobset-$baseName-description"}
|
||||
, nixexprpath => $nixExprPath
|
||||
, nixexprinput => $nixExprInput
|
||||
});
|
||||
} else { # it's an existing jobset
|
||||
$jobset = ($project->jobsets->search({name => $baseName}))[0];
|
||||
die unless defined $jobset;
|
||||
$jobset->name($jobsetName);
|
||||
$jobset->description(trim $c->request->params->{"jobset-$baseName-description"});
|
||||
$jobset->nixexprpath($nixExprPath);
|
||||
$jobset->nixexprinput($nixExprInput);
|
||||
$jobset->update;
|
||||
}
|
||||
|
||||
my %inputNames;
|
||||
|
||||
# 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";
|
||||
|
||||
my $inputName = trim $c->request->params->{"jobset-$baseName-input-$baseName2-name"};
|
||||
die "Invalid input name: $inputName" unless $inputName =~ /^[[:alpha:]]\w*$/;
|
||||
|
||||
my $inputType = trim $c->request->params->{"jobset-$baseName-input-$baseName2-type"};
|
||||
die "Invalid input type: $inputType" unless
|
||||
$inputType eq "svn" || $inputType eq "cvs" || $inputType eq "tarball" ||
|
||||
$inputType eq "string" || $inputType eq "path";
|
||||
|
||||
$inputNames{$inputName} = 1;
|
||||
|
||||
my $input;
|
||||
if ($baseName2 =~ /^\d+$/) { # numeric base name is auto-generated, i.e. a new entry
|
||||
$input = $jobset->jobsetinputs->create(
|
||||
{ name => $inputName
|
||||
, type => $inputType
|
||||
});
|
||||
} 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"};
|
||||
$values = [] unless defined $values;
|
||||
$values = [$values] unless ref($values) eq 'ARRAY';
|
||||
my $altnr = 0;
|
||||
foreach my $value (@{$values}) {
|
||||
print STDERR "VALUE: $value\n";
|
||||
$input->jobsetinputalts->create({altnr => $altnr++, value => trim $value});
|
||||
}
|
||||
}
|
||||
|
||||
# Get rid of deleted inputs.
|
||||
my @inputs = $jobset->jobsetinputs->all;
|
||||
foreach my $input (@inputs) {
|
||||
$input->delete unless defined $inputNames{$input->name};
|
||||
}
|
||||
}
|
||||
|
||||
# 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};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub project :Local {
|
||||
my ( $self, $c, $projectName, $subcommand ) = @_;
|
||||
$c->stash->{template} = 'project.tt';
|
||||
|
||||
(my $project) = $c->model('DB::Projects')->search({ name => $projectName });
|
||||
return error($c, "Project $projectName doesn't exist.") if !defined $project;
|
||||
|
||||
my $isPosted = $c->request->method eq "POST";
|
||||
|
||||
$subcommand = "" unless defined $subcommand;
|
||||
|
||||
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("/"));
|
||||
} elsif ($subcommand eq "") {
|
||||
} else {
|
||||
return error($c, "Unknown subcommand $subcommand.");
|
||||
}
|
||||
|
||||
$c->stash->{curProject} = $project;
|
||||
|
||||
$c->stash->{finishedBuilds} = $c->model('DB::Builds')->search(
|
||||
{project => $projectName, finished => 1});
|
||||
|
||||
$c->stash->{succeededBuilds} = $c->model('DB::Builds')->search(
|
||||
{project => $projectName, finished => 1, buildStatus => 0},
|
||||
{join => 'resultInfo'});
|
||||
|
||||
$c->stash->{scheduledBuilds} = $c->model('DB::Builds')->search(
|
||||
{project => $projectName, finished => 0});
|
||||
|
||||
$c->stash->{busyBuilds} = $c->model('DB::Builds')->search(
|
||||
{project => $projectName, finished => 0, busy => 1},
|
||||
{join => 'schedulingInfo'});
|
||||
|
||||
$c->stash->{totalBuildTime} = $c->model('DB::Builds')->search(
|
||||
{project => $projectName},
|
||||
{join => 'resultInfo', select => {sum => 'stoptime - starttime'}, as => ['sum']})
|
||||
->first->get_column('sum');
|
||||
$c->stash->{totalBuildTime} = 0 unless defined $c->stash->{totalBuildTime};
|
||||
|
||||
$c->stash->{jobNames} =
|
||||
[$c->model('DB::Builds')->search({project => $projectName}, {select => [{distinct => 'attrname'}], as => ['attrname']})];
|
||||
}
|
||||
|
||||
|
||||
sub createproject :Local {
|
||||
my ( $self, $c, $subcommand ) = @_;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
sub job :Local {
|
||||
my ( $self, $c, $projectName, $jobName ) = @_;
|
||||
$c->stash->{template} = 'job.tt';
|
||||
|
||||
(my $project) = $c->model('DB::Projects')->search({ name => $projectName });
|
||||
return error($c, "Project $projectName doesn't exist.") if !defined $project;
|
||||
$c->stash->{curProject} = $project;
|
||||
|
||||
$c->stash->{jobName} = $jobName;
|
||||
$c->stash->{builds} = [$c->model('DB::Builds')->search(
|
||||
{finished => 1, project => $projectName, attrName => $jobName},
|
||||
{order_by => "timestamp DESC"})];
|
||||
}
|
||||
|
||||
|
||||
sub default :Path {
|
||||
my ( $self, $c ) = @_;
|
||||
error($c, "Page not found.");
|
||||
}
|
||||
|
||||
|
||||
sub build :Local {
|
||||
my ( $self, $c, $id ) = @_;
|
||||
|
||||
my $build = getBuild($c, $id);
|
||||
return error($c, "Build with ID $id doesn't exist.") if !defined $build;
|
||||
|
||||
$c->stash->{curProject} = $build->project;
|
||||
|
||||
$c->stash->{template} = 'build.tt';
|
||||
$c->stash->{build} = $build;
|
||||
$c->stash->{id} = $id;
|
||||
|
||||
$c->stash->{curTime} = time;
|
||||
|
||||
if (!$build->finished && $build->schedulingInfo->busy) {
|
||||
my $logfile = $build->schedulingInfo->logfile;
|
||||
$c->stash->{logtext} = `cat $logfile`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub log :Local {
|
||||
my ( $self, $c, $id ) = @_;
|
||||
|
||||
my $build = getBuild($c, $id);
|
||||
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;
|
||||
|
||||
$c->stash->{template} = 'log.tt';
|
||||
$c->stash->{build} = $build;
|
||||
|
||||
# !!! should be done in the view (as a TT plugin).
|
||||
$c->stash->{logtext} = loadLog($build->resultInfo->logfile);
|
||||
}
|
||||
|
||||
|
||||
sub nixlog :Local {
|
||||
my ( $self, $c, $id, $stepnr ) = @_;
|
||||
|
||||
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;
|
||||
|
||||
return error($c, "Build step $stepnr of build $id does not have a log file.") if $step->logfile eq "";
|
||||
|
||||
$c->stash->{template} = 'log.tt';
|
||||
$c->stash->{build} = $build;
|
||||
$c->stash->{step} = $step;
|
||||
|
||||
# !!! should be done in the view (as a TT plugin).
|
||||
$c->stash->{logtext} = loadLog($step->logfile);
|
||||
}
|
||||
|
||||
|
||||
sub loadLog {
|
||||
my ($path) = @_;
|
||||
|
||||
die unless defined $path;
|
||||
|
||||
# !!! 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`;
|
||||
}
|
||||
|
||||
|
||||
sub download :Local {
|
||||
my ( $self, $c, $id, $productnr, $filename, @path ) = @_;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
sub closure :Local {
|
||||
my ( $self, $c, $buildId, $productnr ) = @_;
|
||||
|
||||
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;
|
||||
|
||||
return error($c, "Product is not a Nix build.") if $product->type ne "nix-build";
|
||||
|
||||
return error($c, "Path " . $product->path . " is no longer available.") unless HydraFrontend::Helper::Nix::isValidPath($product->path);
|
||||
|
||||
$c->stash->{current_view} = 'HydraFrontend::View::NixClosure';
|
||||
$c->stash->{storePath} = $product->path;
|
||||
$c->stash->{name} = $build->nixname;
|
||||
}
|
||||
|
||||
|
||||
sub end : ActionClass('RenderView') {}
|
||||
|
||||
|
||||
1;
|
14
src/Hydra/lib/HydraFrontend/Helper/Nix.pm
Normal file
14
src/Hydra/lib/HydraFrontend/Helper/Nix.pm
Normal file
@ -0,0 +1,14 @@
|
||||
package HydraFrontend::Helper::Nix;
|
||||
|
||||
use strict;
|
||||
|
||||
|
||||
sub isValidPath {
|
||||
my $path = shift;
|
||||
$SIG{CHLD} = 'DEFAULT'; # !!! work around system() failing if SIGCHLD is ignored
|
||||
return system("nix-store --check-validity $path") == 0;
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
36
src/Hydra/lib/HydraFrontend/Model/DB.pm
Normal file
36
src/Hydra/lib/HydraFrontend/Model/DB.pm
Normal file
@ -0,0 +1,36 @@
|
||||
package HydraFrontend::Model::DB;
|
||||
|
||||
use strict;
|
||||
use base 'Catalyst::Model::DBIC::Schema';
|
||||
|
||||
__PACKAGE__->config(
|
||||
schema_class => 'HydraFrontend::Schema',
|
||||
connect_info => [
|
||||
'dbi:SQLite:../hydra.sqlite',
|
||||
|
||||
],
|
||||
);
|
||||
|
||||
=head1 NAME
|
||||
|
||||
HydraFrontend::Model::DB - Catalyst DBIC Schema Model
|
||||
=head1 SYNOPSIS
|
||||
|
||||
See L<HydraFrontend>
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
L<Catalyst::Model::DBIC::Schema> Model using schema L<HydraFrontend::Schema>
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Eelco Dolstra
|
||||
|
||||
=head1 LICENSE
|
||||
|
||||
This library is free software, you can redistribute it and/or modify
|
||||
it under the same terms as Perl itself.
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
16
src/Hydra/lib/HydraFrontend/Schema.pm
Normal file
16
src/Hydra/lib/HydraFrontend/Schema.pm
Normal file
@ -0,0 +1,16 @@
|
||||
package HydraFrontend::Schema;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class::Schema';
|
||||
|
||||
__PACKAGE__->load_classes;
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:rS2THZrlrDHnIAWmvduE1g
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
48
src/Hydra/lib/HydraFrontend/Schema/Buildinputs.pm
Normal file
48
src/Hydra/lib/HydraFrontend/Schema/Buildinputs.pm
Normal file
@ -0,0 +1,48 @@
|
||||
package HydraFrontend::Schema::Buildinputs;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("BuildInputs");
|
||||
__PACKAGE__->add_columns(
|
||||
"id",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"build",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"name",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"type",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"uri",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"revision",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"tag",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"value",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"dependency",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"path",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"sha256hash",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
);
|
||||
__PACKAGE__->set_primary_key("id");
|
||||
__PACKAGE__->belongs_to("build", "HydraFrontend::Schema::Builds", { id => "build" });
|
||||
__PACKAGE__->belongs_to(
|
||||
"dependency",
|
||||
"HydraFrontend::Schema::Builds",
|
||||
{ id => "dependency" },
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:9u9ep3Cq/SginPyhrzXlTA
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
41
src/Hydra/lib/HydraFrontend/Schema/Buildproducts.pm
Normal file
41
src/Hydra/lib/HydraFrontend/Schema/Buildproducts.pm
Normal file
@ -0,0 +1,41 @@
|
||||
package HydraFrontend::Schema::Buildproducts;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("BuildProducts");
|
||||
__PACKAGE__->add_columns(
|
||||
"build",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"productnr",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"type",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"subtype",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"filesize",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"sha1hash",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"sha256hash",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"path",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"name",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"description",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
);
|
||||
__PACKAGE__->set_primary_key("build", "productnr");
|
||||
__PACKAGE__->belongs_to("build", "HydraFrontend::Schema::Builds", { id => "build" });
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:d85fCxlq/WDfQa20zXYuzw
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
35
src/Hydra/lib/HydraFrontend/Schema/Buildresultinfo.pm
Normal file
35
src/Hydra/lib/HydraFrontend/Schema/Buildresultinfo.pm
Normal file
@ -0,0 +1,35 @@
|
||||
package HydraFrontend::Schema::Buildresultinfo;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("BuildResultInfo");
|
||||
__PACKAGE__->add_columns(
|
||||
"id",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"iscachedbuild",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"buildstatus",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"errormsg",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"starttime",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"stoptime",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"logfile",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
);
|
||||
__PACKAGE__->set_primary_key("id");
|
||||
__PACKAGE__->belongs_to("id", "HydraFrontend::Schema::Builds", { id => "id" });
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:c2KXbqA8Xan4Lgf7AlK2EA
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
96
src/Hydra/lib/HydraFrontend/Schema/Builds.pm
Normal file
96
src/Hydra/lib/HydraFrontend/Schema/Builds.pm
Normal file
@ -0,0 +1,96 @@
|
||||
package HydraFrontend::Schema::Builds;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("Builds");
|
||||
__PACKAGE__->add_columns(
|
||||
"id",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"finished",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"timestamp",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"project",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"jobset",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"attrname",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"nixname",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"description",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"drvpath",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"outpath",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"system",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
);
|
||||
__PACKAGE__->set_primary_key("id");
|
||||
__PACKAGE__->belongs_to(
|
||||
"project",
|
||||
"HydraFrontend::Schema::Projects",
|
||||
{ name => "project" },
|
||||
);
|
||||
__PACKAGE__->belongs_to(
|
||||
"jobset",
|
||||
"HydraFrontend::Schema::Jobsets",
|
||||
{ name => "jobset", project => "project" },
|
||||
);
|
||||
__PACKAGE__->has_many(
|
||||
"buildschedulinginfoes",
|
||||
"HydraFrontend::Schema::Buildschedulinginfo",
|
||||
{ "foreign.id" => "self.id" },
|
||||
);
|
||||
__PACKAGE__->has_many(
|
||||
"buildresultinfoes",
|
||||
"HydraFrontend::Schema::Buildresultinfo",
|
||||
{ "foreign.id" => "self.id" },
|
||||
);
|
||||
__PACKAGE__->has_many(
|
||||
"buildsteps",
|
||||
"HydraFrontend::Schema::Buildsteps",
|
||||
{ "foreign.id" => "self.id" },
|
||||
);
|
||||
__PACKAGE__->has_many(
|
||||
"buildinputs_builds",
|
||||
"HydraFrontend::Schema::Buildinputs",
|
||||
{ "foreign.build" => "self.id" },
|
||||
);
|
||||
__PACKAGE__->has_many(
|
||||
"buildinputs_dependencies",
|
||||
"HydraFrontend::Schema::Buildinputs",
|
||||
{ "foreign.dependency" => "self.id" },
|
||||
);
|
||||
__PACKAGE__->has_many(
|
||||
"buildproducts",
|
||||
"HydraFrontend::Schema::Buildproducts",
|
||||
{ "foreign.build" => "self.id" },
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:/Iabv2HeyAsubLe+yPc/6Q
|
||||
|
||||
__PACKAGE__->has_many(dependents => 'HydraFrontend::Schema::Buildinputs', 'dependency');
|
||||
|
||||
__PACKAGE__->has_many(inputs => 'HydraFrontend::Schema::Buildinputs', 'build');
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"schedulingInfo",
|
||||
"HydraFrontend::Schema::Buildschedulinginfo",
|
||||
{ id => "id" },
|
||||
);
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"resultInfo",
|
||||
"HydraFrontend::Schema::Buildresultinfo",
|
||||
{ id => "id" },
|
||||
);
|
||||
|
||||
1;
|
31
src/Hydra/lib/HydraFrontend/Schema/Buildschedulinginfo.pm
Normal file
31
src/Hydra/lib/HydraFrontend/Schema/Buildschedulinginfo.pm
Normal file
@ -0,0 +1,31 @@
|
||||
package HydraFrontend::Schema::Buildschedulinginfo;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("BuildSchedulingInfo");
|
||||
__PACKAGE__->add_columns(
|
||||
"id",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"priority",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"busy",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"locker",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"logfile",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
);
|
||||
__PACKAGE__->set_primary_key("id");
|
||||
__PACKAGE__->belongs_to("id", "HydraFrontend::Schema::Builds", { id => "id" });
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:vqJ7HEML5YNn5VIXEhZbnw
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
43
src/Hydra/lib/HydraFrontend/Schema/Buildsteps.pm
Normal file
43
src/Hydra/lib/HydraFrontend/Schema/Buildsteps.pm
Normal file
@ -0,0 +1,43 @@
|
||||
package HydraFrontend::Schema::Buildsteps;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("BuildSteps");
|
||||
__PACKAGE__->add_columns(
|
||||
"id",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"stepnr",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"type",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"drvpath",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"outpath",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"logfile",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"busy",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"status",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"errormsg",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"starttime",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"stoptime",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
);
|
||||
__PACKAGE__->set_primary_key("id", "stepnr");
|
||||
__PACKAGE__->belongs_to("id", "HydraFrontend::Schema::Builds", { id => "id" });
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:BuZp6PHq9l/9xyA/x7TOVQ
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
39
src/Hydra/lib/HydraFrontend/Schema/Jobsetinputalts.pm
Normal file
39
src/Hydra/lib/HydraFrontend/Schema/Jobsetinputalts.pm
Normal file
@ -0,0 +1,39 @@
|
||||
package HydraFrontend::Schema::Jobsetinputalts;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("JobsetInputAlts");
|
||||
__PACKAGE__->add_columns(
|
||||
"project",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"jobset",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"input",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"altnr",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"value",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"revision",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"tag",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
);
|
||||
__PACKAGE__->set_primary_key("project", "jobset", "input", "altnr");
|
||||
__PACKAGE__->belongs_to(
|
||||
"jobsetinput",
|
||||
"HydraFrontend::Schema::Jobsetinputs",
|
||||
{ jobset => "jobset", name => "input", project => "project" },
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:x7OCv8YzB2L4H+RxEfwjbg
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
51
src/Hydra/lib/HydraFrontend/Schema/Jobsetinputs.pm
Normal file
51
src/Hydra/lib/HydraFrontend/Schema/Jobsetinputs.pm
Normal file
@ -0,0 +1,51 @@
|
||||
package HydraFrontend::Schema::Jobsetinputs;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("JobsetInputs");
|
||||
__PACKAGE__->add_columns(
|
||||
"project",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"jobset",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"name",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"type",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
);
|
||||
__PACKAGE__->set_primary_key("project", "jobset", "name");
|
||||
__PACKAGE__->has_many(
|
||||
"jobsets",
|
||||
"HydraFrontend::Schema::Jobsets",
|
||||
{
|
||||
"foreign.name" => "self.job",
|
||||
"foreign.nixexprinput" => "self.name",
|
||||
"foreign.project" => "self.project",
|
||||
},
|
||||
);
|
||||
__PACKAGE__->belongs_to(
|
||||
"jobset",
|
||||
"HydraFrontend::Schema::Jobsets",
|
||||
{ name => "jobset", project => "project" },
|
||||
);
|
||||
__PACKAGE__->has_many(
|
||||
"jobsetinputalts",
|
||||
"HydraFrontend::Schema::Jobsetinputalts",
|
||||
{
|
||||
"foreign.input" => "self.name",
|
||||
"foreign.jobset" => "self.jobset",
|
||||
"foreign.project" => "self.project",
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:SKU48+1LqxIcuVY5gaDHCg
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
56
src/Hydra/lib/HydraFrontend/Schema/Jobsets.pm
Normal file
56
src/Hydra/lib/HydraFrontend/Schema/Jobsets.pm
Normal file
@ -0,0 +1,56 @@
|
||||
package HydraFrontend::Schema::Jobsets;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("Jobsets");
|
||||
__PACKAGE__->add_columns(
|
||||
"name",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"project",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"description",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"nixexprinput",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"nixexprpath",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
);
|
||||
__PACKAGE__->set_primary_key("project", "name");
|
||||
__PACKAGE__->has_many(
|
||||
"builds",
|
||||
"HydraFrontend::Schema::Builds",
|
||||
{
|
||||
"foreign.jobset" => "self.name",
|
||||
"foreign.project" => "self.project",
|
||||
},
|
||||
);
|
||||
__PACKAGE__->belongs_to(
|
||||
"project",
|
||||
"HydraFrontend::Schema::Projects",
|
||||
{ name => "project" },
|
||||
);
|
||||
__PACKAGE__->belongs_to(
|
||||
"jobsetinput",
|
||||
"HydraFrontend::Schema::Jobsetinputs",
|
||||
{ job => "name", name => "nixexprinput", project => "project" },
|
||||
);
|
||||
__PACKAGE__->has_many(
|
||||
"jobsetinputs",
|
||||
"HydraFrontend::Schema::Jobsetinputs",
|
||||
{
|
||||
"foreign.jobset" => "self.name",
|
||||
"foreign.project" => "self.project",
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:F3WF5YS/Yas12dK2Gyekpg
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
38
src/Hydra/lib/HydraFrontend/Schema/Projects.pm
Normal file
38
src/Hydra/lib/HydraFrontend/Schema/Projects.pm
Normal file
@ -0,0 +1,38 @@
|
||||
package HydraFrontend::Schema::Projects;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("Projects");
|
||||
__PACKAGE__->add_columns(
|
||||
"name",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"displayname",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"description",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"enabled",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
);
|
||||
__PACKAGE__->set_primary_key("name");
|
||||
__PACKAGE__->has_many(
|
||||
"builds",
|
||||
"HydraFrontend::Schema::Builds",
|
||||
{ "foreign.project" => "self.name" },
|
||||
);
|
||||
__PACKAGE__->has_many(
|
||||
"jobsets",
|
||||
"HydraFrontend::Schema::Jobsets",
|
||||
{ "foreign.project" => "self.name" },
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:M+HA5YEL1oKKTQlLvhb6dw
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
26
src/Hydra/lib/HydraFrontend/View/NixClosure.pm
Normal file
26
src/Hydra/lib/HydraFrontend/View/NixClosure.pm
Normal file
@ -0,0 +1,26 @@
|
||||
package HydraFrontend::View::NixClosure;
|
||||
|
||||
use strict;
|
||||
use base qw/Catalyst::View/;
|
||||
use IO::Pipe;
|
||||
use POSIX qw(dup2);
|
||||
|
||||
sub process {
|
||||
my ( $self, $c ) = @_;
|
||||
|
||||
$c->response->content_type('application/x-nix-export');
|
||||
$c->response->header('Content-Disposition' => 'attachment; filename=' . $c->stash->{name} . '.closure.gz');
|
||||
|
||||
my $storePath = $c->stash->{storePath};
|
||||
|
||||
open(OUTPUT, "nix-store --export `nix-store -qR $storePath` | gzip |");
|
||||
|
||||
my $fh = new IO::Handle;
|
||||
$fh->fdopen(fileno(OUTPUT), "r") or die;
|
||||
|
||||
$c->response->body($fh);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
1;
|
8
src/Hydra/lib/HydraFrontend/View/TT.pm
Normal file
8
src/Hydra/lib/HydraFrontend/View/TT.pm
Normal file
@ -0,0 +1,8 @@
|
||||
package HydraFrontend::View::TT;
|
||||
|
||||
use strict;
|
||||
use base 'Catalyst::View::TT';
|
||||
|
||||
__PACKAGE__->config(TEMPLATE_EXTENSION => '.tt');
|
||||
|
||||
1;
|
Reference in New Issue
Block a user