* Renaming "release sets" to "views" (not finished yet). Having

releases as a dynamic view on the database was misguided, since
  doing thing like adding a new job to a release set will invalidate
  all old releases.  So we rename release sets to views, and we'll
  reintroduce releases as separate, static entities in the database.
This commit is contained in:
Eelco Dolstra 2009-10-15 21:35:19 +00:00
parent 3ebe5e1069
commit cec3201720
29 changed files with 351 additions and 379 deletions

View file

@ -96,6 +96,15 @@
alter table Jobsets add column enabled integer not null default 1; alter table Jobsets add column enabled integer not null default 1;
# Releases -> Views.
alter table ReleaseSets rename to Views;
alter table Views rename column name to view_;
alter table ReleaseSetJobs rename to ViewJobs;
alter table ViewJobs rename column release_ to view_;
alter table ViewJobs drop column mayFail;
alter table ViewJobs add column autorelease integer not null default 0;
* Job selection: * Job selection:

View file

@ -24,7 +24,7 @@ sub view : Chained('project') PathPart('') Args(0) {
getBuildStats($c, scalar $c->stash->{project}->builds); getBuildStats($c, scalar $c->stash->{project}->builds);
$c->stash->{releaseSets} = [$c->stash->{project}->releasesets->all]; $c->stash->{views} = [$c->stash->{project}->views->all];
} }

View file

