* Merged the Build and Job tables.

This commit is contained in:
Eelco Dolstra 2008-11-11 12:54:37 +00:00
parent 0f24c11292
commit ecd0ba74e9
20 changed files with 327 additions and 286 deletions

View file

@ -29,12 +29,17 @@ sub getBuild {
sub index :Path :Args(0) { sub index :Path :Args(0) {
my ( $self, $c ) = @_; my ( $self, $c ) = @_;
$c->stash->{template} = 'index.tt'; $c->stash->{template} = 'index.tt';
$c->stash->{jobs} = [$c->model('DB::Jobs')->all];
$c->stash->{projects} = [$c->model('DB::Projects')->all]; $c->stash->{projects} = [$c->model('DB::Projects')->all];
$c->stash->{allBuilds} = [$c->model('DB::Builds')->search(undef, {order_by => "timestamp DESC"})]; $c->stash->{scheduled} = [$c->model('DB::Builds')->search(
# Get the latest build for each unique job. {finished => 0}, {join => 'schedulingInfo'})]; # !!!
# select * from builds as x where timestamp == (select max(timestamp) from builds where jobName == x.jobName); $c->stash->{allBuilds} = [$c->model('DB::Builds')->search(
$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)"})]; {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->{template} = 'job.tt';
$c->stash->{projectName} = $project; $c->stash->{projectName} = $project;
$c->stash->{jobName} = $jobName; $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"})];
} }

View file

@ -8,8 +8,8 @@ use base 'DBIx::Class::Schema';
__PACKAGE__->load_classes; __PACKAGE__->load_classes;
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-10 14:25:07 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:dBO/r6lVlITiJ/HlltKcpQ # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1AgCf4sf5h2RU24Slo0sTA
# You can replace this text with custom content, and it will be preserved on regeneration # You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -1,4 +1,4 @@
package HydraFrontend::Schema::Inputs; package HydraFrontend::Schema::Buildinputs;
use strict; use strict;
use warnings; use warnings;
@ -6,14 +6,12 @@ use warnings;
use base 'DBIx::Class'; use base 'DBIx::Class';
__PACKAGE__->load_components("Core"); __PACKAGE__->load_components("Core");
__PACKAGE__->table("inputs"); __PACKAGE__->table("BuildInputs");
__PACKAGE__->add_columns( __PACKAGE__->add_columns(
"id", "id",
{ data_type => "integer", is_nullable => 0, size => undef }, { data_type => "integer", is_nullable => 0, size => undef },
"build", "build",
{ data_type => "integer", is_nullable => 0, size => undef }, { data_type => "integer", is_nullable => 0, size => undef },
"job",
{ data_type => "integer", is_nullable => 0, size => undef },
"name", "name",
{ data_type => "text", is_nullable => 0, size => undef }, { data_type => "text", is_nullable => 0, size => undef },
"type", "type",
@ -33,11 +31,16 @@ __PACKAGE__->add_columns(
); );
__PACKAGE__->set_primary_key("id"); __PACKAGE__->set_primary_key("id");
__PACKAGE__->belongs_to("build", "HydraFrontend::Schema::Builds", { id => "build" }); __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 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:A3Is4VTFkTl2DzrYjzdrZA # 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; 1;

View file

@ -6,7 +6,7 @@ use warnings;
use base 'DBIx::Class'; use base 'DBIx::Class';
__PACKAGE__->load_components("Core"); __PACKAGE__->load_components("Core");
__PACKAGE__->table("buildLogs"); __PACKAGE__->table("BuildLogs");
__PACKAGE__->add_columns( __PACKAGE__->add_columns(
"build", "build",
{ data_type => "integer", is_nullable => 0, size => undef }, { 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" }); __PACKAGE__->belongs_to("build", "HydraFrontend::Schema::Builds", { id => "build" });
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-10 14:25:07 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:eMNna7u2l0ec+OYuvtGRpg # 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 # You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -6,7 +6,7 @@ use warnings;
use base 'DBIx::Class'; use base 'DBIx::Class';
__PACKAGE__->load_components("Core"); __PACKAGE__->load_components("Core");
__PACKAGE__->table("buildProducts"); __PACKAGE__->table("BuildProducts");
__PACKAGE__->add_columns( __PACKAGE__->add_columns(
"build", "build",
{ data_type => "integer", is_nullable => 0, size => undef }, { 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" }); __PACKAGE__->belongs_to("build", "HydraFrontend::Schema::Builds", { id => "build" });
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-10 14:25:07 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:LaXQ4zxxvzdKFBRVcjMdMQ # 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 # You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -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;

View file

@ -6,10 +6,12 @@ use warnings;
use base 'DBIx::Class'; use base 'DBIx::Class';
__PACKAGE__->load_components("Core"); __PACKAGE__->load_components("Core");
__PACKAGE__->table("builds"); __PACKAGE__->table("Builds");
__PACKAGE__->add_columns( __PACKAGE__->add_columns(
"id", "id",
{ data_type => "integer", is_nullable => 0, size => undef }, { data_type => "integer", is_nullable => 0, size => undef },
"finished",
{ data_type => "integer", is_nullable => 0, size => undef },
"timestamp", "timestamp",
{ data_type => "integer", is_nullable => 0, size => undef }, { data_type => "integer", is_nullable => 0, size => undef },
"project", "project",
@ -24,16 +26,6 @@ __PACKAGE__->add_columns(
{ data_type => "text", is_nullable => 0, size => undef }, { data_type => "text", is_nullable => 0, size => undef },
"outpath", "outpath",
{ data_type => "text", is_nullable => 0, size => undef }, { 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", "system",
{ data_type => "text", is_nullable => 0, size => undef }, { data_type => "text", is_nullable => 0, size => undef },
); );
@ -49,10 +41,25 @@ __PACKAGE__->belongs_to(
{ name => "jobset", project => "project" }, { name => "jobset", project => "project" },
); );
__PACKAGE__->has_many( __PACKAGE__->has_many(
"inputs", "buildschedulinginfoes",
"HydraFrontend::Schema::Inputs", "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" }, { "foreign.build" => "self.id" },
); );
__PACKAGE__->has_many(
"buildinputs_dependencies",
"HydraFrontend::Schema::Buildinputs",
{ "foreign.dependency" => "self.id" },
);
__PACKAGE__->has_many( __PACKAGE__->has_many(
"buildproducts", "buildproducts",
"HydraFrontend::Schema::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 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:C1XPkCXQImyXduKER0Dllg # 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; 1;

View 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-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;

View file

@ -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;

View file

@ -6,7 +6,7 @@ use warnings;
use base 'DBIx::Class'; use base 'DBIx::Class';
__PACKAGE__->load_components("Core"); __PACKAGE__->load_components("Core");
__PACKAGE__->table("jobsetInputAlts"); __PACKAGE__->table("JobsetInputAlts");
__PACKAGE__->add_columns( __PACKAGE__->add_columns(
"project", "project",
{ data_type => "text", is_nullable => 0, size => undef }, { 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 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ibTncC1AslPWt1eiTtwplA # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:bvEulSFMDlAMs39sIyHgZQ
# You can replace this text with custom content, and it will be preserved on regeneration # You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -6,7 +6,7 @@ use warnings;
use base 'DBIx::Class'; use base 'DBIx::Class';
__PACKAGE__->load_components("Core"); __PACKAGE__->load_components("Core");
__PACKAGE__->table("jobsetInputs"); __PACKAGE__->table("JobsetInputs");
__PACKAGE__->add_columns( __PACKAGE__->add_columns(
"project", "project",
{ data_type => "text", is_nullable => 0, size => undef }, { 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 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:D1UzSZwPtwDmOI7q6g8uKQ # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:54xK3D1D0Jm5oKgRelXN7Q
# You can replace this text with custom content, and it will be preserved on regeneration # You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -6,7 +6,7 @@ use warnings;
use base 'DBIx::Class'; use base 'DBIx::Class';
__PACKAGE__->load_components("Core"); __PACKAGE__->load_components("Core");
__PACKAGE__->table("jobsets"); __PACKAGE__->table("Jobsets");
__PACKAGE__->add_columns( __PACKAGE__->add_columns(
"name", "name",
{ data_type => "text", is_nullable => 0, size => undef }, { data_type => "text", is_nullable => 0, size => undef },
@ -46,18 +46,10 @@ __PACKAGE__->has_many(
"foreign.project" => "self.project", "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 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:6Pyrgervmq03S5Nx8QfA1Q # 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 # You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -6,7 +6,7 @@ use warnings;
use base 'DBIx::Class'; use base 'DBIx::Class';
__PACKAGE__->load_components("Core"); __PACKAGE__->load_components("Core");
__PACKAGE__->table("projects"); __PACKAGE__->table("Projects");
__PACKAGE__->add_columns( __PACKAGE__->add_columns(
"name", "name",
{ data_type => "text", is_nullable => 0, size => undef }, { data_type => "text", is_nullable => 0, size => undef },
@ -22,15 +22,10 @@ __PACKAGE__->has_many(
"HydraFrontend::Schema::Jobsets", "HydraFrontend::Schema::Jobsets",
{ "foreign.project" => "self.name" }, { "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 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:p8LbF31qRl/JfMK5wfkeCg # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:7Ag5ZfYVgfw3MJZkNUmBYw
# You can replace this text with custom content, and it will be preserved on regeneration # You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -1,7 +1,12 @@
[% WRAPPER layout.tt title="Hydra Overview" %] [% WRAPPER layout.tt title="Hydra Overview" %]
[% USE date %] [% 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> <h2>Information</h2>
@ -11,6 +16,10 @@
<th>Build ID:</th> <th>Build ID:</th>
<td>[% build.id %]</td> <td>[% build.id %]</td>
</tr> </tr>
<tr>
<th>Time added:</th>
<td>[% date.format(build.timestamp, '%Y-%m-%d %H:%M:%S') %]</td>
</tr>
<tr> <tr>
<th>Project:</th> <th>Project:</th>
<td><a href="[% c.uri_for('/project' build.project.name) %]"><tt>[% build.project.name %]</tt></a></td> <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> <td>[% build.description %]</td>
</tr> </tr>
<tr> <tr>
<th>Time added:</th> <th>System:</th>
<td>[% date.format(build.timestamp, '%Y-%m-%d %H:%M:%S') %]</td> <td><tt>[% build.system %]</tt></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>
</tr> </tr>
<tr> <tr>
<th>Derivation store path:</th> <th>Derivation store path:</th>
@ -57,20 +48,41 @@
<th>Output store path:</th> <th>Output store path:</th>
<td><tt>[% build.outpath %]</tt></td> <td><tt>[% build.outpath %]</tt></td>
</tr> </tr>
[% IF build.finished %]
<tr> <tr>
<th>System:</th> <th>Build started:</th>
<td><tt>[% build.system %]</tt></td> <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>
<tr> <tr>
<th>Status:</th> <th>Status:</th>
<td> <td>
[% IF build.buildstatus == 0 %] [% IF build.resultInfo.buildstatus == 0 %]
<img src="/static/images/success.gif" /> <img src="/static/images/success.gif" />
[% ELSE %] [% ELSE %]
<img src="/static/images/failure.gif" /> <img src="/static/images/failure.gif" />
[% END %] [% END %]
</td> </td>
</tr> </tr>
[% ELSE %]
<tr>
<th>Priority:</th>
<td>[% build.schedulingInfo.priority %]</td>
</tr>
[% END %]
</table> </table>
@ -101,6 +113,8 @@
</table> </table>
[% IF build.finished %]
<h2>Build products</h2> <h2>Build products</h2>
<ul class="productList"> <ul class="productList">
@ -167,3 +181,6 @@
[% END %] [% END %]
[% END %]

View file

@ -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> <tr><th>#</th><th>Priority</th><th>Project</th><th>Job</th><th>System</th><th>Timestamp</th><th>Description</th></tr>
</thead> </thead>
<tbody> <tbody>
[% FOREACH job IN jobs -%] [% FOREACH build IN scheduled -%]
<tr [% IF job.busy %]class="runningJob"[% END %] > <tr [% IF build.schedulingInfo.busy %]class="runningJob"[% END %] >
<td>[% job.id %]</td> <td><a href="[% c.uri_for('/build' build.id) %]">[% build.id %]</a></td>
<td>[% job.priority %]</td> <td>[% build.schedulingInfo.priority %]</td>
<td><tt>[% job.project.name %]</tt></td> <td><tt>[% build.project.name %]</tt></td>
<td><tt>[% job.jobset.name %]</tt></td> <td><tt>[% build.jobset.name %]</tt></td>
<td><tt>[% job.system %]</tt></td> <td><tt>[% build.system %]</tt></td>
<td>[% date.format(job.timestamp, '%Y-%m-%d %H:%M:%S') %]</td> <td>[% date.format(build.timestamp, '%Y-%m-%d %H:%M:%S') %]</td>
<td>[% job.description %]</td> <td>[% build.description %]</td>
</tr> </tr>
[% END -%] [% END -%]
</tbody> </tbody>

View file

@ -2,7 +2,7 @@
<tr> <tr>
<td> <td>
[% IF build.buildstatus == 0 %] [% IF build.resultInfo.buildstatus == 0 %]
<img src="/static/images/success.gif" /> <img src="/static/images/success.gif" />
[% ELSE %] [% ELSE %]
<img src="/static/images/failure.gif" /> <img src="/static/images/failure.gif" />

View file

@ -16,11 +16,11 @@ sub isValidPath {
} }
sub buildJob { sub doBuild {
my ($job) = @_; my ($build) = @_;
my $drvPath = $job->drvpath; my $drvPath = $build->drvpath;
my $outPath = $job->outpath; my $outPath = $build->outpath;
my $isCachedBuild = 1; my $isCachedBuild = 1;
my $outputCreated = 1; # i.e., the Nix build succeeded (but it could be a positive failure) 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 { $db->txn_do(sub {
my $build = $db->resultset('Builds')->create( $build->finished(1);
{ timestamp => time() $build->timestamp(time());
, project => $job->project->name $build->update;
, jobset => $job->jobset->name
, attrname => $job->attrname $db->resultset('Buildresultinfo')->create(
, description => $job->description { id => $build->id
, drvpath => $drvPath
, outpath => $outPath
, iscachedbuild => $isCachedBuild , iscachedbuild => $isCachedBuild
, buildstatus => $buildStatus , buildstatus => $buildStatus
, starttime => $startTime , starttime => $startTime
, stoptime => $stopTime , 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; my $logPath = "/nix/var/log/nix/drvs/" . basename $drvPath;
if (-e $logPath) { if (-e $logPath) {
@ -125,47 +115,44 @@ sub buildJob {
} }
} }
$job->delete; $build->schedulingInfo->delete;
}); });
print "STOP ", time, "\n";
} }
my $jobId = $ARGV[0] or die; my $buildId = $ARGV[0] or die;
print "building job $jobId\n"; print "performing build $buildId\n";
# Lock the job. If necessary, steal the lock from the parent process # Lock the build. If necessary, steal the lock from the parent
# (runner.pl). This is so that if the runner dies, the children # process (runner.pl). This is so that if the runner dies, the
# (i.e. the job builders) can continue to run and won't have the lock # children (i.e. the build.pl instances) can continue to run and won't
# taken away. # have the lock taken away.
my $job; my $build;
$db->txn_do(sub { $db->txn_do(sub {
($job) = $db->resultset('Jobs')->search({ id => $jobId }); ($build) = $db->resultset('Builds')->search({id => $buildId});
die "job $jobId doesn't exist" unless defined $job; die "build $buildId doesn't exist" unless defined $build;
if ($job->busy != 0 && $job->locker != getppid) { if ($build->schedulingInfo->busy != 0 && $build->schedulingInfo->locker != getppid) {
die "job $jobId is already being built"; die "build $buildId is already being built";
} }
$job->busy(1); $build->schedulingInfo->busy(1);
$job->locker($$); $build->schedulingInfo->locker($$);
$job->update; $build->schedulingInfo->update;
}); });
die unless $job; die unless $build;
# Build the job. If it throws an error, unlock the job so that it can # Do the build. If it throws an error, unlock the build so that it
# be retried. # can be retried.
eval { eval {
print "BUILD\n"; print "BUILD\n";
buildJob $job; doBuild $build;
print "DONE\n"; print "DONE\n";
}; };
if ($@) { if ($@) {
warn $@; warn $@;
$db->txn_do(sub { $db->txn_do(sub {
$job->busy(0); $build->schedulingInfo->busy(0);
$job->locker($$); $build->schedulingInfo->locker($$);
$job->update; $build->schedulingInfo->update;
}); });
} }

View file

@ -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, 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. -- Info about the inputs.
project text not null, -- !!! foreign key project text not null, -- !!! foreign key
@ -11,25 +20,51 @@ create table builds (
description text, description text,
drvPath text not null, drvPath text not null,
outPath 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, system text not null,
foreign key (project) references projects(name), -- ignored by sqlite foreign key (project) references Projects(name), -- ignored by sqlite
foreign key (project, jobset) references jobsets(project, name) -- ignored by sqlite foreign key (project, jobset) references Jobsets(project, name) -- ignored by sqlite
); );
-- Inputs of jobs/builds. -- Info for a scheduled build.
create table inputs ( 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, 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, build integer,
job integer,
-- Copied from the jobsetinputs from which the build was created. -- Copied from the jobsetinputs from which the build was created.
name text not null, name text not null,
@ -42,29 +77,28 @@ create table inputs (
path text, path text,
foreign key (build) references builds(id) -- ignored by sqlite foreign key (build) references Builds(id) on delete cascade, -- ignored by sqlite
foreign key (job) references jobs(id) -- ignored by sqlite foreign key (dependency) references Builds(id) -- ignored by sqlite
foreign key (dependency) references builds(id) -- ignored by sqlite
); );
create table buildProducts ( create table BuildProducts (
build integer not null, build integer not null,
path text not null, path text not null,
type text not null, -- "nix-build", "file", "doc", "report", ... type text not null, -- "nix-build", "file", "doc", "report", ...
subtype text not null, -- "source-dist", "rpm", ... subtype text not null, -- "source-dist", "rpm", ...
primary key (build, path), 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, build integer not null,
logPhase text not null, logPhase text not null,
path text not null, path text not null,
type text not null, type text not null,
primary key (build, logPhase), 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 create trigger cascadeBuildDeletion
before delete on builds before delete on builds
for each row begin for each row begin
--delete from buildInputs where build = old.id; delete from BuildSchedulingInfo where id = old.id;
delete from buildLogs where build = old.id; delete from BuildResultInfo where id = old.id;
delete from buildProducts where build = old.id; delete from BuildInputs where build = old.id;
delete from BuildLogs where build = old.id;
delete from BuildProducts where build = old.id;
end; end;
create table projects ( create table Projects (
name text primary key not null 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 -- A jobset consists of a set of inputs (e.g. SVN repositories), one
-- of which contains a Nix expression containing an attribute set -- of which contains a Nix expression containing an attribute set
-- describing build jobs. -- describing build jobs.
create table jobsets ( create table Jobsets (
name text not null, name text not null,
project text not null, project text not null,
description text, description text,
nixExprInput text not null, -- name of the jobsetInput containing the Nix expression nixExprInput text not null, -- name of the jobsetInput containing the Nix expression
nixExprPath text not null, -- relative path of the Nix expression nixExprPath text not null, -- relative path of the Nix expression
primary key (project, name), primary key (project, name),
foreign key (project) references projects(name) on delete cascade, -- ignored by sqlite 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, name, nixExprInput) references JobsetInputs(project, job, name)
); );
create table jobsetInputs ( create table JobsetInputs (
project text not null, project text not null,
jobset text not null, jobset text not null,
name text not null, name text not null,
type text not null, -- "svn", "cvs", "path", "file", "string" type text not null, -- "svn", "cvs", "path", "file", "string"
primary key (project, jobset, name), 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, project text not null,
jobset text not null, jobset text not null,
input text not null, input text not null,
@ -121,30 +157,5 @@ create table jobsetInputAlts (
value text, -- for type == 'string' value text, -- for type == 'string'
primary key (project, jobset, input, altnr), primary key (project, jobset, input, altnr),
foreign key (project, jobset, input) references jobsetInputs(project, jobset, name) on delete cascade -- ignored by sqlite 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
); );

View file

@ -12,14 +12,16 @@ $db->storage->dbh->do("PRAGMA synchronous = OFF;");
# Unlock jobs whose building process has died. # Unlock jobs whose building process has died.
$db->txn_do(sub { $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) { 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 if (kill(0, $pid) != 1) { # see if we can signal the process
print "job ", $job->id, " pid $pid died, unlocking\n"; print "job ", $job->id, " pid $pid died, unlocking\n";
$job->busy(0); $job->schedulingInfo->busy(0);
$job->locker(""); $job->schedulingInfo->locker("");
$job->update; $job->schedulingInfo->update;
} }
} }
}); });
@ -32,15 +34,17 @@ sub checkJobs {
$db->txn_do(sub { $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"; print "# of available jobs: ", scalar(@jobs), "\n";
if (scalar @jobs > 0) { if (scalar @jobs > 0) {
$job = $jobs[0]; $job = $jobs[0];
$job->busy(1); $job->schedulingInfo->busy(1);
$job->locker($$); $job->schedulingInfo->locker($$);
$job->update; $job->schedulingInfo->update;
} }
}); });
@ -66,9 +70,9 @@ sub checkJobs {
if ($@) { if ($@) {
warn $@; warn $@;
$db->txn_do(sub { $db->txn_do(sub {
$job->busy(0); $job->schedulingInfo->busy(0);
$job->locker($$); $job->schedulingInfo->locker($$);
$job->update; $job->schedulingInfo->update;
}); });
} }
} }

View file

@ -67,24 +67,15 @@ sub checkJob {
{ project => $project->name, jobset => $jobset->name { project => $project->name, jobset => $jobset->name
, attrname => $jobName, outPath => $outPath })) > 0) , attrname => $jobName, outPath => $outPath })) > 0)
{ {
print " already done\n"; print " already scheduled/done\n";
return; 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"; print " adding to queue\n";
my $job = $db->resultset('Jobs')->create(
{ timestamp => time() my $build = $db->resultset('Builds')->create(
, priority => 0 { finished => 0
, busy => 0 , timestamp => time()
, locker => ""
, project => $project->name , project => $project->name
, jobset => $jobset->name , jobset => $jobset->name
, attrname => $jobName , attrname => $jobName
@ -94,10 +85,17 @@ sub checkJob {
, system => $job->{system} , system => $job->{system}
}); });
$db->resultset('Buildschedulinginfo')->create(
{ id => $build->id
, priority => 0
, busy => 0
, locker => ""
});
foreach my $inputName (keys %{$inputInfo}) { foreach my $inputName (keys %{$inputInfo}) {
my $input = $inputInfo->{$inputName}; my $input = $inputInfo->{$inputName};
$db->resultset('Inputs')->create( $db->resultset('Buildinputs')->create(
{ job => $job->id { build => $build->id
, name => $inputName , name => $inputName
, type => $input->{type} , type => $input->{type}
, uri => $input->{uri} , uri => $input->{uri}
@ -151,8 +149,8 @@ sub checkJobAlternatives {
else { else {
(my $prevBuild) = $db->resultset('Builds')->search( (my $prevBuild) = $db->resultset('Builds')->search(
{project => $project->name, jobset => $jobset->name, attrname => $argName, buildStatus => 0}, {finished => 1, project => $project->name, jobset => $jobset->name, attrname => $argName, buildStatus => 0},
{order_by => "timestamp DESC", rows => 1}); {join => 'resultInfo', order_by => "timestamp DESC", rows => 1});
if (!defined $prevBuild) { if (!defined $prevBuild) {
# !!! reschedule? # !!! reschedule?