2009-03-04 10:59:14 +00:00
|
|
|
|
package Hydra::Controller::Project;
|
|
|
|
|
|
2013-10-03 17:23:41 +02:00
|
|
|
|
use utf8;
|
2009-03-04 10:59:14 +00:00
|
|
|
|
use strict;
|
|
|
|
|
use warnings;
|
|
|
|
|
use base 'Hydra::Base::Controller::ListBuilds';
|
|
|
|
|
use Hydra::Helper::Nix;
|
|
|
|
|
use Hydra::Helper::CatalystUtils;
|
|
|
|
|
|
2012-02-28 15:27:44 +01:00
|
|
|
|
|
2013-06-17 12:34:21 -04:00
|
|
|
|
sub projectChain :Chained('/') :PathPart('project') :CaptureArgs(1) {
|
2009-03-04 10:59:14 +00:00
|
|
|
|
my ($self, $c, $projectName) = @_;
|
2013-10-03 14:05:10 +02:00
|
|
|
|
$c->stash->{params}->{name} //= $projectName;
|
2013-01-22 14:41:02 +01:00
|
|
|
|
|
2014-05-01 13:03:34 +02:00
|
|
|
|
$c->stash->{project} = $c->model('DB::Projects')->find($projectName);
|
2013-06-17 12:34:21 -04:00
|
|
|
|
|
2016-10-27 16:46:20 +02:00
|
|
|
|
$c->stash->{isProjectOwner} = isProjectOwner($c, $c->stash->{project});
|
|
|
|
|
|
2013-10-03 14:16:21 +02:00
|
|
|
|
notFound($c, "Project ‘$projectName’ doesn't exist.")
|
2013-10-03 18:49:37 +02:00
|
|
|
|
if !$c->stash->{project} && !($c->action->name eq "project" and $c->request->method eq "PUT");
|
2009-03-04 10:59:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-28 15:27:44 +01:00
|
|
|
|
|
2013-06-17 12:34:21 -04:00
|
|
|
|
sub project :Chained('projectChain') :PathPart('') :Args(0) :ActionClass('REST::ForBrowsers') { }
|
|
|
|
|
|
|
|
|
|
sub project_GET {
|
2009-03-04 10:59:14 +00:00
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
|
|
|
|
|
|
$c->stash->{template} = 'project.tt';
|
|
|
|
|
|
2010-09-02 12:21:56 +00:00
|
|
|
|
$c->stash->{jobsets} = [jobsetOverview($c, $c->stash->{project})];
|
2013-02-21 01:23:42 +01:00
|
|
|
|
$c->stash->{releases} = [$c->stash->{project}->releases->search({},
|
|
|
|
|
{order_by => ["timestamp DESC"]})];
|
2013-06-17 12:34:21 -04:00
|
|
|
|
|
2013-10-03 14:05:10 +02:00
|
|
|
|
$self->status_ok($c, entity => $c->stash->{project});
|
2009-03-04 10:59:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-17 12:34:21 -04:00
|
|
|
|
sub project_PUT {
|
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
|
|
|
|
|
|
if (defined $c->stash->{project}) {
|
|
|
|
|
requireProjectOwner($c, $c->stash->{project});
|
2013-10-03 17:23:41 +02:00
|
|
|
|
|
2013-06-17 12:34:21 -04:00
|
|
|
|
txn_do($c->model('DB')->schema, sub {
|
|
|
|
|
updateProject($c, $c->stash->{project});
|
|
|
|
|
});
|
|
|
|
|
|
2013-10-03 17:23:41 +02:00
|
|
|
|
my $uri = $c->uri_for($self->action_for("project"), [$c->stash->{project}->name]) . "#tabs-configuration";
|
|
|
|
|
$self->status_ok($c, entity => { redirect => "$uri" });
|
2013-10-03 19:54:22 +02:00
|
|
|
|
|
|
|
|
|
$c->flash->{successMsg} = "The project configuration has been updated.";
|
2013-10-03 14:05:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else {
|
2013-06-17 12:34:21 -04:00
|
|
|
|
requireMayCreateProjects($c);
|
|
|
|
|
|
|
|
|
|
my $project;
|
|
|
|
|
txn_do($c->model('DB')->schema, sub {
|
|
|
|
|
# Note: $projectName is validated in updateProject,
|
|
|
|
|
# which will abort the transaction if the name isn't
|
|
|
|
|
# valid. Idem for the owner.
|
|
|
|
|
my $owner = $c->user->username;
|
|
|
|
|
$project = $c->model('DB::Projects')->create(
|
2013-10-03 17:23:41 +02:00
|
|
|
|
{ name => ".tmp", displayname => "", owner => $owner });
|
2013-06-17 12:34:21 -04:00
|
|
|
|
updateProject($c, $project);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
my $uri = $c->uri_for($self->action_for("project"), [$project->name]);
|
2013-10-03 17:23:41 +02:00
|
|
|
|
$self->status_created($c,
|
|
|
|
|
location => "$uri",
|
|
|
|
|
entity => { name => $project->name, uri => "$uri", redirect => "$uri", type => "project" });
|
2013-06-17 12:34:21 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-03 17:23:41 +02:00
|
|
|
|
sub project_DELETE {
|
2009-03-04 10:59:14 +00:00
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
|
|
2009-03-13 15:41:19 +00:00
|
|
|
|
requireProjectOwner($c, $c->stash->{project});
|
2009-03-04 10:59:14 +00:00
|
|
|
|
|
2013-10-03 17:23:41 +02:00
|
|
|
|
txn_do($c->model('DB')->schema, sub {
|
2013-10-03 19:42:44 +02:00
|
|
|
|
$c->stash->{project}->jobsetevals->delete;
|
|
|
|
|
$c->stash->{project}->builds->delete;
|
2013-10-03 17:23:41 +02:00
|
|
|
|
$c->stash->{project}->delete;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
my $uri = $c->res->redirect($c->uri_for("/"));
|
|
|
|
|
$self->status_ok($c, entity => { redirect => "$uri" });
|
2013-10-03 19:54:22 +02:00
|
|
|
|
|
|
|
|
|
$c->flash->{successMsg} = "The project has been deleted.";
|
2009-03-04 10:59:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-10-03 17:23:41 +02:00
|
|
|
|
sub edit : Chained('projectChain') PathPart Args(0) {
|
2009-03-04 10:59:14 +00:00
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
|
|
2013-10-03 13:06:16 +02:00
|
|
|
|
requireProjectOwner($c, $c->stash->{project});
|
|
|
|
|
|
2013-10-03 17:23:41 +02:00
|
|
|
|
$c->stash->{template} = 'edit-project.tt';
|
|
|
|
|
$c->stash->{edit} = 1;
|
2009-03-04 10:59:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-03-24 14:22:59 +00:00
|
|
|
|
sub requireMayCreateProjects {
|
|
|
|
|
my ($c) = @_;
|
2013-10-14 18:01:04 +02:00
|
|
|
|
requireUser($c);
|
|
|
|
|
accessDenied($c, "Only administrators or authorised users can perform this operation.")
|
2009-03-24 14:22:59 +00:00
|
|
|
|
unless $c->check_user_roles('admin') || $c->check_user_roles('create-projects');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-03-04 11:03:43 +00:00
|
|
|
|
sub create : Path('/create-project') {
|
2009-03-04 10:59:14 +00:00
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
|
|
2009-03-24 14:22:59 +00:00
|
|
|
|
requireMayCreateProjects($c);
|
2009-03-04 10:59:14 +00:00
|
|
|
|
|
2013-02-21 01:12:57 +01:00
|
|
|
|
$c->stash->{template} = 'edit-project.tt';
|
2009-03-04 10:59:14 +00:00
|
|
|
|
$c->stash->{create} = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-06-17 12:34:21 -04:00
|
|
|
|
sub create_jobset : Chained('projectChain') PathPart('create-jobset') Args(0) {
|
2009-04-02 16:15:57 +00:00
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
|
|
|
|
|
|
requireProjectOwner($c, $c->stash->{project});
|
2013-01-22 14:41:02 +01:00
|
|
|
|
|
2013-02-21 02:33:57 +01:00
|
|
|
|
$c->stash->{template} = 'edit-jobset.tt';
|
2009-04-02 16:15:57 +00:00
|
|
|
|
$c->stash->{create} = 1;
|
2013-09-21 14:47:52 +00:00
|
|
|
|
$c->stash->{totalShares} = getTotalShares($c->model('DB')->schema);
|
2009-04-02 16:15:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-03-04 10:59:14 +00:00
|
|
|
|
sub updateProject {
|
|
|
|
|
my ($c, $project) = @_;
|
2013-01-22 14:41:02 +01:00
|
|
|
|
|
2009-04-02 16:15:57 +00:00
|
|
|
|
my $owner = $project->owner;
|
2013-06-17 12:34:21 -04:00
|
|
|
|
if ($c->check_user_roles('admin') and defined $c->stash->{params}->{owner}) {
|
|
|
|
|
$owner = trim $c->stash->{params}->{owner};
|
2013-10-03 14:05:10 +02:00
|
|
|
|
error($c, "The user name ‘$owner’ does not exist.")
|
|
|
|
|
unless defined $c->model('DB::Users')->find($owner);
|
2009-03-04 10:59:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-10-03 14:05:10 +02:00
|
|
|
|
my $projectName = $c->stash->{params}->{name};
|
2013-10-03 17:23:41 +02:00
|
|
|
|
error($c, "Invalid project identifier ‘$projectName’.") if $projectName !~ /^$projectNameRE$/;
|
2013-10-03 14:05:10 +02:00
|
|
|
|
|
2013-10-03 17:23:41 +02:00
|
|
|
|
error($c, "Cannot rename project to ‘$projectName’ since that identifier is already taken.")
|
2013-10-03 14:05:10 +02:00
|
|
|
|
if $projectName ne $project->name && defined $c->model('DB::Projects')->find($projectName);
|
2013-01-22 14:41:02 +01:00
|
|
|
|
|
2013-06-17 12:34:21 -04:00
|
|
|
|
my $displayName = trim $c->stash->{params}->{displayname};
|
2013-10-03 14:05:10 +02:00
|
|
|
|
error($c, "You must specify a display name.") if $displayName eq "";
|
2012-04-17 16:53:11 +02:00
|
|
|
|
|
2009-04-02 16:15:57 +00:00
|
|
|
|
$project->update(
|
|
|
|
|
{ name => $projectName
|
|
|
|
|
, displayname => $displayName
|
2013-06-17 12:34:21 -04:00
|
|
|
|
, description => trim($c->stash->{params}->{description})
|
|
|
|
|
, homepage => trim($c->stash->{params}->{homepage})
|
|
|
|
|
, enabled => defined $c->stash->{params}->{enabled} ? 1 : 0
|
|
|
|
|
, hidden => defined $c->stash->{params}->{visible} ? 0 : 1
|
2009-04-02 16:15:57 +00:00
|
|
|
|
, owner => $owner
|
Enable declarative projects.
This allows fully declarative project specifications. This is best
illustrated by example:
* I create a new project, setting the declarative spec file to
"spec.json" and the declarative input to a git repo pointing
at git://github.com/shlevy/declarative-hydra-example.git
* hydra creates a special ".jobsets" jobset alongside the project
* Just before evaluating the ".jobsets" jobset, hydra fetches
declarative-hydra-example.git, reads spec.json as a jobset spec,
and updates the jobset's configuration accordingly:
{
"enabled": 1,
"hidden": false,
"description": "Jobsets",
"nixexprinput": "src",
"nixexprpath": "default.nix",
"checkinterval": 300,
"schedulingshares": 100,
"enableemail": false,
"emailoverride": "",
"keepnr": 3,
"inputs": {
"src": { "type": "git", "value": "git://github.com/shlevy/declarative-hydra-example.git", "emailresponsible": false },
"nixpkgs": { "type": "git", "value": "git://github.com/NixOS/nixpkgs.git release-16.03", "emailresponsible": false }
}
}
* When the "jobsets" job of the ".jobsets" jobset completes, hydra
reads its output as a JSON representation of a dictionary of
jobset specs and creates a jobset named "master" configured
accordingly (In this example, this is the same configuration as
.jobsets itself, except using release.nix instead of default.nix):
{
"enabled": 1,
"hidden": false,
"description": "js",
"nixexprinput": "src",
"nixexprpath": "release.nix",
"checkinterval": 300,
"schedulingshares": 100,
"enableemail": false,
"emailoverride": "",
"keepnr": 3,
"inputs": {
"src": { "type": "git", "value": "git://github.com/shlevy/declarative-hydra-example.git", "emailresponsible": false },
"nixpkgs": { "type": "git", "value": "git://github.com/NixOS/nixpkgs.git release-16.03", "emailresponsible": false }
}
}
2016-03-11 18:14:58 -05:00
|
|
|
|
, declfile => trim($c->stash->{params}->{declfile})
|
|
|
|
|
, decltype => trim($c->stash->{params}->{decltype})
|
|
|
|
|
, declvalue => trim($c->stash->{params}->{declvalue})
|
2009-04-02 16:15:57 +00:00
|
|
|
|
});
|
Enable declarative projects.
This allows fully declarative project specifications. This is best
illustrated by example:
* I create a new project, setting the declarative spec file to
"spec.json" and the declarative input to a git repo pointing
at git://github.com/shlevy/declarative-hydra-example.git
* hydra creates a special ".jobsets" jobset alongside the project
* Just before evaluating the ".jobsets" jobset, hydra fetches
declarative-hydra-example.git, reads spec.json as a jobset spec,
and updates the jobset's configuration accordingly:
{
"enabled": 1,
"hidden": false,
"description": "Jobsets",
"nixexprinput": "src",
"nixexprpath": "default.nix",
"checkinterval": 300,
"schedulingshares": 100,
"enableemail": false,
"emailoverride": "",
"keepnr": 3,
"inputs": {
"src": { "type": "git", "value": "git://github.com/shlevy/declarative-hydra-example.git", "emailresponsible": false },
"nixpkgs": { "type": "git", "value": "git://github.com/NixOS/nixpkgs.git release-16.03", "emailresponsible": false }
}
}
* When the "jobsets" job of the ".jobsets" jobset completes, hydra
reads its output as a JSON representation of a dictionary of
jobset specs and creates a jobset named "master" configured
accordingly (In this example, this is the same configuration as
.jobsets itself, except using release.nix instead of default.nix):
{
"enabled": 1,
"hidden": false,
"description": "js",
"nixexprinput": "src",
"nixexprpath": "release.nix",
"checkinterval": 300,
"schedulingshares": 100,
"enableemail": false,
"emailoverride": "",
"keepnr": 3,
"inputs": {
"src": { "type": "git", "value": "git://github.com/shlevy/declarative-hydra-example.git", "emailresponsible": false },
"nixpkgs": { "type": "git", "value": "git://github.com/NixOS/nixpkgs.git release-16.03", "emailresponsible": false }
}
}
2016-03-11 18:14:58 -05:00
|
|
|
|
if (length($project->declfile)) {
|
|
|
|
|
$project->jobsets->update_or_create(
|
|
|
|
|
{ name=> ".jobsets"
|
|
|
|
|
, nixexprinput => ""
|
|
|
|
|
, nixexprpath => ""
|
|
|
|
|
, emailoverride => ""
|
|
|
|
|
, triggertime => time
|
|
|
|
|
});
|
|
|
|
|
}
|
2009-03-04 10:59:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Hydra::Base::Controller::ListBuilds needs this.
|
2013-06-17 12:34:21 -04:00
|
|
|
|
sub get_builds : Chained('projectChain') PathPart('') CaptureArgs(0) {
|
2009-03-04 10:59:14 +00:00
|
|
|
|
my ($self, $c) = @_;
|
2009-03-13 15:41:19 +00:00
|
|
|
|
$c->stash->{allBuilds} = $c->stash->{project}->builds;
|
2009-04-03 15:37:21 +00:00
|
|
|
|
$c->stash->{latestSucceeded} = $c->model('DB')->resultset('LatestSucceededForProject')
|
|
|
|
|
->search({}, {bind => [$c->stash->{project}->name]});
|
2009-03-13 15:41:19 +00:00
|
|
|
|
$c->stash->{channelBaseName} = $c->stash->{project}->name;
|
2009-03-04 10:59:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-06-17 12:34:21 -04:00
|
|
|
|
sub create_release : Chained('projectChain') PathPart('create-release') Args(0) {
|
2009-10-21 15:44:17 +00:00
|
|
|
|
my ($self, $c) = @_;
|
2009-10-23 09:58:23 +00:00
|
|
|
|
requireProjectOwner($c, $c->stash->{project});
|
|
|
|
|
$c->stash->{template} = 'edit-release.tt';
|
|
|
|
|
$c->stash->{create} = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-06-17 12:34:21 -04:00
|
|
|
|
sub create_release_submit : Chained('projectChain') PathPart('create-release/submit') Args(0) {
|
2009-10-23 09:58:23 +00:00
|
|
|
|
my ($self, $c) = @_;
|
2013-01-22 14:41:02 +01:00
|
|
|
|
|
2009-10-23 09:58:23 +00:00
|
|
|
|
requireProjectOwner($c, $c->stash->{project});
|
|
|
|
|
|
|
|
|
|
my $releaseName = $c->request->params->{name};
|
|
|
|
|
|
|
|
|
|
my $release;
|
|
|
|
|
txn_do($c->model('DB')->schema, sub {
|
|
|
|
|
# Note: $releaseName is validated in updateRelease, which will
|
|
|
|
|
# abort the transaction if the name isn't valid.
|
|
|
|
|
$release = $c->stash->{project}->releases->create(
|
|
|
|
|
{ name => $releaseName
|
|
|
|
|
, timestamp => time
|
|
|
|
|
});
|
|
|
|
|
Hydra::Controller::Release::updateRelease($c, $release);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$c->res->redirect($c->uri_for($c->controller('Release')->action_for('view'),
|
|
|
|
|
[$c->stash->{project}->name, $release->name]));
|
2009-10-21 15:44:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-03-04 10:59:14 +00:00
|
|
|
|
1;
|