@ -63,192 +63,6 @@ sub queue :Local {
} }
sub getReleaseSet {
my ($c, $projectName, $releaseSetName) = @_;
my $project = $c->model('DB::Projects')->find($projectName);
notFound($c, "Project $projectName doesn't exist.") if !defined $project;
$c->stash->{project} = $project;
(my $releaseSet) = $c->model('DB::ReleaseSets')->find($projectName, $releaseSetName);
notFound($c, "Release set $releaseSetName doesn't exist.") if !defined $releaseSet;
$c->stash->{releaseSet} = $releaseSet;
(my $primaryJob) = $releaseSet->releasesetjobs->search({isprimary => 1});
#die "Release set $releaseSetName doesn't have a primary job." if !defined $primaryJob;
my $jobs = [$releaseSet->releasesetjobs->search({},
{order_by => ["isprimary DESC", "job", "attrs"]})];
$c->stash->{jobs} = $jobs;
return ($project, $releaseSet, $primaryJob, $jobs);
}
sub updateReleaseSet {
my ($c, $releaseSet) = @_;
my $releaseSetName = trim $c->request->params->{name};
error($c, "Invalid release set name: $releaseSetName")
unless $releaseSetName =~ /^[[:alpha:]][\w\-]*$/;
$releaseSet->update(
{ name => $releaseSetName
, description => trim $c->request->params->{description} });
$releaseSet->releasesetjobs->delete_all;
foreach my $param (keys %{$c->request->params}) {
next unless $param =~ /^job-(\d+)-name$/;
my $baseName = $1;
my $name = trim $c->request->params->{"job-$baseName-name"};
my $description = trim $c->request->params->{"job-$baseName-description"};
my $attrs = trim $c->request->params->{"job-$baseName-attrs"};
$name =~ /^([\w\-]+):([\w\-]+)$/ or error($c, "Invalid job name: $name");
my $jobsetName = $1;
my $jobName = $2;
error($c, "Jobset `$jobsetName' doesn't exist.")
unless $releaseSet->project->jobsets->find({name => $jobsetName});
# !!! We could check whether the job exists, but that would
# require the scheduler to have seen the job, which may not be
# the case.
$releaseSet->releasesetjobs->create(
{ jobset => $jobsetName
, job => $jobName
, description => $description
, attrs => $attrs
, isprimary => $c->request->params->{"primary"} eq $baseName ? 1 : 0
});
}
error($c, "There must be one primary job.")
if $releaseSet->releasesetjobs->search({isprimary => 1})->count != 1;
}
sub releases :Local {
my ($self, $c, $projectName, $releaseSetName, $subcommand) = @_;
my ($project, $releaseSet, $primaryJob, $jobs) = getReleaseSet($c, $projectName, $releaseSetName);
my $resultsPerPage = 10;
my $page = 1;
if (defined $subcommand && $subcommand =~ /^\d+$/ ) {
$page = int($subcommand)
}
elsif (defined $subcommand && $subcommand ne "") {
requireProjectOwner($c, $project);
if ($subcommand eq "edit") {
$c->stash->{template} = 'edit-releaseset.tt';
return;
}
elsif ($subcommand eq "submit") {
txn_do($c->model('DB')->schema, sub {
updateReleaseSet($c, $releaseSet);
});
return $c->res->redirect($c->uri_for("/releases", $projectName, $releaseSet->name));
}
elsif ($subcommand eq "delete") {
txn_do($c->model('DB')->schema, sub {
$releaseSet->delete;
});
return $c->res->redirect($c->uri_for($c->controller('Project')->action_for('view'), [$project->name]));
}
else { error($c, "Unknown subcommand."); }
}
$c->stash->{template} = 'releases.tt';
my @releases = ();
push @releases, getRelease($_, $jobs) foreach getPrimaryBuildsForReleaseSet($project, $primaryJob, $page, $resultsPerPage);
$c->stash->{baseUri} = $c->uri_for($self->action_for("releases"), $projectName, $releaseSetName);
$c->stash->{releases} = [@releases];
$c->stash->{page} = $page;
$c->stash->{totalReleases} = getPrimaryBuildTotal($project, $primaryJob);
$c->stash->{resultsPerPage} = $resultsPerPage;
}
sub create_releaseset :Local {
my ($self, $c, $projectName, $subcommand) = @_;
my $project = $c->model('DB::Projects')->find($projectName);
error($c, "Project $projectName doesn't exist.") if !defined $project;
$c->stash->{project} = $project;
requireProjectOwner($c, $project);
if (defined $subcommand && $subcommand eq "submit") {
my $releaseSetName = $c->request->params->{name};
txn_do($c->model('DB')->schema, sub {
# Note: $releaseSetName is validated in updateProject,
# which will abort the transaction if the name isn't
# valid.
my $releaseSet = $project->releasesets->create({name => $releaseSetName});
updateReleaseSet($c, $releaseSet);
return $c->res->redirect($c->uri_for("/releases", $projectName, $releaseSet->name));
});
}
$c->stash->{template} = 'edit-releaseset.tt';
$c->stash->{create} = 1;
}
sub release :Local {
my ($self, $c, $projectName, $releaseSetName, $releaseId, @args) = @_;
$c->stash->{template} = 'release.tt';
my ($project, $releaseSet, $primaryJob, $jobs) = getReleaseSet($c, $projectName, $releaseSetName);
if ($releaseId eq "latest") {
# Redirect to the latest successful release.
my $latest = getLatestSuccessfulRelease($project, $primaryJob, $jobs);
error($c, "This release set has no successful releases yet.") if !defined $latest;
return $c->res->redirect($c->uri_for("/release", $projectName, $releaseSetName, $latest->id, @args));
}
# Note: we don't actually check whether $releaseId is a primary
# build, but who cares?
my $primaryBuild = $project->builds->find($releaseId,
{ join => 'resultInfo',
, '+select' => ["resultInfo.releasename", "resultInfo.buildstatus"]
, '+as' => ["releasename", "buildstatus"] })
or error($c, "Release $releaseId doesn't exist.");
$c->stash->{release} = getRelease($primaryBuild, $jobs);
# Provide a redirect to the specified job of this release. !!!
# This isn't uniquely defined if there are multiple jobs with the
# same name (e.g. builds for different platforms). However, this
# mechanism is primarily to allow linking to resources of which
# there is only one build, such as the manual of the latest
# release.
if (scalar @args != 0) {
my $jobName = shift @args;
(my $build, my @others) = grep { $_->{job}->job eq $jobName } @{$c->stash->{release}->{jobs}};
notFound($c, "Release doesn't have a job named `$jobName'")
unless defined $build;
error($c, "Job `$jobName' isn't unique.") if @others;
return $c->res->redirect($c->uri_for($c->controller('Build')->action_for('view_build'),
[$build->{build}->id], @args));
}
}
# Hydra::Base::Controller::ListBuilds needs this. # Hydra::Base::Controller::ListBuilds needs this.
sub get_builds : Chained('/') PathPart('') CaptureArgs(0) { sub get_builds : Chained('/') PathPart('') CaptureArgs(0) {
my ($self, $c) = @_; my ($self, $c) = @_;

View file

@ -0,0 +1,161 @@
package Hydra::Controller::View;
use strict;
use warnings;
use base 'Catalyst::Controller';
use Hydra::Helper::Nix;
use Hydra::Helper::CatalystUtils;
sub getView {
my ($c, $projectName, $viewName) = @_;
my $project = $c->model('DB::Projects')->find($projectName);
notFound($c, "Project $projectName doesn't exist.") if !defined $project;
$c->stash->{project} = $project;
(my $view) = $c->model('DB::Views')->find($projectName, $viewName);
notFound($c, "View $viewName doesn't exist.") if !defined $view;
$c->stash->{view} = $view;
(my $primaryJob) = $view->viewjobs->search({isprimary => 1});
#die "View $viewName doesn't have a primary job." if !defined $primaryJob;
my $jobs = [$view->viewjobs->search({},
{order_by => ["isprimary DESC", "job", "attrs"]})];
$c->stash->{jobs} = $jobs;
return ($project, $view, $primaryJob, $jobs);
}
sub updateReleaseSet {
my ($c, $releaseSet) = @_;
my $releaseSetName = trim $c->request->params->{name};
error($c, "Invalid release set name: $releaseSetName")
unless $releaseSetName =~ /^[[:alpha:]][\w\-]*$/;
$releaseSet->update(
{ name => $releaseSetName
, description => trim $c->request->params->{description} });
$releaseSet->releasesetjobs->delete_all;
foreach my $param (keys %{$c->request->params}) {
next unless $param =~ /^job-(\d+)-name$/;
my $baseName = $1;
my $name = trim $c->request->params->{"job-$baseName-name"};
my $description = trim $c->request->params->{"job-$baseName-description"};
my $attrs = trim $c->request->params->{"job-$baseName-attrs"};
$name =~ /^([\w\-]+):([\w\-]+)$/ or error($c, "Invalid job name: $name");
my $jobsetName = $1;
my $jobName = $2;
error($c, "Jobset `$jobsetName' doesn't exist.")
unless $releaseSet->project->jobsets->find({name => $jobsetName});
# !!! We could check whether the job exists, but that would
# require the scheduler to have seen the job, which may not be
# the case.
$releaseSet->releasesetjobs->create(
{ jobset => $jobsetName
, job => $jobName
, description => $description
, attrs => $attrs
, isprimary => $c->request->params->{"primary"} eq $baseName ? 1 : 0
});
}
error($c, "There must be one primary job.")
if $releaseSet->releasesetjobs->search({isprimary => 1})->count != 1;
}
sub view : Chained('/') PathPart('view') CaptureArgs(2) {
my ($self, $c, $projectName, $viewName) = @_;
my ($project, $view, $primaryJob, $jobs) = getView($c, $projectName, $viewName);
$c->stash->{project} = $project;
$c->stash->{view} = $view;
$c->stash->{primaryJob} = $primaryJob;
$c->stash->{jobs} = $jobs;
}
sub view_view : Chained('view') PathPart('') Args(0) {
my ($self, $c) = @_;
$c->stash->{template} = 'view.tt';
my $resultsPerPage = 10;
my $page = int($c->req->param('page')) || 1;
my @results = ();
push @results, getRelease($_, $c->stash->{jobs}) foreach
getPrimaryBuildsForReleaseSet($c->stash->{project}, $c->stash->{primaryJob}, $page, $resultsPerPage);
$c->stash->{baseUri} = $c->uri_for($self->action_for("view"), $c->stash->{project}->name, $c->stash->{view}->name);
$c->stash->{results} = [@results];
$c->stash->{page} = $page;
$c->stash->{totalResults} = getPrimaryBuildTotal($c->stash->{project}, $c->stash->{primaryJob});
$c->stash->{resultsPerPage} = $resultsPerPage;
}
sub edit : Chained('view') PathPart('edit') Args(0) {
my ($self, $c) = @_;
requireProjectOwner($c, $c->stash->{project});
$c->stash->{template} = 'edit-view.tt';
}
sub latest : Chained('view') PathPart('latest') {
my ($self, $c, @args) = @_;
# Redirect to the latest result in the view in which every build
# is successful.
my $latest = getLatestSuccessfulRelease(
$c->stash->{project}, $c->stash->{primaryJob}, $c->stash->{jobs});
error($c, "This view set has no successful results yet.") if !defined $latest;
return $c->res->redirect($c->uri_for("/view", $c->stash->{project}->name, $c->stash->{view}->name, $latest->id, @args));
}
sub result : Chained('view') PathPart('') {
my ($self, $c, $id, @args) = @_;
$c->stash->{template} = 'release.tt';
# Note: we don't actually check whether $id is a primary build,
# but who cares?
my $primaryBuild = $c->stash->{project}->builds->find($id,
{ join => 'resultInfo',
, '+select' => ["resultInfo.releasename", "resultInfo.buildstatus"]
, '+as' => ["releasename", "buildstatus"] })
or error($c, "Build $id doesn't exist.");
$c->stash->{release} = getRelease($primaryBuild, $c->stash->{jobs});
# Provide a redirect to the specified job of this release. !!!
# This isn't uniquely defined if there are multiple jobs with the
# same name (e.g. builds for different platforms). However, this
# mechanism is primarily to allow linking to resources of which
# there is only one build, such as the manual of the latest
# release.
if (scalar @args != 0) {
my $jobName = shift @args;
(my $build, my @others) = grep { $_->{job}->job eq $jobName } @{$c->stash->{release}->{jobs}};
notFound($c, "Release doesn't have a job named `$jobName'")
unless defined $build;
error($c, "Job `$jobName' isn't unique.") if @others;
return $c->res->redirect($c->uri_for($c->controller('Build')->action_for('view_build'),
[$build->{build}->id], @args));
}
}
1;

View file

@ -239,12 +239,10 @@ sub getRelease {
$thisBuild = findLastJobForPrimaryBuild($primaryBuild, $job) ; $thisBuild = findLastJobForPrimaryBuild($primaryBuild, $job) ;
} }
if ($job->mayfail != 1) { if (!defined $thisBuild) {
if (!defined $thisBuild) { $status = 2 if $status == 0; # = unfinished
$status = 2 if $status == 0; # = unfinished } elsif ($thisBuild->get_column('buildstatus') != 0) {
} elsif ($thisBuild->get_column('buildstatus') != 0) { $status = 1; # = failed
$status = 1; # = failed
}
} }
$timestamp = $thisBuild->timestamp $timestamp = $thisBuild->timestamp

View file

@ -8,8 +8,8 @@ use base 'DBIx::Class::Schema';
__PACKAGE__->load_classes; __PACKAGE__->load_classes;
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:vdr83mcEie4i5Fn/Uj17Vg # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ODLRc6VfDQpb8MyXPKmqtg
# 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

@ -103,8 +103,8 @@ __PACKAGE__->belongs_to(
); );
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:yodYRloko+NdaEVy+IL5JA # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:gtA3wQA2CLsXs4X95PfX9A
use Hydra::Helper::Nix; use Hydra::Helper::Nix;

