* Put the project-related actions in a separate controller. Put the
[?]
Mar 4, 2009, 10:59 AM
FPK5LF53CFUEKFYJ3IYXT4UTVC6IITWJOCFATMC4PLHEUP5SIEAACDependencies
- [2]
U5KXLUJT - [3]
DSSX3RXD - [4]
7ADDUADT* Fix a weird bug. - [5]
LVAJ2TIZ* The owner parameter is only set for admin roles. - [6]
V4RNHJNR* Add a link to each project's homepage. Suggested by karltk. - [7]
A52HEFHQ* Allow builds to be restarted (if they failed with a transient error, - [8]
K2NXFFKH - [9]
C2DCO6AS - [10]
W6DC6K4I* Happy Javascript hacking. - [11]
PBFZEQLZ - [12]
UAPS46BQ - [13]
2AUODJBT - [14]
SZZ3PFYF - [15]
WJ35RSKA - [16]
XHVZXX6N - [17]
YTSIRIMK* Separate job status and all builds pages. - [18]
IE3SRMWZ* Show global and per-project statistics. - [19]
L2E6EVE2* Merged the Build and Job tables. - [20]
YTZOC7C5* Editing of jobset inputs. - [21]
CS7T2XFI - [22]
K5BEBWKM - [23]
H7CNGK4O* Log evaluation errors etc. in the DB. - [24]
GCHNNFZP - [25]
S66BOMVU* Added authentication. - [26]
FSE6N6S3* Make HEAD requests work when doing Nix closure downloads. - [27]
5NO7NCKT* Refactoring. - [28]
IN272KZW* Automatically keep all builds in the latest successful release in - [29]
JLDUSNUO* Unify rendering of finished and scheduled builds. - [30]
IK53RV4V - [31]
ZNFDFJHG* Provide a redirect to the latest successful release in a release set - [32]
HCKFTNQ6 - [33]
G6HJY2V4 - [34]
ELCI5T2A* Show the latest build for each job. - [35]
TQKGQ5R3 - [36]
7LKUAIGC - [37]
LBNVQXUB* Build the /build stuff in a separate controller. - [38]
UVMFS73T* Some jQuery / CSS hackery. - [39]
WYN733ST* Store build duration, handle cached builds. - [40]
RU7AQO7U* Role-based access control. Only admins can create projects. Only - [41]
GC4LTHDI* Check the owner. - [42]
UHANDRAR* Closure downloads: don't include the product number; it's not needed. - [43]
2JQ7WEFM - [44]
LQNBKF3D - [45]
OD5FSS5A* Quick hack to allow viewing of dependency graphs (via nix-store -q - [46]
HK32XC42 - [47]
2AIIYGI5* Show job status and all builds for a project. - [48]
SMM4HQTP* Put actions related to builds under /build (e.g. /log/<buildid> - [49]
JD27RBKM - [50]
YAPITGB3* Boolean inputs. - [51]
JFZNAYJX* Showing releases. - [52]
J5UVLXOK* Start of a basic Catalyst web interface. - [53]
FHF6IZJQ* Basic release management: releases are now dynamically computed as - [54]
EYNG4EL4* Regenerate the bindings from a clean sqlite database. - [55]
KFFNFE4D - [*]
PKPWUHUX* Idem. - [*]
SHBLLAVH* More global substitution. - [*]
7YBYT2LQ
Change contents
- file addition: ListBuilds.pm[10.19]
package Hydra::Base::Controller::ListBuilds;use strict;use warnings;use base 'Catalyst::Controller';use Hydra::Helper::Nix;use Hydra::Helper::CatalystUtils;sub jobstatus : Chained('get_builds') PathPart Args(0) {my ($self, $c) = @_;$c->stash->{latestBuilds} = getLatestBuilds($c, $c->stash->{allBuilds}, {});}sub all : Chained('get_builds') PathPart {my ($self, $c, $page) = @_;$c->stash->{template} = 'all.tt';$page = (defined $page ? int($page) : 1) || 1;my $resultsPerPage = 50;my $nrBuilds = scalar($c->stash->{allBuilds}->search({finished => 1}));$c->stash->{baseUri} = $c->uri_for($self->action_for("all"), $c->req->captures);$c->stash->{page} = $page;$c->stash->{resultsPerPage} = $resultsPerPage;$c->stash->{totalBuilds} = $nrBuilds;$c->stash->{builds} = [$c->stash->{allBuilds}->search({finished => 1}, {order_by => "timestamp DESC", rows => $resultsPerPage, page => $page})];}1; - replacement in src/Hydra/lib/Hydra/Base/Controller/Nix.pm at line 5
use parent 'Catalyst::Controller';use base 'Catalyst::Controller'; - replacement in src/Hydra/lib/Hydra/Controller/Build.pm at line 122
$c->response->content_type('image/png'); # !!!$c->res->content_type('image/png'); # !!! - replacement in src/Hydra/lib/Hydra/Controller/Build.pm at line 137
$c->response->content_type('image/png'); # !!!$c->res->content_type('image/png'); # !!! - replacement in src/Hydra/lib/Hydra/Controller/Build.pm at line 186
$c->response->redirect($c->uri_for($self->action_for("view_build"), $c->req->captures));$c->res->redirect($c->uri_for($self->action_for("view_build"), $c->req->captures)); - file addition: Project.pm[57.54]
package Hydra::Controller::Project;use strict;use warnings;use base 'Hydra::Base::Controller::ListBuilds';use Hydra::Helper::Nix;use Hydra::Helper::CatalystUtils;__PACKAGE__->config->{namespace} = '';sub project : Chained('/') PathPart('project') CaptureArgs(1) {my ($self, $c, $projectName) = @_;my $project = $c->model('DB::Projects')->find($projectName);notFound($c, "Project $projectName doesn't exist.") unless defined $project;$c->stash->{curProject} = $project;}sub view : Chained('project') PathPart('') Args(0) {my ($self, $c) = @_;$c->stash->{template} = 'project.tt';getBuildStats($c, scalar $c->stash->{curProject}->builds);}sub edit : Chained('project') PathPart Args(0) {my ($self, $c) = @_;requireProjectOwner($c, $c->stash->{curProject});$c->stash->{template} = 'project.tt';$c->stash->{edit} = 1;}sub submit : Chained('project') PathPart Args(0) {my ($self, $c) = @_;requireProjectOwner($c, $c->stash->{curProject});error($c, "Request must be POSTed.") if $c->request->method ne "POST";$c->model('DB')->schema->txn_do(sub {updateProject($c, $c->stash->{curProject});});$c->res->redirect($c->uri_for($self->action_for("view"), $c->req->captures));}sub delete : Chained('project') PathPart Args(0) {my ($self, $c) = @_;requireProjectOwner($c, $c->stash->{curProject});error($c, "Request must be POSTed.") if $c->request->method ne "POST";$c->model('DB')->schema->txn_do(sub {$c->stash->{curProject}->delete;});$c->res->redirect($c->uri_for("/"));}sub create : Path('create-project') {my ($self, $c) = @_;requireAdmin($c);$c->stash->{template} = 'project.tt';$c->stash->{create} = 1;$c->stash->{edit} = 1;}sub create_submit : Path('create-project/submit') {my ($self, $c) = @_;requireAdmin($c);my $projectName = trim $c->request->params->{name};$c->model('DB')->schema->txn_do(sub {# Note: $projectName is validated in updateProject,# which will abort the transaction if the name isn't# valid. Idem for the owner.my $project = $c->model('DB::Projects')->create({name => $projectName, displayname => "", owner => trim $c->request->params->{owner}});updateProject($c, $project);});$c->res->redirect($c->uri_for($self->action_for("view"), [$projectName]));}sub updateProject {my ($c, $project) = @_;my $projectName = trim $c->request->params->{name};error($c, "Invalid project name: " . ($projectName || "(empty)")) unless $projectName =~ /^[[:alpha:]]\w*$/;my $displayName = trim $c->request->params->{displayname};error($c, "Invalid display name: $displayName") if $displayName eq "";$project->name($projectName);$project->displayname($displayName);$project->description(trim $c->request->params->{description});$project->homepage(trim $c->request->params->{homepage});$project->enabled(trim($c->request->params->{enabled}) eq "1" ? 1 : 0);if ($c->check_user_roles('admin')) {my $owner = trim $c->request->params->{owner};error($c, "Invalid owner: $owner")unless defined $c->model('DB::Users')->find({username => $owner});$project->owner($owner);}$project->update;my %jobsetNames;foreach my $param (keys %{$c->request->params}) {next unless $param =~ /^jobset-(\w+)-name$/;my $baseName = $1;next if $baseName eq "template";my $jobsetName = trim $c->request->params->{"jobset-$baseName-name"};error($c, "Invalid jobset name: $jobsetName") unless $jobsetName =~ /^[[:alpha:]]\w*$/;# The Nix expression path must be relative and can't contain ".." elements.my $nixExprPath = trim $c->request->params->{"jobset-$baseName-nixexprpath"};error($c, "Invalid Nix expression path: $nixExprPath") if $nixExprPath !~ /^$relPathRE$/;my $nixExprInput = trim $c->request->params->{"jobset-$baseName-nixexprinput"};error($c, "Invalid Nix expression input name: $nixExprInput") unless $nixExprInput =~ /^\w+$/;$jobsetNames{$jobsetName} = 1;my $jobset;my $description = trim $c->request->params->{"jobset-$baseName-description"};if ($baseName =~ /^\d+$/) { # numeric base name is auto-generated, i.e. a new entry$jobset = $project->jobsets->create({ name => $jobsetName, description => $description, nixexprpath => $nixExprPath, nixexprinput => $nixExprInput});} else { # it's an existing jobset$jobset = ($project->jobsets->search({name => $baseName}))[0];die unless defined $jobset;$jobset->name($jobsetName);$jobset->description($description);$jobset->nixexprpath($nixExprPath);$jobset->nixexprinput($nixExprInput);$jobset->update;}my %inputNames;# Process the inputs of this jobset.foreach my $param (keys %{$c->request->params}) {next unless $param =~ /^jobset-$baseName-input-(\w+)-name$/;my $baseName2 = $1;next if $baseName2 eq "template";print STDERR "GOT INPUT: $baseName2\n";my $inputName = trim $c->request->params->{"jobset-$baseName-input-$baseName2-name"};error($c, "Invalid input name: $inputName") unless $inputName =~ /^[[:alpha:]]\w*$/;my $inputType = trim $c->request->params->{"jobset-$baseName-input-$baseName2-type"};error($c, "Invalid input type: $inputType") unless$inputType eq "svn" || $inputType eq "cvs" || $inputType eq "tarball" ||$inputType eq "string" || $inputType eq "path" || $inputType eq "boolean";$inputNames{$inputName} = 1;my $input;if ($baseName2 =~ /^\d+$/) { # numeric base name is auto-generated, i.e. a new entry$input = $jobset->jobsetinputs->create({ name => $inputName, type => $inputType});} else { # it's an existing jobset$input = ($jobset->jobsetinputs->search({name => $baseName2}))[0];die unless defined $input;$input->name($inputName);$input->type($inputType);$input->update;}# Update the values for this input. Just delete all the# current ones, then create the new values.$input->jobsetinputalts->delete_all;my $values = $c->request->params->{"jobset-$baseName-input-$baseName2-values"};$values = [] unless defined $values;$values = [$values] unless ref($values) eq 'ARRAY';my $altnr = 0;foreach my $value (@{$values}) {print STDERR "VALUE: $value\n";my $value = trim $value;error($c, "Invalid Boolean value: $value") if$inputType eq "boolean" && !($value eq "true" || $value eq "false");$input->jobsetinputalts->create({altnr => $altnr++, value => $value});}}# Get rid of deleted inputs.my @inputs = $jobset->jobsetinputs->all;foreach my $input (@inputs) {$input->delete unless defined $inputNames{$input->name};}}# Get rid of deleted jobsets, i.e., ones that are no longer submitted in the parameters.my @jobsets = $project->jobsets->all;foreach my $jobset (@jobsets) {$jobset->delete unless defined $jobsetNames{$jobset->name};}}# Hydra::Base::Controller::ListBuilds needs this.sub get_builds : Chained('project') PathPart('') CaptureArgs(0) {my ($self, $c) = @_;$c->stash->{allBuilds} = $c->stash->{curProject}->builds;}1; - edit in src/Hydra/lib/Hydra/Controller/Root.pm at line 6
use base 'Hydra::Base::Controller::ListBuilds'; - edit in src/Hydra/lib/Hydra/Controller/Root.pm at line 19[10.75]→[10.1224:1226](∅→∅),[10.1224]→[10.1224:1226](∅→∅),[10.1226]→[10.0:1](∅→∅),[10.1]→[10.1226:1227](∅→∅),[10.1226]→[10.1226:1227](∅→∅),[10.1227]→[10.2:73](∅→∅)
}sub trim {my $s = shift;$s =~ s/^\s+|\s+$//g;return $s; - edit in src/Hydra/lib/Hydra/Controller/Root.pm at line 22
sub getBuildStats {my ($c, $builds) = @_;$c->stash->{finishedBuilds} = $builds->search({finished => 1}) || 0;$c->stash->{succeededBuilds} = $builds->search({finished => 1, buildStatus => 0},{join => 'resultInfo'}) || 0;$c->stash->{scheduledBuilds} = $builds->search({finished => 0}) || 0;$c->stash->{busyBuilds} = $builds->search({finished => 0, busy => 1},{join => 'schedulingInfo'}) || 0;$c->stash->{totalBuildTime} = $builds->search({},{join => 'resultInfo', select => {sum => 'stoptime - starttime'}, as => ['sum']})->first->get_column('sum') || 0;} - edit in src/Hydra/lib/Hydra/Controller/Root.pm at line 63[10.299]→[10.0:4](∅→∅),[10.4]→[10.48:110](∅→∅),[10.110]→[8.0:576](∅→∅),[8.576]→[10.155:308](∅→∅),[10.155]→[10.155:308](∅→∅),[10.308]→[8.577:643](∅→∅),[10.372]→[10.522:526](∅→∅),[8.643]→[10.522:526](∅→∅),[10.522]→[10.522:526](∅→∅),[10.526]→[10.386:550](∅→∅),[10.550]→[10.575:614](∅→∅),[10.575]→[10.575:614](∅→∅),[10.614]→[10.0:51](∅→∅),[10.51]→[10.643:674](∅→∅),[10.643]→[10.643:674](∅→∅),[10.674]→[10.551:612](∅→∅),[10.612]→[10.751:752](∅→∅),[10.751]→[10.751:752](∅→∅),[10.752]→[10.613:650](∅→∅),[10.650]→[10.752:877](∅→∅),[10.752]→[10.752:877](∅→∅),[10.877]→[10.651:695](∅→∅),[10.695]→[10.937:1036](∅→∅),[10.937]→[10.937:1036](∅→∅),[10.299]→[10.0:4](∅→∅),[10.1036]→[10.0:4](∅→∅),[10.604]→[10.0:4](∅→∅),[10.4]→[10.696:821](∅→∅)
}# Return the latest build for each job.sub getLatestBuilds {my ($c, $builds, $extraAttrs) = @_;my @res = ();foreach my $job ($builds->search({},{group_by => ['project', 'attrname', 'system']})){my $attrs ={ project => $job->get_column('project'), attrname => $job->attrname, system => $job->system, finished => 1};my ($build) = $builds->search({ %$attrs, %$extraAttrs },{ join => 'resultInfo', order_by => 'timestamp DESC', rows => 1 } );push @res, $build if defined $build;}return [@res];}sub showJobStatus {my ($c, $builds) = @_;$c->stash->{template} = 'jobstatus.tt';# Get the latest finished build for each unique job.$c->stash->{latestBuilds} = getLatestBuilds($c, $builds, {});}sub jobstatus :Local {my ($self, $c) = @_;showJobStatus($c, $c->model('DB::Builds'));}sub showAllBuilds {my ($c, $baseUri, $page, $builds) = @_;$c->stash->{template} = 'all.tt';$page = (defined $page ? int($page) : 1) || 1;my $resultsPerPage = 50;my $nrBuilds = scalar($builds->search({finished => 1}));$c->stash->{baseUri} = $baseUri;$c->stash->{page} = $page;$c->stash->{resultsPerPage} = $resultsPerPage;$c->stash->{totalBuilds} = $nrBuilds;$c->stash->{builds} = [$builds->search({finished => 1}, {order_by => "timestamp DESC", rows => $resultsPerPage, page => $page})];}sub all :Local {my ($self, $c, $page) = @_;showAllBuilds($c, $c->uri_for("/all"), $page, $c->model('DB::Builds')); - edit in src/Hydra/lib/Hydra/Controller/Root.pm at line 221[10.823]→[10.823:825](∅→∅),[10.825]→[10.4:52](∅→∅),[10.4]→[10.4:52](∅→∅),[10.53]→[10.78:134](∅→∅),[10.134]→[10.104:197](∅→∅),[10.104]→[10.104:197](∅→∅),[10.197]→[10.135:266](∅→∅),[10.163]→[10.335:415](∅→∅),[10.266]→[10.335:415](∅→∅),[10.335]→[10.335:415](∅→∅),[10.415]→[10.267:335](∅→∅),[10.335]→[6.0:62](∅→∅),[6.62]→[10.335:411](∅→∅),[10.335]→[10.335:411](∅→∅),[10.411]→[5.0:251](∅→∅),[10.70]→[10.0:23](∅→∅),[10.182]→[10.0:23](∅→∅),[10.226]→[10.0:23](∅→∅),[5.251]→[10.0:23](∅→∅),[10.411]→[10.0:23](∅→∅),[10.478]→[10.0:23](∅→∅),[10.23]→[10.478:483](∅→∅),[10.478]→[10.478:483](∅→∅),[10.483]→[10.24:222](∅→∅),[10.222]→[10.412:490](∅→∅),[10.490]→[10.295:385](∅→∅),[10.295]→[10.295:385](∅→∅),[10.385]→[10.147:231](∅→∅),[10.231]→[10.491:577](∅→∅),[10.577]→[10.0:91](∅→∅)
sub updateProject {my ($c, $project) = @_;my $projectName = trim $c->request->params->{name};die "Invalid project name: $projectName" unless $projectName =~ /^[[:alpha:]]\w*$/;my $displayName = trim $c->request->params->{displayname};die "Invalid display name: $displayName" if $displayName eq "";$project->name($projectName);$project->displayname($displayName);$project->description(trim $c->request->params->{description});$project->homepage(trim $c->request->params->{homepage});$project->enabled(trim($c->request->params->{enabled}) eq "1" ? 1 : 0);if ($c->check_user_roles('admin')) {my $owner = trim $c->request->params->{owner};die "Invalid owner: $owner"unless defined $c->model('DB::Users')->find({username => $owner});$project->owner($owner);}$project->update;my %jobsetNames;foreach my $param (keys %{$c->request->params}) {next unless $param =~ /^jobset-(\w+)-name$/;my $baseName = $1;next if $baseName eq "template";my $jobsetName = trim $c->request->params->{"jobset-$baseName-name"};die "Invalid jobset name: $jobsetName" unless $jobsetName =~ /^[[:alpha:]]\w*$/;# The Nix expression path must be relative and can't contain ".." elements.my $nixExprPath = trim $c->request->params->{"jobset-$baseName-nixexprpath"};die "Invalid Nix expression path: $nixExprPath" if $nixExprPath !~ /^$relPathRE$/; - edit in src/Hydra/lib/Hydra/Controller/Root.pm at line 222
my $nixExprInput = trim $c->request->params->{"jobset-$baseName-nixexprinput"};die "Invalid Nix expression input name: $nixExprInput" unless $nixExprInput =~ /^\w+$/; - edit in src/Hydra/lib/Hydra/Controller/Root.pm at line 223[10.751]→[10.751:790](∅→∅),[10.790]→[10.0:21](∅→∅),[10.21]→[4.0:87](∅→∅),[10.21]→[10.790:883](∅→∅),[4.87]→[10.790:883](∅→∅),[10.790]→[10.790:883](∅→∅),[10.883]→[10.22:109](∅→∅),[10.109]→[4.88:134](∅→∅),[4.134]→[10.1111:1268](∅→∅),[10.759]→[10.1111:1268](∅→∅),[10.1111]→[10.1111:1268](∅→∅),[10.1268]→[10.110:185](∅→∅),[10.185]→[10.1343:1423](∅→∅),[10.1343]→[10.1343:1423](∅→∅),[10.1423]→[4.135:183](∅→∅),[4.183]→[10.1512:1639](∅→∅),[10.854]→[10.1512:1639](∅→∅),[10.1512]→[10.1512:1639](∅→∅),[10.1639]→[10.186:196](∅→∅),[10.196]→[10.0:25](∅→∅),[10.25]→[10.196:512](∅→∅),[10.196]→[10.196:512](∅→∅),[10.512]→[10.855:953](∅→∅),[10.953]→[10.605:696](∅→∅),[10.605]→[10.605:696](∅→∅),[10.696]→[10.954:1052](∅→∅),[10.1052]→[10.789:934](∅→∅),[10.789]→[10.789:934](∅→∅),[10.934]→[10.0:91](∅→∅),[10.91]→[10.998:999](∅→∅),[10.998]→[10.998:999](∅→∅),[10.999]→[10.26:80](∅→∅),[10.80]→[10.999:1119](∅→∅),[10.999]→[10.999:1119](∅→∅),[10.1119]→[10.81:243](∅→∅),[10.243]→[10.1119:1689](∅→∅),[10.1119]→[10.1119:1689](∅→∅),[10.1689]→[2.0:49](∅→∅),[2.49]→[10.1689:1873](∅→∅),[10.1689]→[10.1689:1873](∅→∅),[10.1873]→[10.92:364](∅→∅),[10.364]→[10.1960:1974](∅→∅),[10.1145]→[10.1960:1974](∅→∅),[10.1960]→[10.1960:1974](∅→∅),[10.1974]→[10.1639:1649](∅→∅),[10.1639]→[10.1639:1649](∅→∅),[10.1649]→[10.244:245](∅→∅),[10.245]→[3.0:37](∅→∅),[3.37]→[10.282:448](∅→∅),[10.282]→[10.282:448](∅→∅),[10.448]→[10.1649:1901](∅→∅),[10.1649]→[10.1649:1901](∅→∅),[10.505]→[10.0:25](∅→∅),[10.604]→[10.0:25](∅→∅),[10.1901]→[10.0:25](∅→∅),[10.226]→[10.0:25](∅→∅),[10.25]→[10.826:884](∅→∅),[10.352]→[10.66:108](∅→∅),[10.560]→[10.66:108](∅→∅),[10.884]→[10.66:108](∅→∅),[10.66]→[10.66:108](∅→∅),[10.108]→[10.0:5](∅→∅),[10.5]→[10.2919:2984](∅→∅),[10.2984]→[10.2903:2981](∅→∅),[10.1228]→[10.561:613](∅→∅),[10.2981]→[10.561:613](∅→∅),[10.280]→[10.561:613](∅→∅),[10.613]→[10.885:930](∅→∅),[10.930]→[10.613:662](∅→∅),[10.613]→[10.613:662](∅→∅),[10.662]→[10.931:1035](∅→∅),[10.1035]→[10.183:184](∅→∅),[10.662]→[10.183:184](∅→∅),[10.184]→[10.1036:1233](∅→∅),[10.267]→[10.227:228](∅→∅),[10.228]→[7.1001:1044](∅→∅),[10.301]→[10.267:276](∅→∅),[10.419]→[10.267:276](∅→∅),[7.1044]→[10.267:276](∅→∅),[10.267]→[10.267:276](∅→∅),[10.487]→[10.487:736](∅→∅),[10.736]→[10.1792:1871](∅→∅),[10.1871]→[10.833:843](∅→∅),[10.833]→[10.833:843](∅→∅),[10.843]→[10.662:663](∅→∅),[10.662]→[10.662:663](∅→∅),[10.663]→[10.844:1081](∅→∅),[10.1081]→[10.3076:3134](∅→∅),[10.3134]→[10.1146:1156](∅→∅),[10.1146]→[10.1146:1156](∅→∅),[10.1156]→[10.1253:1260](∅→∅),[10.1253]→[10.1253:1260](∅→∅),[10.141]→[10.744:792](∅→∅),[10.125]→[10.972:977](∅→∅),[10.792]→[10.972:977](∅→∅),[10.972]→[10.972:977](∅→∅),[10.977]→[10.317:475](∅→∅),[10.317]→[10.317:475](∅→∅),[10.226]→[10.0:3](∅→∅),[10.303]→[10.0:3](∅→∅),[10.475]→[10.0:3](∅→∅),[10.397]→[10.0:3](∅→∅),[10.3]→[10.1261:1289](∅→∅),[10.1289]→[10.353:391](∅→∅),[10.391]→[10.3:4](∅→∅),[10.1329]→[10.3:4](∅→∅),[10.3]→[10.3:4](∅→∅),[10.4]→[7.1045:1087](∅→∅),[7.1087]→[10.420:421](∅→∅),[10.1206]→[10.420:421](∅→∅),[10.421]→[10.3135:3193](∅→∅),[10.3193]→[10.486:532](∅→∅),[10.486]→[10.486:532](∅→∅),[10.532]→[10.1329:1330](∅→∅),[10.1329]→[10.1329:1330](∅→∅),[10.1330]→[10.1330:1388](∅→∅),[10.4]→[10.1330:1388](∅→∅),[10.1388]→[10.3194:3762](∅→∅),[10.3762]→[10.1993:2106](∅→∅),[10.1993]→[10.1993:2106](∅→∅)
$jobsetNames{$jobsetName} = 1;my $jobset;my $description = trim $c->request->params->{"jobset-$baseName-description"};if ($baseName =~ /^\d+$/) { # numeric base name is auto-generated, i.e. a new entry$jobset = $project->jobsets->create({ name => $jobsetName, description => $description, nixexprpath => $nixExprPath, nixexprinput => $nixExprInput});} else { # it's an existing jobset$jobset = ($project->jobsets->search({name => $baseName}))[0];die unless defined $jobset;$jobset->name($jobsetName);$jobset->description($description);$jobset->nixexprpath($nixExprPath);$jobset->nixexprinput($nixExprInput);$jobset->update;}my %inputNames;# Process the inputs of this jobset.foreach my $param (keys %{$c->request->params}) {next unless $param =~ /^jobset-$baseName-input-(\w+)-name$/;my $baseName2 = $1;next if $baseName2 eq "template";print STDERR "GOT INPUT: $baseName2\n";my $inputName = trim $c->request->params->{"jobset-$baseName-input-$baseName2-name"};die "Invalid input name: $inputName" unless $inputName =~ /^[[:alpha:]]\w*$/;my $inputType = trim $c->request->params->{"jobset-$baseName-input-$baseName2-type"};die "Invalid input type: $inputType" unless$inputType eq "svn" || $inputType eq "cvs" || $inputType eq "tarball" ||$inputType eq "string" || $inputType eq "path" || $inputType eq "boolean";$inputNames{$inputName} = 1;my $input;if ($baseName2 =~ /^\d+$/) { # numeric base name is auto-generated, i.e. a new entry$input = $jobset->jobsetinputs->create({ name => $inputName, type => $inputType});} else { # it's an existing jobset$input = ($jobset->jobsetinputs->search({name => $baseName2}))[0];die unless defined $input;$input->name($inputName);$input->type($inputType);$input->update;}# Update the values for this input. Just delete all the# current ones, then create the new values.$input->jobsetinputalts->delete_all;my $values = $c->request->params->{"jobset-$baseName-input-$baseName2-values"};$values = [] unless defined $values;$values = [$values] unless ref($values) eq 'ARRAY';my $altnr = 0;foreach my $value (@{$values}) {print STDERR "VALUE: $value\n";my $value = trim $value;die "Invalid Boolean value: $value" if$inputType eq "boolean" && !($value eq "true" || $value eq "false");$input->jobsetinputalts->create({altnr => $altnr++, value => $value});}}# Get rid of deleted inputs.my @inputs = $jobset->jobsetinputs->all;foreach my $input (@inputs) {$input->delete unless defined $inputNames{$input->name};}}# Get rid of deleted jobsets, i.e., ones that are no longer submitted in the parameters.my @jobsets = $project->jobsets->all;foreach my $jobset (@jobsets) {$jobset->delete unless defined $jobsetNames{$jobset->name};}}sub project :Local {my ($self, $c, $projectName, $subcommand, $arg) = @_;$c->stash->{template} = 'project.tt';my $project = $c->model('DB::Projects')->find($projectName);notFound($c, "Project $projectName doesn't exist.") if !defined $project;my $isPosted = $c->request->method eq "POST";$c->stash->{curProject} = $project;$subcommand = "" unless defined $subcommand;if ($subcommand eq "jobstatus") {return showJobStatus($c, scalar $project->builds);}elsif ($subcommand eq "all") {return showAllBuilds($c, $c->uri_for("/project", $projectName, "all"),$arg, scalar $project->builds);}elsif ($subcommand ne "") {requireProjectOwner($c, $project);if ($subcommand eq "edit") {$c->stash->{edit} = 1;}elsif ($subcommand eq "submit" && $isPosted) {$c->model('DB')->schema->txn_do(sub {updateProject($c, $project);});return $c->res->redirect($c->uri_for("/project", $project->name));}elsif ($subcommand eq "delete" && $isPosted) {$c->model('DB')->schema->txn_do(sub {$project->delete;});return $c->res->redirect($c->uri_for("/"));}else {error($c, "Unknown subcommand $subcommand.");}}getBuildStats($c, scalar $project->builds);$c->stash->{jobNames} =[$c->model('DB::Builds')->search({project => $projectName}, {select => [{distinct => 'attrname'}], as => ['attrname']})];}sub createproject :Local {my ($self, $c, $subcommand) = @_;requireLogin($c) if !$c->user_exists;error($c, "Only administrators can create projects.")unless $c->check_user_roles('admin');if (defined $subcommand && $subcommand eq "submit") {my $projectName = trim $c->request->params->{name};$c->model('DB')->schema->txn_do(sub {# Note: $projectName is validated in updateProject,# which will abort the transaction if the name isn't# valid. Idem for the owner.my $project = $c->model('DB::Projects')->create({name => $projectName, displayname => "", owner => trim $c->request->params->{owner}});updateProject($c, $project);});return $c->res->redirect($c->uri_for("/project", $projectName));}$c->stash->{template} = 'project.tt';$c->stash->{create} = 1;$c->stash->{edit} = 1;} - edit in src/Hydra/lib/Hydra/Controller/Root.pm at line 259
# Hydra::Base::Controller::ListBuilds needs this.sub get_builds : Chained('/') PathPart('') CaptureArgs(0) {my ($self, $c) = @_;$c->stash->{allBuilds} = $c->model('DB::Builds');} - replacement in src/Hydra/lib/Hydra/Helper/CatalystUtils.pm at line 9
getBuild error notFoundrequireLogin requireProjectOwnergetBuild getBuildStats getLatestBuildserror notFoundrequireLogin requireProjectOwner requireAdmintrim - edit in src/Hydra/lib/Hydra/Helper/CatalystUtils.pm at line 21
}sub getBuildStats {my ($c, $builds) = @_;$c->stash->{finishedBuilds} = $builds->search({finished => 1}) || 0;$c->stash->{succeededBuilds} = $builds->search({finished => 1, buildStatus => 0},{join => 'resultInfo'}) || 0;$c->stash->{scheduledBuilds} = $builds->search({finished => 0}) || 0;$c->stash->{busyBuilds} = $builds->search({finished => 0, busy => 1},{join => 'schedulingInfo'}) || 0;$c->stash->{totalBuildTime} = $builds->search({},{join => 'resultInfo', select => {sum => 'stoptime - starttime'}, as => ['sum']})->first->get_column('sum') || 0;}# Return the latest build for each job.sub getLatestBuilds {my ($c, $builds, $extraAttrs) = @_;my @res = ();foreach my $job ($builds->search({},{group_by => ['project', 'attrname', 'system']})){my $attrs ={ project => $job->get_column('project'), attrname => $job->attrname, system => $job->system, finished => 1};my ($build) = $builds->search({ %$attrs, %$extraAttrs },{ join => 'resultInfo', order_by => 'timestamp DESC', rows => 1 } );push @res, $build if defined $build;}return [@res]; - replacement in src/Hydra/lib/Hydra/Helper/CatalystUtils.pm at line 96
error($c, "Only the project owner or the administrator can perform this operation.")error($c, "Only the project owner or administrators can perform this operation.") - edit in src/Hydra/lib/Hydra/Helper/CatalystUtils.pm at line 101
sub requireAdmin {my ($c) = @_;requireLogin($c) if !$c->user_exists;error($c, "Only administrators can perform this operation.")unless $c->check_user_roles('admin');}sub trim {my $s = shift;$s =~ s/^\s+|\s+$//g;return $s;} - replacement in src/Hydra/root/error.tt at line 6
<p>I'm very sorry, but an error occurred: <span class="error-msg">[% FOREACH error IN errors %][% HTML.escape(error) %][% END %]</span></p><p>I'm very sorry, but the following error(s) occurred:</p><ul>[% FOREACH error IN errors %]<li><div class="error-msg">[% HTML.escape(error) %]</div></li>[% END %]</ul> - replacement in src/Hydra/root/layout.tt at line 135
[% INCLUDE makeLink uri = c.uri_for('/createproject') title = "Create project" %][% INCLUDE makeLink uri = c.uri_for('/create-project') title = "Create project" %] - replacement in src/Hydra/root/project.tt at line 142
<form action="[% IF create %][% c.uri_for('/createproject/submit') %][% ELSE %][% c.uri_for('/project' curProject.name 'submit') %][% END %]" method="post"><form action="[% IF create %][% c.uri_for('/create-project/submit') %][% ELSE %][% c.uri_for('/project' curProject.name 'submit') %][% END %]" method="post">