* Merged the Build and Job tables.
This commit is contained in:
parent
0f24c11292
commit
ecd0ba74e9
|
@ -29,12 +29,17 @@ sub getBuild {
|
|||
sub index :Path :Args(0) {
|
||||
my ( $self, $c ) = @_;
|
||||
$c->stash->{template} = 'index.tt';
|
||||
$c->stash->{jobs} = [$c->model('DB::Jobs')->all];
|
||||
$c->stash->{projects} = [$c->model('DB::Projects')->all];
|
||||
$c->stash->{allBuilds} = [$c->model('DB::Builds')->search(undef, {order_by => "timestamp DESC"})];
|
||||
# Get the latest build for each unique job.
|
||||
# select * from builds as x where timestamp == (select max(timestamp) from builds where jobName == x.jobName);
|
||||
$c->stash->{latestBuilds} = [$c->model('DB::Builds')->search(undef, {order_by => "project, attrName", where => "timestamp == (select max(timestamp) from builds where project == me.project and attrName == me.attrName)"})];
|
||||
$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)"
|
||||
, order_by => "project, attrname"
|
||||
})];
|
||||
}
|
||||
|
||||
|
||||
|
@ -54,7 +59,9 @@ sub job :Local {
|
|||
$c->stash->{template} = 'job.tt';
|
||||
$c->stash->{projectName} = $project;
|
||||
$c->stash->{jobName} = $jobName;
|
||||
$c->stash->{builds} = [$c->model('DB::Builds')->search({project => $project, attrName => $jobName}, {order_by => "timestamp DESC"})];
|
||||
$c->stash->{builds} = [$c->model('DB::Builds')->search(
|
||||
{finished => 1, project => $project, attrName => $jobName},
|
||||
{order_by => "timestamp DESC"})];
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ use base 'DBIx::Class::Schema';
|
|||
__PACKAGE__->load_classes;
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-10 14:25:07
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:dBO/r6lVlITiJ/HlltKcpQ
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1AgCf4sf5h2RU24Slo0sTA
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package HydraFrontend::Schema::Inputs;
|
||||
package HydraFrontend::Schema::Buildinputs;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
@ -6,14 +6,12 @@ use warnings;
|
|||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("inputs");
|
||||
__PACKAGE__->table("BuildInputs");
|
||||
__PACKAGE__->add_columns(
|
||||
"id",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"build",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"job",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"name",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"type",
|
||||
|
@ -33,11 +31,16 @@ __PACKAGE__->add_columns(
|
|||
);
|
||||
__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-10 14:25:07
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:A3Is4VTFkTl2DzrYjzdrZA
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:dKMSSomUN+gJX57Z5e295w
|
||||
|
||||
__PACKAGE__->belongs_to("dependency", "HydraFrontend::Schema::Builds", { id => "dependency" });
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
|
@ -6,7 +6,7 @@ use warnings;
|
|||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("buildLogs");
|
||||
__PACKAGE__->table("BuildLogs");
|
||||
__PACKAGE__->add_columns(
|
||||
"build",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
|
@ -21,8 +21,8 @@ __PACKAGE__->set_primary_key("build", "logphase");
|
|||
__PACKAGE__->belongs_to("build", "HydraFrontend::Schema::Builds", { id => "build" });
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-10 14:25:07
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:eMNna7u2l0ec+OYuvtGRpg
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ZOxJeT+ltgyc/zuDl9aEDQ
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
|
|
|
@ -6,7 +6,7 @@ use warnings;
|
|||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("buildProducts");
|
||||
__PACKAGE__->table("BuildProducts");
|
||||
__PACKAGE__->add_columns(
|
||||
"build",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
|
@ -21,8 +21,8 @@ __PACKAGE__->set_primary_key("build", "path");
|
|||
__PACKAGE__->belongs_to("build", "HydraFrontend::Schema::Builds", { id => "build" });
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-10 14:25:07
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:LaXQ4zxxvzdKFBRVcjMdMQ
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:rZPTilX/PAiIoxffxc0nJw
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
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 },
|
||||
);
|
||||
__PACKAGE__->set_primary_key("id");
|
||||
__PACKAGE__->belongs_to("id", "HydraFrontend::Schema::Builds", { id => "id" });
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:2Vfqs9RUhbDrje18yZb3AA
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
|
@ -6,10 +6,12 @@ use warnings;
|
|||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("builds");
|
||||
__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",
|
||||
|
@ -24,16 +26,6 @@ __PACKAGE__->add_columns(
|
|||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
"outpath",
|
||||
{ data_type => "text", 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 },
|
||||
"system",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
);
|
||||
|
@ -49,10 +41,25 @@ __PACKAGE__->belongs_to(
|
|||
{ name => "jobset", project => "project" },
|
||||
);
|
||||
__PACKAGE__->has_many(
|
||||
"inputs",
|
||||
"HydraFrontend::Schema::Inputs",
|
||||
"buildschedulinginfoes",
|
||||
"HydraFrontend::Schema::Buildschedulinginfo",
|
||||
{ "foreign.id" => "self.id" },
|
||||
);
|
||||
__PACKAGE__->has_many(
|
||||
"buildresultinfoes",
|
||||
"HydraFrontend::Schema::Buildresultinfo",
|
||||
{ "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",
|
||||
|
@ -65,9 +72,23 @@ __PACKAGE__->has_many(
|
|||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-10 14:25:07
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:C1XPkCXQImyXduKER0Dllg
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1GZeB3YVr064AZrGargmFg
|
||||
|
||||
__PACKAGE__->has_many(dependents => 'HydraFrontend::Schema::Inputs', 'dependency');
|
||||
__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;
|
||||
|
|
|
@ -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-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:rN7v2+MnC8TkrEHUzt2Gqg
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
|
@ -1,58 +0,0 @@
|
|||
package HydraFrontend::Schema::Jobs;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("jobs");
|
||||
__PACKAGE__->add_columns(
|
||||
"id",
|
||||
{ data_type => "integer", is_nullable => 0, size => undef },
|
||||
"timestamp",
|
||||
{ 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 },
|
||||
"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 },
|
||||
"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" },
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-10 14:25:07
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ZF8UB1MtbPuOk7wTSFJR5Q
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"inputs",
|
||||
"HydraFrontend::Schema::Inputs",
|
||||
{ "foreign.job" => "self.id" },
|
||||
);
|
||||
|
||||
1;
|
|
@ -6,7 +6,7 @@ use warnings;
|
|||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("jobsetInputAlts");
|
||||
__PACKAGE__->table("JobsetInputAlts");
|
||||
__PACKAGE__->add_columns(
|
||||
"project",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
|
@ -33,8 +33,8 @@ __PACKAGE__->belongs_to(
|
|||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-10 14:25:07
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ibTncC1AslPWt1eiTtwplA
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:bvEulSFMDlAMs39sIyHgZQ
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
|
|
|
@ -6,7 +6,7 @@ use warnings;
|
|||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("jobsetInputs");
|
||||
__PACKAGE__->table("JobsetInputs");
|
||||
__PACKAGE__->add_columns(
|
||||
"project",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
|
@ -43,8 +43,8 @@ __PACKAGE__->has_many(
|
|||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-10 14:25:07
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:D1UzSZwPtwDmOI7q6g8uKQ
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:54xK3D1D0Jm5oKgRelXN7Q
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
|
|
|
@ -6,7 +6,7 @@ use warnings;
|
|||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("jobsets");
|
||||
__PACKAGE__->table("Jobsets");
|
||||
__PACKAGE__->add_columns(
|
||||
"name",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
|
@ -46,18 +46,10 @@ __PACKAGE__->has_many(
|
|||
"foreign.project" => "self.project",
|
||||
},
|
||||
);
|
||||
__PACKAGE__->has_many(
|
||||
"jobs",
|
||||
"HydraFrontend::Schema::Jobs",
|
||||
{
|
||||
"foreign.jobset" => "self.name",
|
||||
"foreign.project" => "self.project",
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-10 14:25:07
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:6Pyrgervmq03S5Nx8QfA1Q
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:JHirlq7Jc8dQOy+Op/VflA
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
|
|
|
@ -6,7 +6,7 @@ use warnings;
|
|||
use base 'DBIx::Class';
|
||||
|
||||
__PACKAGE__->load_components("Core");
|
||||
__PACKAGE__->table("projects");
|
||||
__PACKAGE__->table("Projects");
|
||||
__PACKAGE__->add_columns(
|
||||
"name",
|
||||
{ data_type => "text", is_nullable => 0, size => undef },
|
||||
|
@ -22,15 +22,10 @@ __PACKAGE__->has_many(
|
|||
"HydraFrontend::Schema::Jobsets",
|
||||
{ "foreign.project" => "self.name" },
|
||||
);
|
||||
__PACKAGE__->has_many(
|
||||
"jobs",
|
||||
"HydraFrontend::Schema::Jobs",
|
||||
{ "foreign.project" => "self.name" },
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-10 14:25:07
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:p8LbF31qRl/JfMK5wfkeCg
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:7Ag5ZfYVgfw3MJZkNUmBYw
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
[% WRAPPER layout.tt title="Hydra Overview" %]
|
||||
[% USE date %]
|
||||
|
||||
<h1>Job <tt>[% build.project.name %]:[% build.attrname %]</tt> build [% id %]</h1>
|
||||
<h1>
|
||||
Job <tt>[% build.project.name %]:[% build.attrname %]</tt> build [% id %]
|
||||
[% IF !build.finished %]
|
||||
(scheduled)
|
||||
[% END %]
|
||||
</h1>
|
||||
|
||||
|
||||
<h2>Information</h2>
|
||||
|
@ -11,6 +16,10 @@
|
|||
<th>Build ID:</th>
|
||||
<td>[% build.id %]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Time added:</th>
|
||||
<td>[% date.format(build.timestamp, '%Y-%m-%d %H:%M:%S') %]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Project:</th>
|
||||
<td><a href="[% c.uri_for('/project' build.project.name) %]"><tt>[% build.project.name %]</tt></a></td>
|
||||
|
@ -28,26 +37,8 @@
|
|||
<td>[% build.description %]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Time added:</th>
|
||||
<td>[% date.format(build.timestamp, '%Y-%m-%d %H:%M:%S') %]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Build started:</th>
|
||||
<td>[% IF build.starttime %][% date.format(build.starttime, '%Y-%m-%d %H:%M:%S') %][% ELSE %]<em>(cached build)</em>[% END %]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Build finished:</th>
|
||||
<td>[% IF build.stoptime %][% date.format(build.stoptime, '%Y-%m-%d %H:%M:%S') %][% ELSE %]<em>(cached build)</em>[% END %]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Duration (seconds):</th>
|
||||
<td>
|
||||
[% IF build.iscachedbuild %]
|
||||
<em>(cached build)</em>
|
||||
[% ELSE %]
|
||||
[% build.stoptime - build.starttime %]
|
||||
[% END %]
|
||||
</td>
|
||||
<th>System:</th>
|
||||
<td><tt>[% build.system %]</tt></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Derivation store path:</th>
|
||||
|
@ -57,20 +48,41 @@
|
|||
<th>Output store path:</th>
|
||||
<td><tt>[% build.outpath %]</tt></td>
|
||||
</tr>
|
||||
[% IF build.finished %]
|
||||
<tr>
|
||||
<th>System:</th>
|
||||
<td><tt>[% build.system %]</tt></td>
|
||||
<th>Build started:</th>
|
||||
<td>[% IF build.resultInfo.starttime %][% date.format(build.resultInfo.starttime, '%Y-%m-%d %H:%M:%S') %][% ELSE %]<em>(cached build)</em>[% END %]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Build finished:</th>
|
||||
<td>[% IF build.resultInfo.stoptime %][% date.format(build.resultInfo.stoptime, '%Y-%m-%d %H:%M:%S') %][% ELSE %]<em>(cached build)</em>[% END %]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Duration (seconds):</th>
|
||||
<td>
|
||||
[% IF build.resultInfo.iscachedbuild %]
|
||||
<em>(cached build)</em>
|
||||
[% ELSE %]
|
||||
[% build.resultInfo.stoptime - build.resultInfo.starttime %]
|
||||
[% END %]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Status:</th>
|
||||
<td>
|
||||
[% IF build.buildstatus == 0 %]
|
||||
[% IF build.resultInfo.buildstatus == 0 %]
|
||||
<img src="/static/images/success.gif" />
|
||||
[% ELSE %]
|
||||
<img src="/static/images/failure.gif" />
|
||||
[% END %]
|
||||
</td>
|
||||
</tr>
|
||||
[% ELSE %]
|
||||
<tr>
|
||||
<th>Priority:</th>
|
||||
<td>[% build.schedulingInfo.priority %]</td>
|
||||
</tr>
|
||||
[% END %]
|
||||
</table>
|
||||
|
||||
|
||||
|
@ -101,6 +113,8 @@
|
|||
</table>
|
||||
|
||||
|
||||
[% IF build.finished %]
|
||||
|
||||
<h2>Build products</h2>
|
||||
|
||||
<ul class="productList">
|
||||
|
@ -167,3 +181,6 @@
|
|||
|
||||
[% END %]
|
||||
|
||||
|
||||
[% END %]
|
||||
|
||||
|
|
|
@ -8,15 +8,15 @@
|
|||
<tr><th>#</th><th>Priority</th><th>Project</th><th>Job</th><th>System</th><th>Timestamp</th><th>Description</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
[% FOREACH job IN jobs -%]
|
||||
<tr [% IF job.busy %]class="runningJob"[% END %] >
|
||||
<td>[% job.id %]</td>
|
||||
<td>[% job.priority %]</td>
|
||||
<td><tt>[% job.project.name %]</tt></td>
|
||||
<td><tt>[% job.jobset.name %]</tt></td>
|
||||
<td><tt>[% job.system %]</tt></td>
|
||||
<td>[% date.format(job.timestamp, '%Y-%m-%d %H:%M:%S') %]</td>
|
||||
<td>[% job.description %]</td>
|
||||
[% FOREACH build IN scheduled -%]
|
||||
<tr [% IF build.schedulingInfo.busy %]class="runningJob"[% END %] >
|
||||
<td><a href="[% c.uri_for('/build' build.id) %]">[% build.id %]</a></td>
|
||||
<td>[% build.schedulingInfo.priority %]</td>
|
||||
<td><tt>[% build.project.name %]</tt></td>
|
||||
<td><tt>[% build.jobset.name %]</tt></td>
|
||||
<td><tt>[% build.system %]</tt></td>
|
||||
<td>[% date.format(build.timestamp, '%Y-%m-%d %H:%M:%S') %]</td>
|
||||
<td>[% build.description %]</td>
|
||||
</tr>
|
||||
[% END -%]
|
||||
</tbody>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<tr>
|
||||
<td>
|
||||
[% IF build.buildstatus == 0 %]
|
||||
[% IF build.resultInfo.buildstatus == 0 %]
|
||||
<img src="/static/images/success.gif" />
|
||||
[% ELSE %]
|
||||
<img src="/static/images/failure.gif" />
|
||||
|
|
77
src/build.pl
77
src/build.pl
|
@ -16,11 +16,11 @@ sub isValidPath {
|
|||
}
|
||||
|
||||
|
||||
sub buildJob {
|
||||
my ($job) = @_;
|
||||
sub doBuild {
|
||||
my ($build) = @_;
|
||||
|
||||
my $drvPath = $job->drvpath;
|
||||
my $outPath = $job->outpath;
|
||||
my $drvPath = $build->drvpath;
|
||||
my $outPath = $build->outpath;
|
||||
|
||||
my $isCachedBuild = 1;
|
||||
my $outputCreated = 1; # i.e., the Nix build succeeded (but it could be a positive failure)
|
||||
|
@ -52,27 +52,17 @@ sub buildJob {
|
|||
}
|
||||
|
||||
$db->txn_do(sub {
|
||||
my $build = $db->resultset('Builds')->create(
|
||||
{ timestamp => time()
|
||||
, project => $job->project->name
|
||||
, jobset => $job->jobset->name
|
||||
, attrname => $job->attrname
|
||||
, description => $job->description
|
||||
, drvpath => $drvPath
|
||||
, outpath => $outPath
|
||||
$build->finished(1);
|
||||
$build->timestamp(time());
|
||||
$build->update;
|
||||
|
||||
$db->resultset('Buildresultinfo')->create(
|
||||
{ id => $build->id
|
||||
, iscachedbuild => $isCachedBuild
|
||||
, buildstatus => $buildStatus
|
||||
, starttime => $startTime
|
||||
, stoptime => $stopTime
|
||||
, system => $job->system
|
||||
});
|
||||
print " build ID = ", $build->id, "\n";
|
||||
|
||||
foreach my $input ($job->inputs) {
|
||||
$input->job(undef);
|
||||
$input->build($build->id);
|
||||
$input->update;
|
||||
}
|
||||
|
||||
my $logPath = "/nix/var/log/nix/drvs/" . basename $drvPath;
|
||||
if (-e $logPath) {
|
||||
|
@ -125,47 +115,44 @@ sub buildJob {
|
|||
}
|
||||
}
|
||||
|
||||
$job->delete;
|
||||
|
||||
$build->schedulingInfo->delete;
|
||||
});
|
||||
|
||||
print "STOP ", time, "\n";
|
||||
}
|
||||
|
||||
|
||||
my $jobId = $ARGV[0] or die;
|
||||
print "building job $jobId\n";
|
||||
my $buildId = $ARGV[0] or die;
|
||||
print "performing build $buildId\n";
|
||||
|
||||
# Lock the job. If necessary, steal the lock from the parent process
|
||||
# (runner.pl). This is so that if the runner dies, the children
|
||||
# (i.e. the job builders) can continue to run and won't have the lock
|
||||
# taken away.
|
||||
my $job;
|
||||
# Lock the build. If necessary, steal the lock from the parent
|
||||
# process (runner.pl). This is so that if the runner dies, the
|
||||
# children (i.e. the build.pl instances) can continue to run and won't
|
||||
# have the lock taken away.
|
||||
my $build;
|
||||
$db->txn_do(sub {
|
||||
($job) = $db->resultset('Jobs')->search({ id => $jobId });
|
||||
die "job $jobId doesn't exist" unless defined $job;
|
||||
if ($job->busy != 0 && $job->locker != getppid) {
|
||||
die "job $jobId is already being built";
|
||||
($build) = $db->resultset('Builds')->search({id => $buildId});
|
||||
die "build $buildId doesn't exist" unless defined $build;
|
||||
if ($build->schedulingInfo->busy != 0 && $build->schedulingInfo->locker != getppid) {
|
||||
die "build $buildId is already being built";
|
||||
}
|
||||
$job->busy(1);
|
||||
$job->locker($$);
|
||||
$job->update;
|
||||
$build->schedulingInfo->busy(1);
|
||||
$build->schedulingInfo->locker($$);
|
||||
$build->schedulingInfo->update;
|
||||
});
|
||||
|
||||
die unless $job;
|
||||
die unless $build;
|
||||
|
||||
# Build the job. If it throws an error, unlock the job so that it can
|
||||
# be retried.
|
||||
# Do the build. If it throws an error, unlock the build so that it
|
||||
# can be retried.
|
||||
eval {
|
||||
print "BUILD\n";
|
||||
buildJob $job;
|
||||
doBuild $build;
|
||||
print "DONE\n";
|
||||
};
|
||||
if ($@) {
|
||||
warn $@;
|
||||
$db->txn_do(sub {
|
||||
$job->busy(0);
|
||||
$job->locker($$);
|
||||
$job->update;
|
||||
$build->schedulingInfo->busy(0);
|
||||
$build->schedulingInfo->locker($$);
|
||||
$build->schedulingInfo->update;
|
||||
});
|
||||
}
|
||||
|
|
125
src/hydra.sql
125
src/hydra.sql
|
@ -1,6 +1,15 @@
|
|||
create table builds (
|
||||
-- This table contains all builds, either scheduled or finished. For
|
||||
-- scheduled builds, additional info (such as the priority) can be
|
||||
-- found in the BuildSchedulingInfo table. For finished builds,
|
||||
-- additional info (such as the logs, build products, etc.) can be
|
||||
-- found in several tables, such as BuildResultInfo, BuildLogs and
|
||||
-- BuildProducts.
|
||||
create table Builds (
|
||||
id integer primary key autoincrement not null,
|
||||
timestamp integer not null, -- time this build was added to the db (in Unix time)
|
||||
|
||||
finished integer not null, -- 0 = scheduled, 1 = finished
|
||||
|
||||
timestamp integer not null, -- time this build was scheduled / finished building
|
||||
|
||||
-- Info about the inputs.
|
||||
project text not null, -- !!! foreign key
|
||||
|
@ -11,25 +20,51 @@ create table builds (
|
|||
description text,
|
||||
drvPath text not null,
|
||||
outPath text not null,
|
||||
isCachedBuild integer not null, -- boolean
|
||||
buildStatus integer, -- 0 = succeeded, 1 = Nix build failure, 2 = positive build failure
|
||||
errorMsg text, -- error message in case of a Nix failure
|
||||
startTime integer, -- in Unix time, 0 = used cached build result
|
||||
stopTime integer,
|
||||
system text not null,
|
||||
|
||||
foreign key (project) references projects(name), -- ignored by sqlite
|
||||
foreign key (project, jobset) references jobsets(project, name) -- ignored by sqlite
|
||||
|
||||
foreign key (project) references Projects(name), -- ignored by sqlite
|
||||
foreign key (project, jobset) references Jobsets(project, name) -- ignored by sqlite
|
||||
);
|
||||
|
||||
|
||||
-- Inputs of jobs/builds.
|
||||
create table inputs (
|
||||
-- Info for a scheduled build.
|
||||
create table BuildSchedulingInfo (
|
||||
id integer primary key not null,
|
||||
|
||||
priority integer not null default 0,
|
||||
|
||||
busy integer not null default 0, -- true means someone is building this job now
|
||||
locker text not null default '', -- !!! hostname/pid of the process building this job?
|
||||
|
||||
logfile text, -- if busy, the path of the logfile
|
||||
|
||||
foreign key (id) references Builds(id) on delete cascade -- ignored by sqlite
|
||||
);
|
||||
|
||||
|
||||
-- Info for a finished build.
|
||||
create table BuildResultInfo (
|
||||
id integer primary key not null,
|
||||
|
||||
isCachedBuild integer not null, -- boolean
|
||||
|
||||
buildStatus integer, -- 0 = succeeded, 1 = Nix build failure, 2 = positive build failure
|
||||
|
||||
errorMsg text, -- error message in case of a Nix failure
|
||||
|
||||
startTime integer, -- in Unix time, 0 = used cached build result
|
||||
stopTime integer,
|
||||
|
||||
foreign key (id) references Builds(id) on delete cascade -- ignored by sqlite
|
||||
);
|
||||
|
||||
|
||||
-- Inputs of builds.
|
||||
create table BuildInputs (
|
||||
id integer primary key autoincrement not null,
|
||||
|
||||
-- Which job or build this input belongs to. Exactly one must be non-null.
|
||||
-- Which build this input belongs to.
|
||||
build integer,
|
||||
job integer,
|
||||
|
||||
-- Copied from the jobsetinputs from which the build was created.
|
||||
name text not null,
|
||||
|
@ -42,29 +77,28 @@ create table inputs (
|
|||
|
||||
path text,
|
||||
|
||||
foreign key (build) references builds(id) -- ignored by sqlite
|
||||
foreign key (job) references jobs(id) -- ignored by sqlite
|
||||
foreign key (dependency) references builds(id) -- ignored by sqlite
|
||||
foreign key (build) references Builds(id) on delete cascade, -- ignored by sqlite
|
||||
foreign key (dependency) references Builds(id) -- ignored by sqlite
|
||||
);
|
||||
|
||||
|
||||
create table buildProducts (
|
||||
create table BuildProducts (
|
||||
build integer not null,
|
||||
path text not null,
|
||||
type text not null, -- "nix-build", "file", "doc", "report", ...
|
||||
subtype text not null, -- "source-dist", "rpm", ...
|
||||
primary key (build, path),
|
||||
foreign key (build) references builds(id) on delete cascade -- ignored by sqlite
|
||||
foreign key (build) references Builds(id) on delete cascade -- ignored by sqlite
|
||||
);
|
||||
|
||||
|
||||
create table buildLogs (
|
||||
create table BuildLogs (
|
||||
build integer not null,
|
||||
logPhase text not null,
|
||||
path text not null,
|
||||
type text not null,
|
||||
primary key (build, logPhase),
|
||||
foreign key (build) references builds(id) on delete cascade -- ignored by sqlite
|
||||
foreign key (build) references Builds(id) on delete cascade -- ignored by sqlite
|
||||
);
|
||||
|
||||
|
||||
|
@ -72,13 +106,15 @@ create table buildLogs (
|
|||
create trigger cascadeBuildDeletion
|
||||
before delete on builds
|
||||
for each row begin
|
||||
--delete from buildInputs where build = old.id;
|
||||
delete from buildLogs where build = old.id;
|
||||
delete from buildProducts where build = old.id;
|
||||
delete from BuildSchedulingInfo where id = old.id;
|
||||
delete from BuildResultInfo where id = old.id;
|
||||
delete from BuildInputs where build = old.id;
|
||||
delete from BuildLogs where build = old.id;
|
||||
delete from BuildProducts where build = old.id;
|
||||
end;
|
||||
|
||||
|
||||
create table projects (
|
||||
create table Projects (
|
||||
name text primary key not null
|
||||
);
|
||||
|
||||
|
@ -86,29 +122,29 @@ create table projects (
|
|||
-- A jobset consists of a set of inputs (e.g. SVN repositories), one
|
||||
-- of which contains a Nix expression containing an attribute set
|
||||
-- describing build jobs.
|
||||
create table jobsets (
|
||||
create table Jobsets (
|
||||
name text not null,
|
||||
project text not null,
|
||||
description text,
|
||||
nixExprInput text not null, -- name of the jobsetInput containing the Nix expression
|
||||
nixExprPath text not null, -- relative path of the Nix expression
|
||||
primary key (project, name),
|
||||
foreign key (project) references projects(name) on delete cascade, -- ignored by sqlite
|
||||
foreign key (project, name, nixExprInput) references jobsetInputs(project, job, name)
|
||||
foreign key (project) references Projects(name) on delete cascade, -- ignored by sqlite
|
||||
foreign key (project, name, nixExprInput) references JobsetInputs(project, job, name)
|
||||
);
|
||||
|
||||
|
||||
create table jobsetInputs (
|
||||
create table JobsetInputs (
|
||||
project text not null,
|
||||
jobset text not null,
|
||||
name text not null,
|
||||
type text not null, -- "svn", "cvs", "path", "file", "string"
|
||||
primary key (project, jobset, name),
|
||||
foreign key (project, jobset) references jobsets(project, name) on delete cascade -- ignored by sqlite
|
||||
foreign key (project, jobset) references Jobsets(project, name) on delete cascade -- ignored by sqlite
|
||||
);
|
||||
|
||||
|
||||
create table jobsetInputAlts (
|
||||
create table JobsetInputAlts (
|
||||
project text not null,
|
||||
jobset text not null,
|
||||
input text not null,
|
||||
|
@ -121,30 +157,5 @@ create table jobsetInputAlts (
|
|||
value text, -- for type == 'string'
|
||||
|
||||
primary key (project, jobset, input, altnr),
|
||||
foreign key (project, jobset, input) references jobsetInputs(project, jobset, name) on delete cascade -- ignored by sqlite
|
||||
);
|
||||
|
||||
|
||||
create table jobs (
|
||||
id integer primary key autoincrement not null,
|
||||
timestamp integer not null, -- time this build was added to the db (in Unix time)
|
||||
|
||||
priority integer not null default 0,
|
||||
|
||||
busy integer not null default 0, -- true means someone is building this job now
|
||||
locker text not null default '', -- !!! hostname/pid of the process building this job?
|
||||
|
||||
-- Info about the inputs.
|
||||
project text not null, -- !!! foreign key
|
||||
jobset text not null, -- !!! foreign key
|
||||
attrName text not null,
|
||||
|
||||
-- What this job will build.
|
||||
description text,
|
||||
drvPath text not null,
|
||||
outPath text not null,
|
||||
system text not null,
|
||||
|
||||
foreign key (project) references projects(name), -- ignored by sqlite
|
||||
foreign key (project, jobset) references jobsets(project, name) -- ignored by sqlite
|
||||
foreign key (project, jobset, input) references JobsetInputs(project, jobset, name) on delete cascade -- ignored by sqlite
|
||||
);
|
||||
|
|
|
@ -12,14 +12,16 @@ $db->storage->dbh->do("PRAGMA synchronous = OFF;");
|
|||
|
||||
# Unlock jobs whose building process has died.
|
||||
$db->txn_do(sub {
|
||||
my @jobs = $db->resultset('Jobs')->search({ busy => 1 });
|
||||
my @jobs = $db->resultset('Builds')->search(
|
||||
{finished => 0, busy => 1}, {join => 'schedulingInfo'});
|
||||
foreach my $job (@jobs) {
|
||||
my $pid = $job->locker;
|
||||
print $job, "\n";
|
||||
my $pid = $job->schedulingInfo->locker;
|
||||
if (kill(0, $pid) != 1) { # see if we can signal the process
|
||||
print "job ", $job->id, " pid $pid died, unlocking\n";
|
||||
$job->busy(0);
|
||||
$job->locker("");
|
||||
$job->update;
|
||||
$job->schedulingInfo->busy(0);
|
||||
$job->schedulingInfo->locker("");
|
||||
$job->schedulingInfo->update;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -32,15 +34,17 @@ sub checkJobs {
|
|||
|
||||
$db->txn_do(sub {
|
||||
|
||||
my @jobs = $db->resultset('Jobs')->search({ busy => 0 }, {order_by => ["priority", "timestamp"]});
|
||||
my @jobs = $db->resultset('Builds')->search(
|
||||
{finished => 0, busy => 0},
|
||||
{join => 'schedulingInfo', order_by => ["priority", "timestamp"]});
|
||||
|
||||
print "# of available jobs: ", scalar(@jobs), "\n";
|
||||
|
||||
if (scalar @jobs > 0) {
|
||||
$job = $jobs[0];
|
||||
$job->busy(1);
|
||||
$job->locker($$);
|
||||
$job->update;
|
||||
$job->schedulingInfo->busy(1);
|
||||
$job->schedulingInfo->locker($$);
|
||||
$job->schedulingInfo->update;
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -66,9 +70,9 @@ sub checkJobs {
|
|||
if ($@) {
|
||||
warn $@;
|
||||
$db->txn_do(sub {
|
||||
$job->busy(0);
|
||||
$job->locker($$);
|
||||
$job->update;
|
||||
$job->schedulingInfo->busy(0);
|
||||
$job->schedulingInfo->locker($$);
|
||||
$job->schedulingInfo->update;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,24 +67,15 @@ sub checkJob {
|
|||
{ project => $project->name, jobset => $jobset->name
|
||||
, attrname => $jobName, outPath => $outPath })) > 0)
|
||||
{
|
||||
print " already done\n";
|
||||
print " already scheduled/done\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (scalar($db->resultset('Jobs')->search(
|
||||
{ project => $project->name, jobset => $jobset->name
|
||||
, attrname => $jobName, outPath => $outPath })) > 0)
|
||||
{
|
||||
print " already queued\n";
|
||||
return;
|
||||
}
|
||||
|
||||
print " adding to queue\n";
|
||||
my $job = $db->resultset('Jobs')->create(
|
||||
{ timestamp => time()
|
||||
, priority => 0
|
||||
, busy => 0
|
||||
, locker => ""
|
||||
|
||||
my $build = $db->resultset('Builds')->create(
|
||||
{ finished => 0
|
||||
, timestamp => time()
|
||||
, project => $project->name
|
||||
, jobset => $jobset->name
|
||||
, attrname => $jobName
|
||||
|
@ -94,10 +85,17 @@ sub checkJob {
|
|||
, system => $job->{system}
|
||||
});
|
||||
|
||||
$db->resultset('Buildschedulinginfo')->create(
|
||||
{ id => $build->id
|
||||
, priority => 0
|
||||
, busy => 0
|
||||
, locker => ""
|
||||
});
|
||||
|
||||
foreach my $inputName (keys %{$inputInfo}) {
|
||||
my $input = $inputInfo->{$inputName};
|
||||
$db->resultset('Inputs')->create(
|
||||
{ job => $job->id
|
||||
$db->resultset('Buildinputs')->create(
|
||||
{ build => $build->id
|
||||
, name => $inputName
|
||||
, type => $input->{type}
|
||||
, uri => $input->{uri}
|
||||
|
@ -151,8 +149,8 @@ sub checkJobAlternatives {
|
|||
else {
|
||||
|
||||
(my $prevBuild) = $db->resultset('Builds')->search(
|
||||
{project => $project->name, jobset => $jobset->name, attrname => $argName, buildStatus => 0},
|
||||
{order_by => "timestamp DESC", rows => 1});
|
||||
{finished => 1, project => $project->name, jobset => $jobset->name, attrname => $argName, buildStatus => 0},
|
||||
{join => 'resultInfo', order_by => "timestamp DESC", rows => 1});
|
||||
|
||||
if (!defined $prevBuild) {
|
||||
# !!! reschedule?
|
||||
|
|
Loading…
Reference in a new issue