View file

@ -91,8 +91,8 @@ __PACKAGE__->set_primary_key("build", "productnr");
__PACKAGE__->belongs_to("build", "Hydra::Schema::Builds", { id => "build" }); __PACKAGE__->belongs_to("build", "Hydra::Schema::Builds", { id => "build" });
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:GdjLBqXz+LK4ewxnpIs9eQ # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ii6N3v4M1fX1tQ3YmJNFWw
# 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

@ -86,8 +86,8 @@ __PACKAGE__->set_primary_key("id");
__PACKAGE__->belongs_to("id", "Hydra::Schema::Builds", { id => "id" }); __PACKAGE__->belongs_to("id", "Hydra::Schema::Builds", { id => "id" });
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:KTPvLaqbXGpynWt107ISew # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:EMvF2g+MDIE84yjnJOs7og
__PACKAGE__->belongs_to( __PACKAGE__->belongs_to(
"failedDep", "failedDep",

View file

@ -43,8 +43,8 @@ __PACKAGE__->set_primary_key("id");
__PACKAGE__->belongs_to("id", "Hydra::Schema::Builds", { id => "id" }); __PACKAGE__->belongs_to("id", "Hydra::Schema::Builds", { id => "id" });
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:thMie1PGP25FGbo5qypE/w # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:RcdX5dHefBQnxQYbMxNF/w
# 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

@ -91,7 +91,7 @@ __PACKAGE__->set_primary_key("build", "stepnr");
__PACKAGE__->belongs_to("build", "Hydra::Schema::Builds", { id => "build" }); __PACKAGE__->belongs_to("build", "Hydra::Schema::Builds", { id => "build" });
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Ua+P31BMRmMKP6QFOdA89A # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1AQCHpuv8Lqk/FYdU8JYFA
1; 1;

View file

@ -163,8 +163,8 @@ __PACKAGE__->has_many(
); );
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:luxYxoOAtLoCgl5iFTYdJA # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:CcYlMej7OPRUJn6375Qlqw
use Hydra::Helper::Nix; use Hydra::Helper::Nix;

View file

@ -47,8 +47,8 @@ __PACKAGE__->add_columns(
__PACKAGE__->set_primary_key("srcpath", "sha256hash"); __PACKAGE__->set_primary_key("srcpath", "sha256hash");
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:DeoyeS42ddQ2FXa+8n31OQ # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:mYBdemei1tFuK8Ll6eMLfQ
# 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

@ -40,8 +40,8 @@ __PACKAGE__->add_columns(
__PACKAGE__->set_primary_key("uri", "revision"); __PACKAGE__->set_primary_key("uri", "revision");
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:CaFTGQtLjPwCISqk5W4fag # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:bE+w54cACUS2L0PJ9gPjtw
# 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

@ -75,8 +75,8 @@ __PACKAGE__->belongs_to(
); );
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:+Cb0mIbX8ddDbZY39u9feA # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:c0OEe2zPd/E4vh0PRXm4Ag
# 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

@ -69,8 +69,8 @@ __PACKAGE__->belongs_to(
); );
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:m3a1Q6c2FePidqbqYhz5dg # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:jS8pitmHFnplE8WcK0OyMQ
# 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

@ -65,8 +65,8 @@ __PACKAGE__->has_many(
); );
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:QSYSg5xsN292LnfvbAG0Vw # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:W0rhMTOzLBZNsVShQHg5+A
# 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

@ -103,8 +103,8 @@ __PACKAGE__->has_many(
); );
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:85FwtlvNxjGix7PUCJTMqA # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:CB5lPsrozpvO8gLXHTyMrQ
# 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

@ -65,19 +65,19 @@ __PACKAGE__->has_many(
{ "foreign.project" => "self.name" }, { "foreign.project" => "self.name" },
); );
__PACKAGE__->has_many( __PACKAGE__->has_many(
"releasesets", "views",
"Hydra::Schema::ReleaseSets", "Hydra::Schema::Views",
{ "foreign.project" => "self.name" }, { "foreign.project" => "self.name" },
); );
__PACKAGE__->has_many( __PACKAGE__->has_many(
"releasesetjobs", "viewjobs",
"Hydra::Schema::ReleaseSetJobs", "Hydra::Schema::ViewJobs",
{ "foreign.project" => "self.name" }, { "foreign.project" => "self.name" },
); );
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Dru36PNUe9iYHEwhhHKJ3A # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:N6NPLJfc1gKM4zz6dS5PJw
# 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

@ -21,8 +21,8 @@ __PACKAGE__->add_columns(
__PACKAGE__->set_primary_key("system"); __PACKAGE__->set_primary_key("system");
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:mfZTzyri5eSRhfmBmwyuFQ # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:EkpopxgwlZf8Du3EmWzTKQ
# 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

@ -28,8 +28,8 @@ __PACKAGE__->set_primary_key("username", "role");
__PACKAGE__->belongs_to("username", "Hydra::Schema::Users", { username => "username" }); __PACKAGE__->belongs_to("username", "Hydra::Schema::Users", { username => "username" });
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:6RgJY04rmD+PumWXz5KGoQ # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:W2Q6219GlZl2IqQkBoFmFA
# 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

@ -50,8 +50,8 @@ __PACKAGE__->has_many(
); );
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ZWzljXMF0IbU12wNUn+djg # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:qH+qBI3xxQgTNf3v7E3sDw
# 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 Hydra::Schema::ReleaseSetJobs; package Hydra::Schema::ViewJobs;
use strict; use strict;
use warnings; use warnings;
@ -6,7 +6,7 @@ use warnings;
use base 'DBIx::Class'; use base 'DBIx::Class';
__PACKAGE__->load_components("Core"); __PACKAGE__->load_components("Core");
__PACKAGE__->table("ReleaseSetJobs"); __PACKAGE__->table("ViewJobs");
__PACKAGE__->add_columns( __PACKAGE__->add_columns(
"project", "project",
{ {
@ -16,7 +16,7 @@ __PACKAGE__->add_columns(
is_nullable => 0, is_nullable => 0,
size => undef, size => undef,
}, },
"release_", "view_",
{ {
data_type => "text", data_type => "text",
default_value => undef, default_value => undef,
@ -40,8 +40,6 @@ __PACKAGE__->add_columns(
}, },
"isprimary", "isprimary",
{ data_type => "integer", default_value => 0, is_nullable => 0, size => undef }, { data_type => "integer", default_value => 0, is_nullable => 0, size => undef },
"mayfail",
{ data_type => "integer", default_value => 0, is_nullable => 0, size => undef },
"description", "description",
{ {
data_type => "text", data_type => "text",
@ -56,18 +54,20 @@ __PACKAGE__->add_columns(
is_nullable => 0, is_nullable => 0,
size => undef, size => undef,
}, },
"autorelease",
{ data_type => "integer", default_value => 0, is_nullable => 0, size => undef },
); );
__PACKAGE__->set_primary_key("project", "release_", "job", "attrs"); __PACKAGE__->set_primary_key("project", "view_", "job", "attrs");
__PACKAGE__->belongs_to("project", "Hydra::Schema::Projects", { name => "project" }); __PACKAGE__->belongs_to("project", "Hydra::Schema::Projects", { name => "project" });
__PACKAGE__->belongs_to( __PACKAGE__->belongs_to(
"releaseset", "view",
"Hydra::Schema::ReleaseSets", "Hydra::Schema::Views",
{ name => "release_", project => "project" }, { name => "view_", project => "project" },
); );
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:qSQjyHzxQp0qO3CbRdcXmw # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:LkiGAkZOiLNJk6oDY0+zNw
# 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 Hydra::Schema::ReleaseSets; package Hydra::Schema::Views;
use strict; use strict;
use warnings; use warnings;
@ -6,7 +6,7 @@ use warnings;
use base 'DBIx::Class'; use base 'DBIx::Class';
__PACKAGE__->load_components("Core"); __PACKAGE__->load_components("Core");
__PACKAGE__->table("ReleaseSets"); __PACKAGE__->table("Views");
__PACKAGE__->add_columns( __PACKAGE__->add_columns(
"project", "project",
{ {
@ -36,17 +36,14 @@ __PACKAGE__->add_columns(
__PACKAGE__->set_primary_key("project", "name"); __PACKAGE__->set_primary_key("project", "name");
__PACKAGE__->belongs_to("project", "Hydra::Schema::Projects", { name => "project" }); __PACKAGE__->belongs_to("project", "Hydra::Schema::Projects", { name => "project" });
__PACKAGE__->has_many( __PACKAGE__->has_many(
"releasesetjobs", "viewjobs",
"Hydra::Schema::ReleaseSetJobs", "Hydra::Schema::ViewJobs",
{ { "foreign.project" => "self.project", "foreign.view_" => "self.name" },
"foreign.project" => "self.project",
"foreign.release_" => "self.name",
},
); );
# Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-08 13:25:04 # Created by DBIx::Class::Schema::Loader v0.04999_06 @ 2009-10-15 23:14:39
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:pEjxqTAwP4ZmP/s6F4VOsg # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:hV+xzi564rgcYeDvz75zCA
# 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,8 +1,8 @@
[% WRAPPER layout.tt title=(create ? "New Release Set" : "Release Set $project.name:$releaseSet.name") %] [% WRAPPER layout.tt title=(create ? "New View" : "View $project.name:$view.name") %]
[% PROCESS common.tt %] [% PROCESS common.tt %]
[% USE HTML %] [% USE HTML %]
<h1>[% IF create %]New Release Set[% ELSE %]Release Set <tt>[% project.name %]:[% releaseSet.name %]</tt>[% END %]</h1> <h1>[% IF create %]New View[% ELSE %]View <tt>[% project.name %]:[% view.name %]</tt>[% END %]</h1>
[% BLOCK renderJob %] [% BLOCK renderJob %]
@ -21,16 +21,16 @@
[% END %] [% END %]
<form action="[% IF create %][% c.uri_for('/create_releaseset' project.name 'submit') %][% ELSE %][% c.uri_for('/releases' project.name releaseSet.name 'submit') %][% END %]" method="post"> <form action="[% IF create %][% c.uri_for('/create-view' project.name 'submit') %][% ELSE %][% c.uri_for('/view' project.name view.name 'submit') %][% END %]" method="post">
<table class="layoutTable"> <table class="layoutTable">
<tr> <tr>
<th>Identifier:</th> <th>Identifier:</th>
<td><input type="text" class="string" name="name" [% HTML.attributes(value => releaseSet.name) %] /></td> <td><input type="text" class="string" name="name" [% HTML.attributes(value => view.name) %] /></td>
</tr> </tr>
<tr> <tr>
<th>Description:</th> <th>Description:</th>
<td><input type="text" class="string" name="description" [% HTML.attributes(value => releaseSet.description) %] /></td> <td><input type="text" class="string" name="description" [% HTML.attributes(value => view.description) %] /></td>
</tr> </tr>
</table> </table>
@ -86,13 +86,13 @@
[% IF !create %] [% IF !create %]
<form action="[% c.uri_for('/releases' project.name releaseSet.name 'delete') %]" method="post"> <form action="[% c.uri_for('/view' project.name view.name 'delete') %]" method="post">
<p><button id="delete-project" type="submit"><img src="/static/images/failure.gif" />Delete this release set</button></p> <p><button id="delete-project" type="submit"><img src="/static/images/failure.gif" />Delete this view</button></p>
</form> </form>
<script type="text/javascript"> <script type="text/javascript">
$("#delete-project").click(function() { $("#delete-project").click(function() {
return confirm("Are you sure you want to delete this release set?"); return confirm("Are you sure you want to delete this view?");
}); });
</script> </script>

View file

@ -99,28 +99,28 @@
<h2>Releases</h2> <h2>Views</h2>
[% IF releaseSets.size > 0 %] [% IF views.size > 0 %]
<p>Project <tt>[% project.name %]</tt> has the following release sets:</p> <p>Project <tt>[% project.name %]</tt> has the following views:</p>
<ul> <ul>
[% FOREACH releaseSet IN releaseSets %] [% FOREACH view IN views %]
<li> <li>
<a href="[% c.uri_for('/releases' project.name releaseSet.name) %]"><tt>[% releaseSet.name %]</tt></a> <a href="[% c.uri_for('/view' project.name view.name) %]"><tt>[% view.name %]</tt></a>
[<a href="[% c.uri_for('/releases' project.name releaseSet.name "edit") %]">Edit</a>] [<a href="[% c.uri_for('/view' project.name view.name "edit") %]">Edit</a>]
</li> </li>
[% END %] [% END %]
</ul> </ul>
[% ELSE %] [% ELSE %]
<p>Project <tt>[% project.name %]</tt> has no release sets.</p> <p>Project <tt>[% project.name %]</tt> has no views.</p>
[% END %] [% END %]
<p><a href="[% c.uri_for('/create_releaseset' project.name) %]">[Create a new release set]</a></p> <p><a href="[% c.uri_for('/project' project.name 'create-view') %]">[Create a new view]</a></p>
<h2>Channels</h2> <h2>Channels</h2>

View file

@ -1,75 +0,0 @@
[% WRAPPER layout.tt title="Release Set $releaseSet.project.name:$releaseSet.name" %]
[% PROCESS common.tt %]
[% USE HTML %]
<h1>Release Set <tt>[% releaseSet.project.name %]:[% releaseSet.name %]</tt></h1>
<p>
[<a href="[% c.uri_for('/releases' project.name releaseSet.name "edit") %]">Edit</a>]
[<a href="[% c.uri_for('/release' project.name releaseSet.name "latest") %]">Latest</a>]
</p>
<p>Showing releases [% (page - 1) * resultsPerPage + 1 %] - [% (page - 1) * resultsPerPage + releases.size %] out of [% totalReleases %].</p>
<table class="tablesorter">
<thead>
<tr>
<th></th>
<th>#</th>
<th>Release</th>
<th>Date</th>
[% FOREACH j IN jobs %]
<th class="releaseSetJobName">[% INCLUDE renderReleaseJobName job=j %]</th>
[% END %]
</tr>
</thead>
<tbody>
[% FOREACH release IN releases %]
[% link = c.uri_for('/release' releaseSet.project.name releaseSet.name release.id) %]
<tr class="clickable" onclick="window.location = '[% link %]'">
<td>
[% IF release.status == 0 %]
<img src="/static/images/success.gif" />
[% ELSIF release.status == 1 %]
<img src="/static/images/failure.gif" />
[% ELSIF release.status == 2 %]
<img src="/static/images/question-mark.png" />
[% END %]
</td>
<td><a href="[% link %]">[% release.id %]</a></td>
<td>
[% IF release.releasename %]
<tt>[% release.releasename %]</tt>
[% ELSE %]
<em>No name</em>
[% END %]
</td>
<td>[% INCLUDE renderDateTime timestamp=release.timestamp %]</td>
[% FOREACH j IN release.jobs %]
<td class="centered">
[% IF j.build %]
<a href="[% c.uri_for('/build' j.build.id) %]">
[% IF j.build.get_column('buildstatus') == 0 %]
<img src="/static/images/success.gif" />
[% ELSE %]
<img src="/static/images/failure.gif" />
[% END %]
</a>
[% END %]
</td>
[% END %]
</tr>
[% END %]
</tbody>
</table>
[% IF page > 1 %]
[<a href="[% "$baseUri/"; (page - 1) %]">Prev</a>]
[% END %]
[% IF page * resultsPerPage < totalReleases %]
[<a href="[% "$baseUri/"; (page + 1) %]">Next</a>]
[% END %]
[<a href="[% "$baseUri/"; (totalReleases - 1) div resultsPerPage + 1 %]">Last</a>]
[% END %]

76
src/root/view.tt Normal file
View file

@ -0,0 +1,76 @@
[% WRAPPER layout.tt title="View $view.project.name:$view.name" %]
[% PROCESS common.tt %]
[% USE HTML %]
<h1>View <tt>[% view.project.name %]:[% view.name %]</tt></h1>
<p>
[<a href="[% c.uri_for('/view' project.name view.name "edit") %]">Edit</a>]
[<a href="[% c.uri_for('/view' project.name view.name "latest") %]">Latest</a>]
</p>
<p>Showing results [% (page - 1) * resultsPerPage + 1 %] - [% (page - 1) * resultsPerPage + results.size %] out of [% totalResults %].</p>
<table class="tablesorter">
<thead>
<tr>
<th></th>
<th>#</th>
<th>Name</th>
<th>Date</th>
[% FOREACH j IN jobs %]
<th class="releaseSetJobName">[% INCLUDE renderReleaseJobName job=j %]</th>
[% END %]
</tr>
</thead>
<tbody>
[% FOREACH result IN results %]
[% link = c.uri_for('/view' project.name view.name result.id) %]
<tr class="clickable" onclick="window.location = '[% link %]'">
<td>
[% IF result.status == 0 %]
<img src="/static/images/success.gif" />
[% ELSIF result.status == 1 %]
<img src="/static/images/failure.gif" />
[% ELSIF result.status == 2 %]
<img src="/static/images/question-mark.png" />
[% END %]
</td>
<td><a href="[% link %]">[% result.id %]</a></td>
<td>
[% IF result.releasename %]
<tt>[% result.releasename %]</tt>
[% ELSE %]
<em>No name</em>
[% END %]
</td>
<td>[% INCLUDE renderDateTime timestamp=result.timestamp %]</td>
[% FOREACH j IN result.jobs %]
<td class="centered">
[% IF j.build %]
<a href="[% c.uri_for('/build' j.build.id) %]">
[% IF j.build.get_column('buildstatus') == 0 %]
<img src="/static/images/success.gif" />
[% ELSE %]
<img src="/static/images/failure.gif" />
[% END %]
</a>
[% END %]
</td>
[% END %]
</tr>
[% END %]
</tbody>
</table>
[<a href="[% "$baseUri?page=1" %]">First</a>]
[% IF page > 1 %]
[<a href="[% "$baseUri?page="; (page - 1) %]">Prev</a>]
[% END %]
[% IF page * resultsPerPage < totalResults %]
[<a href="[% "$baseUri?page="; (page + 1) %]">Next</a>]
[% END %]
[<a href="[% "$baseUri?page="; (totalResults - 1) div resultsPerPage + 1 %]">Last</a>]
[% END %]

View file

@ -184,8 +184,8 @@ create trigger cascadeProjectUpdate
update JobsetInputs set project = new.name where project = old.name; update JobsetInputs set project = new.name where project = old.name;
update JobsetInputAlts set project = new.name where project = old.name; update JobsetInputAlts set project = new.name where project = old.name;
update Builds set project = new.name where project = old.name; update Builds set project = new.name where project = old.name;
update ReleaseSets set project = new.name where project = old.name; update Views set project = new.name where project = old.name;
update ReleaseSetJobs set project = new.name where project = old.name; update ViewJobs set project = new.name where project = old.name;
end; end;
@ -329,28 +329,19 @@ create trigger cascadeUserDelete
end; end;
-- Release sets are a mechanism to automatically group related builds -- Views are a mechanism to automatically group related builds
-- together. A release set defines what an individual release -- together. A view definition consists of a build of some "primary"
-- consists of, namely: a release consists of a build of some -- job, plus all builds of the other jobs named in ViewJobs that have
-- "primary" job, plus all builds of the other jobs named in -- that build as an input. If there are multiple builds matching a
-- ReleaseSetJobs that have that build as an input. If there are -- ViewJob, then we take the oldest successful build, or the oldest
-- multiple builds matching a ReleaseSetJob, then we take the oldest -- unsuccessful build if there is no successful build.
-- successful build, or the oldest unsuccessful build if there is no create table Views (
-- successful build. A release is itself considered successful if all
-- builds (except those for jobs that have mayFail set) are
-- successful.
--
-- Note that individual releases aren't separately stored in the
-- database, so they're really just a dynamic view on the universe of
-- builds, defined by a ReleaseSet.
create table ReleaseSets (
project text not null, project text not null,
name text not null, name text not null,
description text, description text,
-- If true, don't garbage-collect builds belonging to the releases -- If true, don't garbage-collect builds included in this view.
-- defined by this row.
keep integer not null default 0, keep integer not null default 0,
primary key (project, name), primary key (project, name),
@ -358,26 +349,23 @@ create table ReleaseSets (
); );
create trigger cascadeReleaseSetDelete create trigger cascadeViewDelete
before delete on ReleaseSets before delete on Views
for each row begin for each row begin
delete from ReleaseSetJobs where project = old.project and release_ = old.name; delete from ViewJobs where project = old.project and view_ = old.name;
end; end;
create trigger cascadeReleaseSetUpdate create trigger cascadeViewUpdate
update of name on ReleaseSets update of name on Views
for each row begin for each row begin
update ReleaseSetJobs set release_ = new.name where project = old.project and release_ = old.name; update ViewJobs set view_ = new.name where project = old.project and view_ = old.name;
end; end;
create table ReleaseSetJobs ( create table ViewJobs (
project text not null, project text not null,
-- `release' is a reserved keyword in sqlite >= 3.6.8. We could view_ text not null,
-- quote them ("release") here, but since the Perl bindings don't
-- do that it still wouldn't work. So use `release_' instead.
release_ text not null,
job text not null, job text not null,
@ -386,19 +374,23 @@ create table ReleaseSetJobs (
-- be a separate table but I'm lazy. -- be a separate table but I'm lazy.
attrs text not null, attrs text not null,
-- If set, this is the primary job for the release. There can be -- If set, this is the primary job for the view. There can be
-- onlyt one such job per release set. -- only one such job per view.
isPrimary integer not null default 0, isPrimary integer not null default 0,
mayFail integer not null default 0,
description text, description text,
jobset text not null, jobset text not null,
-- If set, once there is a successful build for every job
-- associated with a build of the view's primary job, that set of
-- builds is automatically added as a release to the Releases
-- table.
autoRelease integer not null default 0,
primary key (project, release_, job, attrs), primary key (project, view_, job, attrs),
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, release_) references ReleaseSets(project, name) on delete cascade -- ignored by sqlite foreign key (project, view_) references Views(project, name) on delete cascade -- ignored by sqlite
foreign key (project, jobset) references Jobsets(project, name) on delete restrict -- ignored by sqlite foreign key (project, jobset) references Jobsets(project, name) on delete restrict -- ignored by sqlite
); );