Merge branch 'master' into persona
Conflicts: src/lib/Hydra/Helper/CatalystUtils.pm src/root/layout.tt src/root/topbar.tt src/root/user.tt
This commit is contained in:
@ -15,8 +15,6 @@ use Digest::SHA qw(sha256_hex);
|
||||
use Text::Diff;
|
||||
use File::Slurp;
|
||||
|
||||
# !!! Rewrite this to use View::JSON.
|
||||
|
||||
|
||||
sub api : Chained('/') PathPart('api') CaptureArgs(0) {
|
||||
my ($self, $c) = @_;
|
||||
@ -24,32 +22,6 @@ sub api : Chained('/') PathPart('api') CaptureArgs(0) {
|
||||
}
|
||||
|
||||
|
||||
sub projectToHash {
|
||||
my ($project) = @_;
|
||||
return {
|
||||
name => $project->name,
|
||||
description => $project->description
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
sub projects : Chained('api') PathPart('projects') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
my @projects = $c->model('DB::Projects')->search({hidden => 0}, {order_by => 'name'});
|
||||
|
||||
my @list;
|
||||
foreach my $p (@projects) {
|
||||
push @list, projectToHash($p);
|
||||
}
|
||||
|
||||
$c->stash->{'plain'} = {
|
||||
data => scalar (JSON::Any->objToJson(\@list))
|
||||
};
|
||||
$c->forward('Hydra::View::Plain');
|
||||
}
|
||||
|
||||
|
||||
sub buildToHash {
|
||||
my ($build) = @_;
|
||||
my $result = {
|
||||
|
@ -34,8 +34,12 @@ sub machines : Chained('admin') PathPart('machines') Args(0) {
|
||||
|
||||
sub clear_queue_non_current : Chained('admin') PathPart('clear-queue-non-current') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
my $time = time();
|
||||
$c->model('DB::Builds')->search({finished => 0, iscurrent => 0, busy => 0})->update({ finished => 1, buildstatus => 4, starttime => $time, stoptime => $time });
|
||||
my $builds = $c->model('DB::Builds')->search(
|
||||
{ finished => 0, busy => 0
|
||||
, id => { -not_in => \ "select build from JobsetEvalMembers where eval in (select max(id) from JobsetEvals where hasNewBuilds = 1 group by project, jobset)" }
|
||||
});
|
||||
my $n = cancelBuilds($c->model('DB')->schema, $builds);
|
||||
$c->flash->{successMsg} = "$n builds have been cancelled.";
|
||||
$c->res->redirect($c->request->referer // "/admin");
|
||||
}
|
||||
|
||||
@ -49,19 +53,11 @@ sub clearfailedcache : Chained('admin') PathPart('clear-failed-cache') Args(0) {
|
||||
|
||||
sub clearvcscache : Chained('admin') PathPart('clear-vcs-cache') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
print STDERR "Clearing path cache\n";
|
||||
$c->model('DB::CachedPathInputs')->delete_all;
|
||||
|
||||
print STDERR "Clearing git cache\n";
|
||||
$c->model('DB::CachedGitInputs')->delete_all;
|
||||
|
||||
print STDERR "Clearing subversion cache\n";
|
||||
$c->model('DB::CachedSubversionInputs')->delete_all;
|
||||
|
||||
print STDERR "Clearing bazaar cache\n";
|
||||
$c->model('DB::CachedBazaarInputs')->delete_all;
|
||||
|
||||
$c->model('DB::CachedPathInputs')->delete;
|
||||
$c->model('DB::CachedGitInputs')->delete;
|
||||
$c->model('DB::CachedSubversionInputs')->delete;
|
||||
$c->model('DB::CachedBazaarInputs')->delete;
|
||||
$c->flash->{successMsg} = "VCS caches have been cleared.";
|
||||
$c->res->redirect($c->request->referer // "/admin");
|
||||
}
|
||||
|
||||
|
@ -35,18 +35,18 @@ sub buildChain :Chained('/') :PathPart('build') :CaptureArgs(1) {
|
||||
|
||||
|
||||
sub findBuildStepByOutPath {
|
||||
my ($self, $c, $path, $status) = @_;
|
||||
my ($self, $c, $path) = @_;
|
||||
return $c->model('DB::BuildSteps')->search(
|
||||
{ path => $path, busy => 0, status => $status },
|
||||
{ join => ["buildstepoutputs"], order_by => ["stopTime"], limit => 1 })->single;
|
||||
{ path => $path, busy => 0 },
|
||||
{ join => ["buildstepoutputs"], order_by => ["status", "stopTime"], rows => 1 })->single;
|
||||
}
|
||||
|
||||
|
||||
sub findBuildStepByDrvPath {
|
||||
my ($self, $c, $drvPath, $status) = @_;
|
||||
my ($self, $c, $drvPath) = @_;
|
||||
return $c->model('DB::BuildSteps')->search(
|
||||
{ drvpath => $drvPath, busy => 0, status => $status },
|
||||
{ order_by => ["stopTime"], limit => 1 })->single;
|
||||
{ drvpath => $drvPath, busy => 0 },
|
||||
{ order_by => ["status", "stopTime"], rows => 1 })->single;
|
||||
}
|
||||
|
||||
|
||||
@ -60,7 +60,6 @@ sub build_GET {
|
||||
$c->stash->{template} = 'build.tt';
|
||||
$c->stash->{available} = all { isValidPath($_->path) } $build->buildoutputs->all;
|
||||
$c->stash->{drvAvailable} = isValidPath $build->drvpath;
|
||||
$c->stash->{flashMsg} = $c->flash->{buildMsg};
|
||||
|
||||
if (!$build->finished && $build->busy) {
|
||||
$c->stash->{logtext} = read_file($build->logfile, err_mode => 'quiet') // "";
|
||||
@ -68,8 +67,7 @@ sub build_GET {
|
||||
|
||||
if ($build->finished && $build->iscachedbuild) {
|
||||
my $path = ($build->buildoutputs)[0]->path or die;
|
||||
my $cachedBuildStep = findBuildStepByOutPath($self, $c, $path,
|
||||
$build->buildstatus == 0 || $build->buildstatus == 6 ? 0 : 1);
|
||||
my $cachedBuildStep = findBuildStepByOutPath($self, $c, $path);
|
||||
$c->stash->{cachedBuild} = $cachedBuildStep->build if defined $cachedBuildStep;
|
||||
}
|
||||
|
||||
@ -95,25 +93,16 @@ sub build_GET {
|
||||
|
||||
# Get the first eval of which this build was a part.
|
||||
($c->stash->{nrEvals}) = $c->stash->{build}->jobsetevals->search({ hasnewbuilds => 1 })->count;
|
||||
($c->stash->{eval}) = $c->stash->{build}->jobsetevals->search(
|
||||
$c->stash->{eval} = $c->stash->{build}->jobsetevals->search(
|
||||
{ hasnewbuilds => 1},
|
||||
{ limit => 1, order_by => ["id"] });
|
||||
{ rows => 1, order_by => ["id"] })->single;
|
||||
$self->status_ok(
|
||||
$c,
|
||||
entity => $c->model('DB::Builds')->find($build->id,{
|
||||
columns => [
|
||||
'id',
|
||||
'finished',
|
||||
'timestamp',
|
||||
'buildstatus',
|
||||
'job',
|
||||
'project',
|
||||
'jobset',
|
||||
'starttime',
|
||||
'stoptime',
|
||||
]
|
||||
})
|
||||
entity => $build
|
||||
);
|
||||
|
||||
# If this is an aggregate build, get its constituents.
|
||||
$c->stash->{constituents} = [$c->stash->{build}->constituents_->search({}, {order_by => ["job"]})];
|
||||
}
|
||||
|
||||
|
||||
@ -125,35 +114,43 @@ sub view_nixlog : Chained('buildChain') PathPart('nixlog') {
|
||||
|
||||
$c->stash->{step} = $step;
|
||||
|
||||
showLog($c, $step->drvpath, $mode);
|
||||
showLog($c, $mode, $step->drvpath, map { $_->path } $step->buildstepoutputs->all);
|
||||
}
|
||||
|
||||
|
||||
sub view_log : Chained('buildChain') PathPart('log') {
|
||||
my ($self, $c, $mode) = @_;
|
||||
showLog($c, $c->stash->{build}->drvpath, $mode);
|
||||
showLog($c, $mode, $c->stash->{build}->drvpath, map { $_->path } $c->stash->{build}->buildoutputs->all);
|
||||
}
|
||||
|
||||
|
||||
sub showLog {
|
||||
my ($c, $drvPath, $mode) = @_;
|
||||
my ($c, $mode, $drvPath, @outPaths) = @_;
|
||||
|
||||
my $logPath = getDrvLogPath($drvPath);
|
||||
my $logPath = findLog($c, $drvPath, @outPaths);
|
||||
|
||||
notFound($c, "The build log of derivation ‘$drvPath’ is not available.") unless defined $logPath;
|
||||
|
||||
my $size = stat($logPath)->size;
|
||||
error($c, "This build log is too big to display ($size bytes).")
|
||||
if $size >= 64 * 1024 * 1024;
|
||||
|
||||
if (!$mode) {
|
||||
# !!! quick hack
|
||||
my $pipeline = "nix-store -l $drvPath"
|
||||
my $pipeline = ($logPath =~ /.bz2$/ ? "bzip2 -d < $logPath" : "cat $logPath")
|
||||
. " | nix-log2xml | xsltproc " . $c->path_to("xsl/mark-errors.xsl") . " -"
|
||||
. " | xsltproc " . $c->path_to("xsl/log2html.xsl") . " - | tail -n +2";
|
||||
$c->stash->{template} = 'log.tt';
|
||||
$c->stash->{logtext} = `$pipeline`;
|
||||
$c->stash->{logtext} = `ulimit -t 5 ; $pipeline`;
|
||||
}
|
||||
|
||||
elsif ($mode eq "raw") {
|
||||
$c->stash->{'plain'} = { data => (scalar logContents($drvPath)) || " " };
|
||||
$c->forward('Hydra::View::Plain');
|
||||
if ($logPath !~ /.bz2$/) {
|
||||
$c->serve_static_file($logPath);
|
||||
} else {
|
||||
$c->stash->{'plain'} = { data => (scalar logContents($logPath)) || " " };
|
||||
$c->forward('Hydra::View::Plain');
|
||||
}
|
||||
}
|
||||
|
||||
elsif ($mode eq "tail-reload") {
|
||||
@ -162,12 +159,12 @@ sub showLog {
|
||||
$c->stash->{url} = $url;
|
||||
$c->stash->{reload} = !$c->stash->{build}->finished && $c->stash->{build}->busy;
|
||||
$c->stash->{title} = "";
|
||||
$c->stash->{contents} = (scalar logContents($drvPath, 50)) || " ";
|
||||
$c->stash->{contents} = (scalar logContents($logPath, 50)) || " ";
|
||||
$c->stash->{template} = 'plain-reload.tt';
|
||||
}
|
||||
|
||||
elsif ($mode eq "tail") {
|
||||
$c->stash->{'plain'} = { data => (scalar logContents($drvPath, 50)) || " " };
|
||||
$c->stash->{'plain'} = { data => (scalar logContents($logPath, 50)) || " " };
|
||||
$c->forward('Hydra::View::Plain');
|
||||
}
|
||||
|
||||
@ -238,6 +235,21 @@ sub download : Chained('buildChain') PathPart {
|
||||
}
|
||||
|
||||
|
||||
sub output : Chained('buildChain') PathPart Args(1) {
|
||||
my ($self, $c, $outputName) = @_;
|
||||
my $build = $c->stash->{build};
|
||||
|
||||
error($c, "This build is not finished yet.") unless $build->finished;
|
||||
my $output = $build->buildoutputs->find({name => $outputName});
|
||||
notFound($c, "This build has no output named ‘$outputName’") unless defined $output;
|
||||
error($c, "Output is not available.") unless isValidPath $output->path;
|
||||
|
||||
$c->response->header('Content-Disposition', "attachment; filename=\"build-${\$build->id}-${\$outputName}.nar.bz2\"");
|
||||
$c->stash->{current_view} = 'NixNAR';
|
||||
$c->stash->{storePath} = $output->path;
|
||||
}
|
||||
|
||||
|
||||
# Redirect to a download with the given type. Useful when you want to
|
||||
# link to some build product of the latest build (i.e. in conjunction
|
||||
# with the .../latest redirect).
|
||||
@ -269,7 +281,7 @@ sub contents : Chained('buildChain') PathPart Args(1) {
|
||||
notFound($c, "Product $path has disappeared.") unless -e $path;
|
||||
|
||||
# Sanitize $path to prevent shell injection attacks.
|
||||
$path =~ /^\/[\/[A-Za-z0-9_\-\.=]+$/ or die "Filename contains illegal characters.\n";
|
||||
$path =~ /^\/[\/[A-Za-z0-9_\-\.=+:]+$/ or die "Filename contains illegal characters.\n";
|
||||
|
||||
# FIXME: don't use shell invocations below.
|
||||
|
||||
@ -339,8 +351,8 @@ sub getDependencyGraph {
|
||||
{ path => $path
|
||||
, name => $name
|
||||
, buildStep => $runtime
|
||||
? findBuildStepByOutPath($self, $c, $path, 0)
|
||||
: findBuildStepByDrvPath($self, $c, $path, 0)
|
||||
? findBuildStepByOutPath($self, $c, $path)
|
||||
: findBuildStepByDrvPath($self, $c, $path)
|
||||
};
|
||||
$$done{$path} = $node;
|
||||
my @refs;
|
||||
@ -409,49 +421,22 @@ sub nix : Chained('buildChain') PathPart('nix') CaptureArgs(0) {
|
||||
|
||||
sub restart : Chained('buildChain') PathPart Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
my $build = $c->stash->{build};
|
||||
|
||||
requireProjectOwner($c, $build->project);
|
||||
|
||||
my $drvpath = $build->drvpath;
|
||||
error($c, "This build cannot be restarted.")
|
||||
unless $build->finished && -f $drvpath;
|
||||
|
||||
restartBuild($c->model('DB')->schema, $build);
|
||||
|
||||
$c->flash->{buildMsg} = "Build has been restarted.";
|
||||
|
||||
my $n = restartBuilds($c->model('DB')->schema, $c->model('DB::Builds')->search({ id => $build->id }));
|
||||
error($c, "This build cannot be restarted.") if $n != 1;
|
||||
$c->flash->{successMsg} = "Build has been restarted.";
|
||||
$c->res->redirect($c->uri_for($self->action_for("build"), $c->req->captures));
|
||||
}
|
||||
|
||||
|
||||
sub cancel : Chained('buildChain') PathPart Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
my $build = $c->stash->{build};
|
||||
|
||||
requireProjectOwner($c, $build->project);
|
||||
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
error($c, "This build cannot be cancelled.")
|
||||
if $build->finished || $build->busy;
|
||||
|
||||
# !!! Actually, it would be nice to be able to cancel busy
|
||||
# builds as well, but we would have to send a signal or
|
||||
# something to the build process.
|
||||
|
||||
my $time = time();
|
||||
$build->update(
|
||||
{ finished => 1, busy => 0
|
||||
, iscachedbuild => 0, buildstatus => 4 # = cancelled
|
||||
, starttime => $time
|
||||
, stoptime => $time
|
||||
});
|
||||
});
|
||||
|
||||
$c->flash->{buildMsg} = "Build has been cancelled.";
|
||||
|
||||
my $n = cancelBuilds($c->model('DB')->schema, $c->model('DB::Builds')->search({ id => $build->id }));
|
||||
error($c, "This build cannot be cancelled.") if $n != 1;
|
||||
$c->flash->{successMsg} = "Build has been cancelled.";
|
||||
$c->res->redirect($c->uri_for($self->action_for("build"), $c->req->captures));
|
||||
}
|
||||
|
||||
@ -472,7 +457,7 @@ sub keep : Chained('buildChain') PathPart Args(1) {
|
||||
$build->update({keep => $keep});
|
||||
});
|
||||
|
||||
$c->flash->{buildMsg} =
|
||||
$c->flash->{successMsg} =
|
||||
$keep ? "Build will be kept." : "Build will not be kept.";
|
||||
|
||||
$c->res->redirect($c->uri_for($self->action_for("build"), $c->req->captures));
|
||||
@ -502,89 +487,12 @@ sub add_to_release : Chained('buildChain') PathPart('add-to-release') Args(0) {
|
||||
|
||||
$release->releasemembers->create({build => $build->id, description => $build->description});
|
||||
|
||||
$c->flash->{buildMsg} = "Build added to project <tt>$releaseName</tt>.";
|
||||
$c->flash->{successMsg} = "Build added to project <tt>$releaseName</tt>.";
|
||||
|
||||
$c->res->redirect($c->uri_for($self->action_for("build"), $c->req->captures));
|
||||
}
|
||||
|
||||
|
||||
sub clone : Chained('buildChain') PathPart('clone') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
my $build = $c->stash->{build};
|
||||
|
||||
requireProjectOwner($c, $build->project);
|
||||
|
||||
$c->stash->{template} = 'clone-build.tt';
|
||||
}
|
||||
|
||||
|
||||
sub clone_submit : Chained('buildChain') PathPart('clone/submit') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
my $build = $c->stash->{build};
|
||||
|
||||
requireProjectOwner($c, $build->project);
|
||||
|
||||
my ($nixExprPath, $nixExprInputName) = Hydra::Controller::Jobset::nixExprPathFromParams $c;
|
||||
|
||||
# When the expression is in a .scm file, assume it's a Guile + Guix
|
||||
# build expression.
|
||||
my $exprType =
|
||||
$c->request->params->{"nixexprpath"} =~ /.scm$/ ? "guile" : "nix";
|
||||
|
||||
my $jobName = trim $c->request->params->{"jobname"};
|
||||
error($c, "Invalid job name: $jobName") if $jobName !~ /^$jobNameRE$/;
|
||||
|
||||
my $inputInfo = {};
|
||||
|
||||
foreach my $param (keys %{$c->request->params}) {
|
||||
next unless $param =~ /^input-(\w+)-name$/;
|
||||
my $baseName = $1;
|
||||
my ($inputName, $inputType) =
|
||||
Hydra::Controller::Jobset::checkInput($c, $baseName);
|
||||
my $inputValue = Hydra::Controller::Jobset::checkInputValue(
|
||||
$c, $inputType, $c->request->params->{"input-$baseName-value"});
|
||||
eval {
|
||||
# !!! fetchInput can take a long time, which might cause
|
||||
# the current HTTP request to time out. So maybe this
|
||||
# should be done asynchronously. But then error reporting
|
||||
# becomes harder.
|
||||
my $info = fetchInput(
|
||||
$c->hydra_plugins, $c->model('DB'), $build->project, $build->jobset,
|
||||
$inputName, $inputType, $inputValue);
|
||||
push @{$$inputInfo{$inputName}}, $info if defined $info;
|
||||
};
|
||||
error($c, $@) if $@;
|
||||
}
|
||||
|
||||
my ($jobs, $nixExprInput) = evalJobs($inputInfo, $exprType, $nixExprInputName, $nixExprPath);
|
||||
|
||||
my $job;
|
||||
foreach my $j (@{$jobs->{job}}) {
|
||||
print STDERR $j->{jobName}, "\n";
|
||||
if ($j->{jobName} eq $jobName) {
|
||||
error($c, "Nix expression returned multiple builds for job $jobName.")
|
||||
if $job;
|
||||
$job = $j;
|
||||
}
|
||||
}
|
||||
|
||||
error($c, "Nix expression did not return a job named $jobName.") unless $job;
|
||||
|
||||
my %currentBuilds;
|
||||
my $newBuild = checkBuild(
|
||||
$c->model('DB'), $build->project, $build->jobset,
|
||||
$inputInfo, $nixExprInput, $job, \%currentBuilds, undef, {});
|
||||
|
||||
error($c, "This build has already been performed.") unless $newBuild;
|
||||
|
||||
$c->flash->{buildMsg} = "Build " . $newBuild->id . " added to the queue.";
|
||||
|
||||
$c->res->redirect($c->uri_for($c->controller('Root')->action_for('queue')));
|
||||
}
|
||||
|
||||
|
||||
sub get_info : Chained('buildChain') PathPart('api/get-info') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
my $build = $c->stash->{build};
|
||||
@ -614,6 +522,22 @@ sub evals : Chained('buildChain') PathPart('evals') Args(0) {
|
||||
}
|
||||
|
||||
|
||||
# Redirect to the latest finished evaluation that contains this build.
|
||||
sub eval : Chained('buildChain') PathPart('eval') {
|
||||
my ($self, $c, @rest) = @_;
|
||||
|
||||
my $eval = $c->stash->{build}->jobsetevals->find(
|
||||
{ hasnewbuilds => 1 },
|
||||
{ order_by => "id DESC", rows => 1
|
||||
, "not exists (select 1 from jobsetevalmembers m2 join builds b2 on me.eval = m2.eval and m2.build = b2.id and b2.finished = 0)"
|
||||
});
|
||||
|
||||
notFound($c, "There is no finished evaluation containing this build.") unless defined $eval;
|
||||
|
||||
$c->res->redirect($c->uri_for($c->controller('JobsetEval')->action_for("view"), [$eval->id], @rest, $c->req->params));
|
||||
}
|
||||
|
||||
|
||||
sub reproduce : Chained('buildChain') PathPart('reproduce') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
$c->response->content_type('text/x-shellscript');
|
||||
|
@ -20,24 +20,52 @@ sub job : Chained('/') PathPart('job') CaptureArgs(3) {
|
||||
|
||||
sub overview : Chained('job') PathPart('') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
my $job = $c->stash->{job};
|
||||
|
||||
$c->stash->{template} = 'job.tt';
|
||||
|
||||
$c->stash->{lastBuilds} =
|
||||
[ $c->stash->{job}->builds->search({ finished => 1 },
|
||||
[ $job->builds->search({ finished => 1 },
|
||||
{ order_by => 'id DESC', rows => 10, columns => [@buildListColumns] }) ];
|
||||
|
||||
$c->stash->{queuedBuilds} = [
|
||||
$c->stash->{job}->builds->search(
|
||||
$job->builds->search(
|
||||
{ finished => 0 },
|
||||
{ join => ['project']
|
||||
, order_by => ["priority DESC", "id"]
|
||||
, '+select' => ['project.enabled']
|
||||
, '+as' => ['enabled']
|
||||
}
|
||||
{ order_by => ["priority DESC", "id"] }
|
||||
) ];
|
||||
|
||||
$c->stash->{systems} = [$c->stash->{job}->builds->search({iscurrent => 1}, {select => ["system"], distinct => 1})];
|
||||
# If this is an aggregate job, then get its constituents.
|
||||
my @constituents = $c->model('DB::Builds')->search(
|
||||
{ aggregate => { -in => $job->builds->search({}, { columns => ["id"], order_by => "id desc", rows => 15 })->as_query } },
|
||||
{ join => 'aggregateconstituents_constituents',
|
||||
columns => ['id', 'job', 'finished', 'buildstatus'],
|
||||
+select => ['aggregateconstituents_constituents.aggregate'],
|
||||
+as => ['aggregate']
|
||||
});
|
||||
|
||||
my $aggregates = {};
|
||||
my %constituentJobs;
|
||||
foreach my $b (@constituents) {
|
||||
my $jobName = $b->get_column('job');
|
||||
$aggregates->{$b->get_column('aggregate')}->{constituents}->{$jobName} =
|
||||
{ id => $b->id, finished => $b->finished, buildstatus => $b->buildstatus };
|
||||
$constituentJobs{$jobName} = 1;
|
||||
}
|
||||
|
||||
foreach my $agg (keys %$aggregates) {
|
||||
# FIXME: could be done in one query.
|
||||
$aggregates->{$agg}->{build} =
|
||||
$c->model('DB::Builds')->find({id => $agg}, {columns => [@buildListColumns]}) or die;
|
||||
}
|
||||
|
||||
$c->stash->{aggregates} = $aggregates;
|
||||
$c->stash->{constituentJobs} = [sort (keys %constituentJobs)];
|
||||
|
||||
$c->stash->{starred} = $c->user->starredjobs(
|
||||
{ project => $c->stash->{project}->name
|
||||
, jobset => $c->stash->{jobset}->name
|
||||
, job => $c->stash->{job}->name
|
||||
})->count == 1 if $c->user_exists;
|
||||
}
|
||||
|
||||
|
||||
@ -45,9 +73,6 @@ sub overview : Chained('job') PathPart('') Args(0) {
|
||||
sub get_builds : Chained('job') PathPart('') CaptureArgs(0) {
|
||||
my ($self, $c) = @_;
|
||||
$c->stash->{allBuilds} = $c->stash->{job}->builds;
|
||||
$c->stash->{jobStatus} = $c->model('DB')->resultset('JobStatusForJob')
|
||||
->search({}, {bind => [$c->stash->{project}->name, $c->stash->{jobset}->name, $c->stash->{job}->name]});
|
||||
$c->stash->{allJobs} = $c->stash->{job_};
|
||||
$c->stash->{latestSucceeded} = $c->model('DB')->resultset('LatestSucceededForJob')
|
||||
->search({}, {bind => [$c->stash->{project}->name, $c->stash->{jobset}->name, $c->stash->{job}->name]});
|
||||
$c->stash->{channelBaseName} =
|
||||
@ -55,4 +80,22 @@ sub get_builds : Chained('job') PathPart('') CaptureArgs(0) {
|
||||
}
|
||||
|
||||
|
||||
sub star : Chained('job') PathPart('star') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
requirePost($c);
|
||||
requireUser($c);
|
||||
my $args =
|
||||
{ project => $c->stash->{project}->name
|
||||
, jobset => $c->stash->{jobset}->name
|
||||
, job => $c->stash->{job}->name
|
||||
};
|
||||
if ($c->request->params->{star} eq "1") {
|
||||
$c->user->starredjobs->update_or_create($args);
|
||||
} else {
|
||||
$c->user->starredjobs->find($args)->delete;
|
||||
}
|
||||
$c->stash->{resource}->{success} = 1;
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
@ -1,5 +1,6 @@
|
||||
package Hydra::Controller::Jobset;
|
||||
|
||||
use utf8;
|
||||
use strict;
|
||||
use warnings;
|
||||
use base 'Hydra::Base::Controller::ListBuilds';
|
||||
@ -9,35 +10,18 @@ use Hydra::Helper::CatalystUtils;
|
||||
|
||||
sub jobsetChain :Chained('/') :PathPart('jobset') :CaptureArgs(2) {
|
||||
my ($self, $c, $projectName, $jobsetName) = @_;
|
||||
$c->stash->{params}->{name} //= $jobsetName;
|
||||
|
||||
my $project = $c->model('DB::Projects')->find($projectName);
|
||||
|
||||
if ($project) {
|
||||
$c->stash->{project} = $project;
|
||||
notFound($c, "Project ‘$projectName’ doesn't exist.") if !$project;
|
||||
|
||||
$c->stash->{jobset_} = $project->jobsets->search({'me.name' => $jobsetName});
|
||||
my $jobset = $c->stash->{jobset_}->single;
|
||||
$c->stash->{project} = $project;
|
||||
|
||||
if ($jobset) {
|
||||
$c->stash->{jobset} = $jobset;
|
||||
} else {
|
||||
if ($c->action->name eq "jobset" and $c->request->method eq "PUT") {
|
||||
$c->stash->{jobsetName} = $jobsetName;
|
||||
} else {
|
||||
$self->status_not_found(
|
||||
$c,
|
||||
message => "Jobset $jobsetName doesn't exist."
|
||||
);
|
||||
$c->detach;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$self->status_not_found(
|
||||
$c,
|
||||
message => "Project $projectName doesn't exist."
|
||||
);
|
||||
$c->detach;
|
||||
}
|
||||
$c->stash->{jobset} = $project->jobsets->find({ name => $jobsetName });
|
||||
|
||||
notFound($c, "Jobset ‘$jobsetName’ doesn't exist.")
|
||||
if !$c->stash->{jobset} && !($c->action->name eq "jobset" and $c->request->method eq "PUT");
|
||||
}
|
||||
|
||||
|
||||
@ -50,26 +34,11 @@ sub jobset_GET {
|
||||
|
||||
$c->stash->{evals} = getEvals($self, $c, scalar $c->stash->{jobset}->jobsetevals, 0, 10);
|
||||
|
||||
($c->stash->{latestEval}) = $c->stash->{jobset}->jobsetevals->search({}, { limit => 1, order_by => ["id desc"] });
|
||||
$c->stash->{latestEval} = $c->stash->{jobset}->jobsetevals->search({}, { rows => 1, order_by => ["id desc"] })->single;
|
||||
|
||||
$self->status_ok(
|
||||
$c,
|
||||
entity => $c->stash->{jobset_}->find({}, {
|
||||
columns => [
|
||||
'me.name',
|
||||
'me.project',
|
||||
'me.errormsg',
|
||||
'jobsetinputs.name',
|
||||
{
|
||||
'jobsetinputs.jobsetinputalts.altnr' => 'jobsetinputalts.altnr',
|
||||
'jobsetinputs.jobsetinputalts.value' => 'jobsetinputalts.value'
|
||||
}
|
||||
],
|
||||
join => { 'jobsetinputs' => 'jobsetinputalts' },
|
||||
collapse => 1,
|
||||
order_by => "me.name"
|
||||
})
|
||||
);
|
||||
$c->stash->{totalShares} = getTotalShares($c->model('DB')->schema);
|
||||
|
||||
$self->status_ok($c, entity => $c->stash->{jobset});
|
||||
}
|
||||
|
||||
sub jobset_PUT {
|
||||
@ -78,133 +47,91 @@ sub jobset_PUT {
|
||||
requireProjectOwner($c, $c->stash->{project});
|
||||
|
||||
if (defined $c->stash->{jobset}) {
|
||||
error($c, "Cannot rename jobset `$c->stash->{params}->{oldName}' over existing jobset `$c->stash->{jobset}->name") if defined $c->stash->{params}->{oldName} and $c->stash->{params}->{oldName} ne $c->stash->{jobset}->name;
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
updateJobset($c, $c->stash->{jobset});
|
||||
});
|
||||
|
||||
if ($c->req->looks_like_browser) {
|
||||
$c->res->redirect($c->uri_for($self->action_for("jobset"),
|
||||
[$c->stash->{project}->name, $c->stash->{jobset}->name]) . "#tabs-configuration");
|
||||
} else {
|
||||
$self->status_no_content($c);
|
||||
}
|
||||
} elsif (defined $c->stash->{params}->{oldName}) {
|
||||
my $jobset = $c->stash->{project}->jobsets->find({'me.name' => $c->stash->{params}->{oldName}});
|
||||
my $uri = $c->uri_for($self->action_for("jobset"), [$c->stash->{project}->name, $c->stash->{jobset}->name]) . "#tabs-configuration";
|
||||
$self->status_ok($c, entity => { redirect => "$uri" });
|
||||
|
||||
if (defined $jobset) {
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
updateJobset($c, $jobset);
|
||||
});
|
||||
|
||||
my $uri = $c->uri_for($self->action_for("jobset"), [$c->stash->{project}->name, $jobset->name]);
|
||||
|
||||
if ($c->req->looks_like_browser) {
|
||||
$c->res->redirect($uri . "#tabs-configuration");
|
||||
} else {
|
||||
$self->status_created(
|
||||
$c,
|
||||
location => "$uri",
|
||||
entity => { name => $jobset->name, uri => "$uri", type => "jobset" }
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$self->status_not_found(
|
||||
$c,
|
||||
message => "Jobset $c->stash->{params}->{oldName} doesn't exist."
|
||||
);
|
||||
}
|
||||
} else {
|
||||
my $exprType =
|
||||
$c->stash->{params}->{"nixexprpath"} =~ /.scm$/ ? "guile" : "nix";
|
||||
|
||||
error($c, "Invalid jobset name: ‘$c->stash->{jobsetName}’") if $c->stash->{jobsetName} !~ /^$jobsetNameRE$/;
|
||||
$c->flash->{successMsg} = "The jobset configuration has been updated.";
|
||||
}
|
||||
|
||||
else {
|
||||
my $jobset;
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
# Note: $jobsetName is validated in updateProject, which will
|
||||
# abort the transaction if the name isn't valid.
|
||||
$jobset = $c->stash->{project}->jobsets->create(
|
||||
{name => $c->stash->{jobsetName}, nixexprinput => "", nixexprpath => "", emailoverride => ""});
|
||||
{name => ".tmp", nixexprinput => "", nixexprpath => "", emailoverride => ""});
|
||||
updateJobset($c, $jobset);
|
||||
});
|
||||
|
||||
my $uri = $c->uri_for($self->action_for("jobset"), [$c->stash->{project}->name, $jobset->name]);
|
||||
if ($c->req->looks_like_browser) {
|
||||
$c->res->redirect($uri . "#tabs-configuration");
|
||||
} else {
|
||||
$self->status_created(
|
||||
$c,
|
||||
location => "$uri",
|
||||
entity => { name => $jobset->name, uri => "$uri", type => "jobset" }
|
||||
);
|
||||
}
|
||||
$self->status_created($c,
|
||||
location => "$uri",
|
||||
entity => { name => $jobset->name, uri => "$uri", redirect => "$uri", type => "jobset" });
|
||||
}
|
||||
}
|
||||
|
||||
sub jobset_DELETE {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
requireProjectOwner($c, $c->stash->{project});
|
||||
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
$c->stash->{jobset}->jobsetevals->delete;
|
||||
$c->stash->{jobset}->builds->delete;
|
||||
$c->stash->{jobset}->delete;
|
||||
});
|
||||
|
||||
my $uri = $c->uri_for($c->controller('Project')->action_for("project"), [$c->stash->{project}->name]);
|
||||
$self->status_ok($c, entity => { redirect => "$uri" });
|
||||
|
||||
$c->flash->{successMsg} = "The jobset has been deleted.";
|
||||
}
|
||||
|
||||
|
||||
sub jobs_tab : Chained('jobsetChain') PathPart('jobs-tab') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
$c->stash->{template} = 'jobset-jobs-tab.tt';
|
||||
|
||||
$c->stash->{activeJobs} = [];
|
||||
$c->stash->{inactiveJobs} = [];
|
||||
$c->stash->{filter} = $c->request->params->{filter} // "";
|
||||
my $filter = "%" . $c->stash->{filter} . "%";
|
||||
|
||||
(my $latestEval) = $c->stash->{jobset}->jobsetevals->search(
|
||||
{ hasnewbuilds => 1}, { limit => 1, order_by => ["id desc"] });
|
||||
my @evals = $c->stash->{jobset}->jobsetevals->search({ hasnewbuilds => 1}, { order_by => "id desc", rows => 20 });
|
||||
|
||||
my %activeJobs;
|
||||
if (defined $latestEval) {
|
||||
foreach my $build ($latestEval->builds->search({}, { order_by => ["job"], select => ["job"] })) {
|
||||
my $job = $build->get_column("job");
|
||||
if (!defined $activeJobs{$job}) {
|
||||
$activeJobs{$job} = 1;
|
||||
push @{$c->stash->{activeJobs}}, $job;
|
||||
}
|
||||
my $evals = {};
|
||||
my %jobs;
|
||||
my $nrBuilds = 0;
|
||||
|
||||
foreach my $eval (@evals) {
|
||||
my @builds = $eval->builds->search(
|
||||
{ job => { ilike => $filter } },
|
||||
{ columns => ['id', 'job', 'finished', 'buildstatus'] });
|
||||
foreach my $b (@builds) {
|
||||
my $jobName = $b->get_column('job');
|
||||
$evals->{$eval->id}->{$jobName} =
|
||||
{ id => $b->id, finished => $b->finished, buildstatus => $b->buildstatus };
|
||||
$jobs{$jobName} = 1;
|
||||
$nrBuilds++;
|
||||
}
|
||||
last if $nrBuilds >= 10000;
|
||||
}
|
||||
|
||||
if ($c->request->params->{showInactive}) {
|
||||
$c->stash->{showInactive} = 1;
|
||||
foreach my $job ($c->stash->{jobset}->jobs->search({ name => { ilike => $filter } })) {
|
||||
next if defined $jobs{$job->name};
|
||||
$c->stash->{inactiveJobs}->{$job->name} = $jobs{$job->name} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $job ($c->stash->{jobset}->jobs->search({}, { order_by => ["name"] })) {
|
||||
if (!defined $activeJobs{$job->name}) {
|
||||
push @{$c->stash->{inactiveJobs}}, $job->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub status_tab : Chained('jobsetChain') PathPart('status-tab') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
$c->stash->{template} = 'jobset-status-tab.tt';
|
||||
|
||||
# FIXME: use latest eval instead of iscurrent.
|
||||
|
||||
$c->stash->{systems} =
|
||||
[ $c->stash->{jobset}->builds->search({ iscurrent => 1 }, { select => ["system"], distinct => 1, order_by => "system" }) ];
|
||||
|
||||
# status per system
|
||||
my @systems = ();
|
||||
foreach my $system (@{$c->stash->{systems}}) {
|
||||
push(@systems, $system->system);
|
||||
}
|
||||
|
||||
my @select = ();
|
||||
my @as = ();
|
||||
push(@select, "job"); push(@as, "job");
|
||||
foreach my $system (@systems) {
|
||||
push(@select, "(select buildstatus from Builds b where b.id = (select max(id) from Builds t where t.project = me.project and t.jobset = me.jobset and t.job = me.job and t.system = '$system' and t.iscurrent = 1 ))");
|
||||
push(@as, $system);
|
||||
push(@select, "(select b.id from Builds b where b.id = (select max(id) from Builds t where t.project = me.project and t.jobset = me.jobset and t.job = me.job and t.system = '$system' and t.iscurrent = 1 ))");
|
||||
push(@as, "$system-build");
|
||||
}
|
||||
|
||||
$c->stash->{activeJobsStatus} = [
|
||||
$c->model('DB')->resultset('ActiveJobsForJobset')->search(
|
||||
{},
|
||||
{ bind => [$c->stash->{project}->name, $c->stash->{jobset}->name]
|
||||
, select => \@select
|
||||
, as => \@as
|
||||
, order_by => ["job"]
|
||||
}) ];
|
||||
$c->stash->{evals} = $evals;
|
||||
my @jobs = sort (keys %jobs);
|
||||
$c->stash->{nrJobs} = scalar @jobs;
|
||||
splice @jobs, 250 if $c->stash->{filter} eq "";
|
||||
$c->stash->{jobs} = [@jobs];
|
||||
}
|
||||
|
||||
|
||||
@ -212,10 +139,6 @@ sub status_tab : Chained('jobsetChain') PathPart('status-tab') Args(0) {
|
||||
sub get_builds : Chained('jobsetChain') PathPart('') CaptureArgs(0) {
|
||||
my ($self, $c) = @_;
|
||||
$c->stash->{allBuilds} = $c->stash->{jobset}->builds;
|
||||
$c->stash->{jobStatus} = $c->model('DB')->resultset('JobStatusForJobset')
|
||||
->search({}, {bind => [$c->stash->{project}->name, $c->stash->{jobset}->name]});
|
||||
$c->stash->{allJobsets} = $c->stash->{jobset_};
|
||||
$c->stash->{allJobs} = $c->stash->{jobset}->jobs;
|
||||
$c->stash->{latestSucceeded} = $c->model('DB')->resultset('LatestSucceededForJobset')
|
||||
->search({}, {bind => [$c->stash->{project}->name, $c->stash->{jobset}->name]});
|
||||
$c->stash->{channelBaseName} =
|
||||
@ -230,31 +153,8 @@ sub edit : Chained('jobsetChain') PathPart Args(0) {
|
||||
|
||||
$c->stash->{template} = 'edit-jobset.tt';
|
||||
$c->stash->{edit} = 1;
|
||||
}
|
||||
|
||||
|
||||
sub submit : Chained('jobsetChain') PathPart Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
requirePost($c);
|
||||
|
||||
if (($c->request->params->{submit} // "") eq "delete") {
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
$c->stash->{jobset}->jobsetevals->delete_all;
|
||||
$c->stash->{jobset}->builds->delete_all;
|
||||
$c->stash->{jobset}->delete;
|
||||
});
|
||||
return $c->res->redirect($c->uri_for($c->controller('Project')->action_for("project"), [$c->stash->{project}->name]));
|
||||
}
|
||||
|
||||
my $newName = trim $c->stash->{params}->{name};
|
||||
my $oldName = trim $c->stash->{jobset}->name;
|
||||
unless ($oldName eq $newName) {
|
||||
$c->stash->{params}->{oldName} = $oldName;
|
||||
$c->stash->{jobsetName} = $newName;
|
||||
undef $c->stash->{jobset};
|
||||
}
|
||||
jobset_PUT($self, $c);
|
||||
$c->stash->{clone} = defined $c->stash->{params}->{clone};
|
||||
$c->stash->{totalShares} = getTotalShares($c->model('DB')->schema);
|
||||
}
|
||||
|
||||
|
||||
@ -263,10 +163,10 @@ sub nixExprPathFromParams {
|
||||
|
||||
# The Nix expression path must be relative and can't contain ".." elements.
|
||||
my $nixExprPath = trim $c->stash->{params}->{"nixexprpath"};
|
||||
error($c, "Invalid Nix expression path: $nixExprPath") if $nixExprPath !~ /^$relPathRE$/;
|
||||
error($c, "Invalid Nix expression path ‘$nixExprPath’.") if $nixExprPath !~ /^$relPathRE$/;
|
||||
|
||||
my $nixExprInput = trim $c->stash->{params}->{"nixexprinput"};
|
||||
error($c, "Invalid Nix expression input name: $nixExprInput") unless $nixExprInput =~ /^\w+$/;
|
||||
error($c, "Invalid Nix expression input name ‘$nixExprInput’.") unless $nixExprInput =~ /^[[:alpha:]][\w-]*$/;
|
||||
|
||||
return ($nixExprPath, $nixExprInput);
|
||||
}
|
||||
@ -275,7 +175,7 @@ sub nixExprPathFromParams {
|
||||
sub checkInputValue {
|
||||
my ($c, $type, $value) = @_;
|
||||
$value = trim $value;
|
||||
error($c, "Invalid Boolean value: $value") if
|
||||
error($c, "Invalid Boolean value ‘$value’.") if
|
||||
$type eq "boolean" && !($value eq "true" || $value eq "false");
|
||||
return $value;
|
||||
}
|
||||
@ -284,8 +184,11 @@ sub checkInputValue {
|
||||
sub updateJobset {
|
||||
my ($c, $jobset) = @_;
|
||||
|
||||
my $jobsetName = $c->stash->{jobsetName} // $jobset->name;
|
||||
error($c, "Invalid jobset name: ‘$jobsetName’") if $jobsetName !~ /^$jobsetNameRE$/;
|
||||
my $jobsetName = $c->stash->{params}->{name};
|
||||
error($c, "Invalid jobset identifier ‘$jobsetName’.") if $jobsetName !~ /^$jobsetNameRE$/;
|
||||
|
||||
error($c, "Cannot rename jobset to ‘$jobsetName’ since that identifier is already taken.")
|
||||
if $jobsetName ne $jobset->name && defined $c->stash->{project}->jobsets->find({ name => $jobsetName });
|
||||
|
||||
# When the expression is in a .scm file, assume it's a Guile + Guix
|
||||
# build expression.
|
||||
@ -294,118 +197,61 @@ sub updateJobset {
|
||||
|
||||
my ($nixExprPath, $nixExprInput) = nixExprPathFromParams $c;
|
||||
|
||||
my $enabled = int($c->stash->{params}->{enabled});
|
||||
die if $enabled < 0 || $enabled > 2;
|
||||
|
||||
$jobset->update(
|
||||
{ name => $jobsetName
|
||||
, description => trim($c->stash->{params}->{"description"})
|
||||
, nixexprpath => $nixExprPath
|
||||
, nixexprinput => $nixExprInput
|
||||
, enabled => defined $c->stash->{params}->{enabled} ? 1 : 0
|
||||
, enabled => $enabled
|
||||
, enableemail => defined $c->stash->{params}->{enableemail} ? 1 : 0
|
||||
, emailoverride => trim($c->stash->{params}->{emailoverride}) || ""
|
||||
, hidden => defined $c->stash->{params}->{visible} ? 0 : 1
|
||||
, keepnr => int(trim($c->stash->{params}->{keepnr})) || 3
|
||||
, keepnr => int(trim($c->stash->{params}->{keepnr}))
|
||||
, checkinterval => int(trim($c->stash->{params}->{checkinterval}))
|
||||
, triggertime => $jobset->triggertime // time()
|
||||
, triggertime => $enabled ? $jobset->triggertime // time() : undef
|
||||
, schedulingshares => int($c->stash->{params}->{schedulingshares})
|
||||
});
|
||||
|
||||
# Process the inputs of this jobset.
|
||||
unless (defined $c->stash->{params}->{inputs}) {
|
||||
$c->stash->{params}->{inputs} = {};
|
||||
foreach my $param (keys %{$c->stash->{params}}) {
|
||||
next unless $param =~ /^input-(\w+)-name$/;
|
||||
my $baseName = $1;
|
||||
next if $baseName eq "template";
|
||||
$c->stash->{params}->{inputs}->{$c->stash->{params}->{$param}} = { type => $c->stash->{params}->{"input-$baseName-type"}, values => $c->stash->{params}->{"input-$baseName-values"} };
|
||||
unless ($baseName =~ /^\d+$/) { # non-numeric base name is an existing entry
|
||||
$c->stash->{params}->{inputs}->{$c->stash->{params}->{$param}}->{oldName} = $baseName;
|
||||
}
|
||||
}
|
||||
}
|
||||
# Set the inputs of this jobset.
|
||||
$jobset->jobsetinputs->delete;
|
||||
|
||||
foreach my $inputName (keys %{$c->stash->{params}->{inputs}}) {
|
||||
my $inputData = $c->stash->{params}->{inputs}->{$inputName};
|
||||
error($c, "Invalid input name: $inputName") unless $inputName =~ /^[[:alpha:]]\w*$/;
|
||||
|
||||
my $inputType = $inputData->{type};
|
||||
error($c, "Invalid input type: $inputType") unless
|
||||
$inputType eq "svn" || $inputType eq "svn-checkout" || $inputType eq "hg" || $inputType eq "tarball" ||
|
||||
$inputType eq "string" || $inputType eq "path" || $inputType eq "boolean" || $inputType eq "bzr" || $inputType eq "bzr-checkout" ||
|
||||
$inputType eq "git" || $inputType eq "build" || $inputType eq "sysbuild" ;
|
||||
|
||||
my $input;
|
||||
unless (defined $inputData->{oldName}) {
|
||||
$input = $jobset->jobsetinputs->update_or_create(
|
||||
{ name => $inputName
|
||||
, type => $inputType
|
||||
});
|
||||
} else { # it's an existing input
|
||||
$input = ($jobset->jobsetinputs->search({name => $inputData->{oldName}}))[0];
|
||||
die unless defined $input;
|
||||
$input->update({name => $inputName, type => $inputType});
|
||||
}
|
||||
|
||||
# Update the values for this input. Just delete all the
|
||||
# current ones, then create the new values.
|
||||
$input->jobsetinputalts->delete_all;
|
||||
foreach my $name (keys %{$c->stash->{params}->{inputs}}) {
|
||||
my $inputData = $c->stash->{params}->{inputs}->{$name};
|
||||
my $type = $inputData->{type};
|
||||
my $values = $inputData->{values};
|
||||
$values = [] unless defined $values;
|
||||
$values = [$values] unless ref($values) eq 'ARRAY';
|
||||
my $emailresponsible = defined $inputData->{emailresponsible} ? 1 : 0;
|
||||
|
||||
error($c, "Invalid input name ‘$name’.") unless $name =~ /^[[:alpha:]][\w-]*$/;
|
||||
error($c, "Invalid input type ‘$type’.") unless defined $c->stash->{inputTypes}->{$type};
|
||||
|
||||
my $input = $jobset->jobsetinputs->create({
|
||||
name => $name,
|
||||
type => $type,
|
||||
emailresponsible => $emailresponsible
|
||||
});
|
||||
|
||||
# Set the values for this input.
|
||||
my @values = ref($values) eq 'ARRAY' ? @{$values} : ($values);
|
||||
my $altnr = 0;
|
||||
foreach my $value (@{$values}) {
|
||||
$value = checkInputValue($c, $inputType, $value);
|
||||
foreach my $value (@values) {
|
||||
$value = checkInputValue($c, $type, $value);
|
||||
$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 $c->stash->{params}->{inputs}->{$input->name};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub clone : Chained('jobsetChain') PathPart('clone') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
my $jobset = $c->stash->{jobset};
|
||||
requireProjectOwner($c, $jobset->project);
|
||||
requireProjectOwner($c, $c->stash->{project});
|
||||
|
||||
$c->stash->{template} = 'clone-jobset.tt';
|
||||
}
|
||||
|
||||
|
||||
sub clone_submit : Chained('jobsetChain') PathPart('clone/submit') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
my $jobset = $c->stash->{jobset};
|
||||
requireProjectOwner($c, $jobset->project);
|
||||
requirePost($c);
|
||||
|
||||
my $newJobsetName = trim $c->stash->{params}->{"newjobset"};
|
||||
error($c, "Invalid jobset name: $newJobsetName") unless $newJobsetName =~ /^[[:alpha:]][\w\-]*$/;
|
||||
|
||||
my $newJobset;
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
$newJobset = $jobset->project->jobsets->create(
|
||||
{ name => $newJobsetName
|
||||
, description => $jobset->description
|
||||
, nixexprpath => $jobset->nixexprpath
|
||||
, nixexprinput => $jobset->nixexprinput
|
||||
, enabled => 0
|
||||
, enableemail => $jobset->enableemail
|
||||
, emailoverride => $jobset->emailoverride || ""
|
||||
});
|
||||
|
||||
foreach my $input ($jobset->jobsetinputs) {
|
||||
my $newinput = $newJobset->jobsetinputs->create({name => $input->name, type => $input->type});
|
||||
foreach my $inputalt ($input->jobsetinputalts) {
|
||||
$newinput->jobsetinputalts->create({altnr => $inputalt->altnr, value => $inputalt->value});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$c->res->redirect($c->uri_for($c->controller('Jobset')->action_for("edit"), [$jobset->project->name, $newJobsetName]));
|
||||
$c->stash->{template} = 'edit-jobset.tt';
|
||||
$c->stash->{clone} = 1;
|
||||
$c->stash->{totalShares} = getTotalShares($c->model('DB')->schema);
|
||||
}
|
||||
|
||||
|
||||
@ -428,24 +274,7 @@ sub evals_GET {
|
||||
my $offset = ($page - 1) * $resultsPerPage;
|
||||
$c->stash->{evals} = getEvals($self, $c, $evals, $offset, $resultsPerPage);
|
||||
my %entity = (
|
||||
evals => [ $evals->search({ 'me.hasnewbuilds' => 1 }, {
|
||||
columns => [
|
||||
'me.hasnewbuilds',
|
||||
'me.id',
|
||||
'jobsetevalinputs.name',
|
||||
'jobsetevalinputs.altnr',
|
||||
'jobsetevalinputs.revision',
|
||||
'jobsetevalinputs.type',
|
||||
'jobsetevalinputs.uri',
|
||||
'jobsetevalinputs.dependency',
|
||||
'jobsetevalmembers.build',
|
||||
],
|
||||
join => [ 'jobsetevalinputs', 'jobsetevalmembers' ],
|
||||
collapse => 1,
|
||||
rows => $resultsPerPage,
|
||||
offset => $offset,
|
||||
order_by => "me.id DESC",
|
||||
}) ],
|
||||
evals => [ map { $_->{eval} } @{$c->stash->{evals}} ],
|
||||
first => "?page=1",
|
||||
last => "?page=" . POSIX::ceil($c->stash->{total}/$resultsPerPage)
|
||||
);
|
||||
|
@ -26,6 +26,9 @@ sub view : Chained('eval') PathPart('') Args(0) {
|
||||
|
||||
my $eval = $c->stash->{eval};
|
||||
|
||||
$c->stash->{filter} = $c->request->params->{filter} // "";
|
||||
my $filter = $c->stash->{filter} eq "" ? {} : { job => { ilike => "%" . $c->stash->{filter} . "%" } };
|
||||
|
||||
my $compare = $c->req->params->{compare};
|
||||
my $eval2;
|
||||
|
||||
@ -36,6 +39,11 @@ sub view : Chained('eval') PathPart('') Args(0) {
|
||||
if ($compare =~ /^\d+$/) {
|
||||
$eval2 = $c->model('DB::JobsetEvals')->find($compare)
|
||||
or notFound($c, "Evaluation $compare doesn't exist.");
|
||||
} elsif ($compare =~ /^-(\d+)$/) {
|
||||
my $t = int($1);
|
||||
$eval2 = $c->stash->{jobset}->jobsetevals->find(
|
||||
{ hasnewbuilds => 1, timestamp => {'<=', $eval->timestamp - $t} },
|
||||
{ order_by => "timestamp desc", rows => 1});
|
||||
} elsif (defined $compare && $compare =~ /^($jobsetNameRE)$/) {
|
||||
my $j = $c->stash->{project}->jobsets->find({name => $compare})
|
||||
or notFound($c, "Jobset $compare doesn't exist.");
|
||||
@ -51,10 +59,17 @@ sub view : Chained('eval') PathPart('') Args(0) {
|
||||
|
||||
$c->stash->{otherEval} = $eval2 if defined $eval2;
|
||||
|
||||
my @builds = $eval->builds->search({}, { order_by => ["job", "system", "id"], columns => [@buildListColumns] });
|
||||
my @builds2 = defined $eval2
|
||||
? $eval2->builds->search({}, { order_by => ["job", "system", "id"], columns => [@buildListColumns] })
|
||||
: ();
|
||||
sub cmpBuilds {
|
||||
my ($a, $b) = @_;
|
||||
return $a->get_column('job') cmp $b->get_column('job')
|
||||
|| $a->get_column('system') cmp $b->get_column('system')
|
||||
}
|
||||
|
||||
my @builds = $eval->builds->search($filter, { columns => [@buildListColumns] });
|
||||
my @builds2 = defined $eval2 ? $eval2->builds->search($filter, { columns => [@buildListColumns] }) : ();
|
||||
|
||||
@builds = sort { cmpBuilds($a, $b) } @builds;
|
||||
@builds2 = sort { cmpBuilds($a, $b) } @builds2;
|
||||
|
||||
$c->stash->{stillSucceed} = [];
|
||||
$c->stash->{stillFail} = [];
|
||||
@ -63,15 +78,19 @@ sub view : Chained('eval') PathPart('') Args(0) {
|
||||
$c->stash->{new} = [];
|
||||
$c->stash->{removed} = [];
|
||||
$c->stash->{unfinished} = [];
|
||||
$c->stash->{aborted} = [];
|
||||
|
||||
my $n = 0;
|
||||
foreach my $build (@builds) {
|
||||
if ($build->finished != 0 && ($build->buildstatus == 3 || $build->buildstatus == 4)) {
|
||||
push @{$c->stash->{aborted}}, $build;
|
||||
next;
|
||||
}
|
||||
my $d;
|
||||
my $found = 0;
|
||||
while ($n < scalar(@builds2)) {
|
||||
my $build2 = $builds2[$n];
|
||||
my $d = $build->get_column('job') cmp $build2->get_column('job')
|
||||
|| $build->get_column('system') cmp $build2->get_column('system');
|
||||
my $d = cmpBuilds($build, $build2);
|
||||
last if $d == -1;
|
||||
if ($d == 0) {
|
||||
$n++;
|
||||
@ -135,6 +154,25 @@ sub release : Chained('eval') PathPart('release') Args(0) {
|
||||
}
|
||||
|
||||
|
||||
sub cancel : Chained('eval') PathPart('cancel') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
requireProjectOwner($c, $c->stash->{eval}->project);
|
||||
my $n = cancelBuilds($c->model('DB')->schema, $c->stash->{eval}->builds);
|
||||
$c->flash->{successMsg} = "$n builds have been cancelled.";
|
||||
$c->res->redirect($c->uri_for($c->controller('JobsetEval')->action_for('view'), $c->req->captures));
|
||||
}
|
||||
|
||||
|
||||
sub restart_aborted : Chained('eval') PathPart('restart-aborted') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
requireProjectOwner($c, $c->stash->{eval}->project);
|
||||
my $builds = $c->stash->{eval}->builds->search({ finished => 1, buildstatus => { -in => [3, 4] } });
|
||||
my $n = restartBuilds($c->model('DB')->schema, $builds);
|
||||
$c->flash->{successMsg} = "$n builds have been restarted.";
|
||||
$c->res->redirect($c->uri_for($c->controller('JobsetEval')->action_for('view'), $c->req->captures));
|
||||
}
|
||||
|
||||
|
||||
# Hydra::Base::Controller::NixChannel needs this.
|
||||
sub nix : Chained('eval') PathPart('channel') CaptureArgs(0) {
|
||||
my ($self, $c) = @_;
|
||||
@ -144,8 +182,20 @@ sub nix : Chained('eval') PathPart('channel') CaptureArgs(0) {
|
||||
->search({ finished => 1, buildstatus => 0 },
|
||||
{ columns => [@buildListColumns, 'drvpath', 'description', 'homepage']
|
||||
, join => ["buildoutputs"]
|
||||
, order_by => ["build.id", "buildoutputs.name"]
|
||||
, '+select' => ['buildoutputs.path', 'buildoutputs.name'], '+as' => ['outpath', 'outname'] });
|
||||
}
|
||||
|
||||
|
||||
sub job : Chained('eval') PathPart('job') {
|
||||
my ($self, $c, $job, @rest) = @_;
|
||||
|
||||
my $build = $c->stash->{eval}->builds->find({job => $job});
|
||||
|
||||
notFound($c, "This evaluation has no job with the specified name.") unless defined $build;
|
||||
|
||||
$c->res->redirect($c->uri_for($c->controller('Build')->action_for("build"), [$build->id], @rest));
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
@ -1,5 +1,6 @@
|
||||
package Hydra::Controller::Project;
|
||||
|
||||
use utf8;
|
||||
use strict;
|
||||
use warnings;
|
||||
use base 'Hydra::Base::Controller::ListBuilds';
|
||||
@ -9,35 +10,15 @@ use Hydra::Helper::CatalystUtils;
|
||||
|
||||
sub projectChain :Chained('/') :PathPart('project') :CaptureArgs(1) {
|
||||
my ($self, $c, $projectName) = @_;
|
||||
$c->stash->{params}->{name} //= $projectName;
|
||||
|
||||
my $project = $c->model('DB::Projects')->find($projectName, { columns => [
|
||||
"me.name",
|
||||
"me.displayName",
|
||||
"me.description",
|
||||
"me.enabled",
|
||||
"me.hidden",
|
||||
"me.homepage",
|
||||
"owner.username",
|
||||
"owner.fullname",
|
||||
"views.name",
|
||||
"releases.name",
|
||||
"releases.timestamp",
|
||||
"jobsets.name",
|
||||
], join => [ 'owner', 'views', 'releases', 'jobsets' ], order_by => { -desc => "releases.timestamp" }, collapse => 1 });
|
||||
$c->stash->{project} = $c->model('DB::Projects')->find($projectName, {
|
||||
join => [ 'releases' ],
|
||||
order_by => { -desc => "releases.timestamp" },
|
||||
});
|
||||
|
||||
if ($project) {
|
||||
$c->stash->{project} = $project;
|
||||
} else {
|
||||
if ($c->action->name eq "project" and $c->request->method eq "PUT") {
|
||||
$c->stash->{projectName} = $projectName;
|
||||
} else {
|
||||
$self->status_not_found(
|
||||
$c,
|
||||
message => "Project $projectName doesn't exist."
|
||||
);
|
||||
$c->detach;
|
||||
}
|
||||
}
|
||||
notFound($c, "Project ‘$projectName’ doesn't exist.")
|
||||
if !$c->stash->{project} && !($c->action->name eq "project" and $c->request->method eq "PUT");
|
||||
}
|
||||
|
||||
|
||||
@ -53,55 +34,27 @@ sub project_GET {
|
||||
$c->stash->{releases} = [$c->stash->{project}->releases->search({},
|
||||
{order_by => ["timestamp DESC"]})];
|
||||
|
||||
$self->status_ok(
|
||||
$c,
|
||||
entity => $c->stash->{project}
|
||||
);
|
||||
$self->status_ok($c, entity => $c->stash->{project});
|
||||
}
|
||||
|
||||
sub project_PUT {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
if (defined $c->stash->{project}) {
|
||||
error($c, "Cannot rename project `$c->stash->{params}->{oldName}' over existing project `$c->stash->{project}->name") if defined $c->stash->{params}->{oldName};
|
||||
requireProjectOwner($c, $c->stash->{project});
|
||||
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
updateProject($c, $c->stash->{project});
|
||||
});
|
||||
|
||||
if ($c->req->looks_like_browser) {
|
||||
$c->res->redirect($c->uri_for($self->action_for("project"), [$c->stash->{project}->name]) . "#tabs-configuration");
|
||||
} else {
|
||||
$self->status_no_content($c);
|
||||
}
|
||||
} elsif (defined $c->stash->{params}->{oldName}) {
|
||||
my $project = $c->model('DB::Projects')->find($c->stash->{params}->{oldName});
|
||||
if (defined $project) {
|
||||
requireProjectOwner($c, $project);
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
updateProject($c, $project);
|
||||
});
|
||||
my $uri = $c->uri_for($self->action_for("project"), [$c->stash->{project}->name]) . "#tabs-configuration";
|
||||
$self->status_ok($c, entity => { redirect => "$uri" });
|
||||
|
||||
my $uri = $c->uri_for($self->action_for("project"), [$project->name]);
|
||||
$c->flash->{successMsg} = "The project configuration has been updated.";
|
||||
}
|
||||
|
||||
if ($c->req->looks_like_browser) {
|
||||
$c->res->redirect($uri . "#tabs-configuration");
|
||||
} else {
|
||||
$self->status_created(
|
||||
$c,
|
||||
location => "$uri",
|
||||
entity => { name => $project->name, uri => "$uri", type => "project" }
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$self->status_not_found(
|
||||
$c,
|
||||
message => "Project $c->stash->{params}->{oldName} doesn't exist."
|
||||
);
|
||||
}
|
||||
} else {
|
||||
else {
|
||||
requireMayCreateProjects($c);
|
||||
error($c, "Invalid project name: ‘$c->stash->{projectName}’") if $c->stash->{projectName} !~ /^$projectNameRE$/;
|
||||
|
||||
my $project;
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
@ -110,23 +63,34 @@ sub project_PUT {
|
||||
# valid. Idem for the owner.
|
||||
my $owner = $c->user->username;
|
||||
$project = $c->model('DB::Projects')->create(
|
||||
{name => $c->stash->{projectName}, displayname => "", owner => $owner});
|
||||
{ name => ".tmp", displayname => "", owner => $owner });
|
||||
updateProject($c, $project);
|
||||
});
|
||||
|
||||
my $uri = $c->uri_for($self->action_for("project"), [$project->name]);
|
||||
if ($c->req->looks_like_browser) {
|
||||
$c->res->redirect($uri . "#tabs-configuration");
|
||||
} else {
|
||||
$self->status_created(
|
||||
$c,
|
||||
location => "$uri",
|
||||
entity => { name => $project->name, uri => "$uri", type => "project" }
|
||||
);
|
||||
}
|
||||
$self->status_created($c,
|
||||
location => "$uri",
|
||||
entity => { name => $project->name, uri => "$uri", redirect => "$uri", type => "project" });
|
||||
}
|
||||
}
|
||||
|
||||
sub project_DELETE {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
requireProjectOwner($c, $c->stash->{project});
|
||||
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
$c->stash->{project}->jobsetevals->delete;
|
||||
$c->stash->{project}->builds->delete;
|
||||
$c->stash->{project}->delete;
|
||||
});
|
||||
|
||||
my $uri = $c->res->redirect($c->uri_for("/"));
|
||||
$self->status_ok($c, entity => { redirect => "$uri" });
|
||||
|
||||
$c->flash->{successMsg} = "The project has been deleted.";
|
||||
}
|
||||
|
||||
|
||||
sub edit : Chained('projectChain') PathPart Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
@ -138,36 +102,10 @@ sub edit : Chained('projectChain') PathPart Args(0) {
|
||||
}
|
||||
|
||||
|
||||
sub submit : Chained('projectChain') PathPart Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
requirePost($c);
|
||||
if (($c->request->params->{submit} // "") eq "delete") {
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
$c->stash->{project}->jobsetevals->delete_all;
|
||||
$c->stash->{project}->builds->delete_all;
|
||||
$c->stash->{project}->delete;
|
||||
});
|
||||
return $c->res->redirect($c->uri_for("/"));
|
||||
}
|
||||
|
||||
my $newName = trim $c->stash->{params}->{name};
|
||||
my $oldName = trim $c->stash->{project}->name;
|
||||
unless ($oldName eq $newName) {
|
||||
$c->stash->{params}->{oldName} = $oldName;
|
||||
$c->stash->{projectName} = $newName;
|
||||
undef $c->stash->{project};
|
||||
}
|
||||
project_PUT($self, $c);
|
||||
}
|
||||
|
||||
|
||||
sub requireMayCreateProjects {
|
||||
my ($c) = @_;
|
||||
|
||||
requireLogin($c) if !$c->user_exists;
|
||||
|
||||
error($c, "Only administrators or authorised users can perform this operation.")
|
||||
requireUser($c);
|
||||
accessDenied($c, "Only administrators or authorised users can perform this operation.")
|
||||
unless $c->check_user_roles('admin') || $c->check_user_roles('create-projects');
|
||||
}
|
||||
|
||||
@ -183,15 +121,6 @@ sub create : Path('/create-project') {
|
||||
}
|
||||
|
||||
|
||||
sub create_submit : Path('/create-project/submit') {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
$c->stash->{projectName} = trim $c->stash->{params}->{name};
|
||||
|
||||
project_PUT($self, $c);
|
||||
}
|
||||
|
||||
|
||||
sub create_jobset : Chained('projectChain') PathPart('create-jobset') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
@ -200,15 +129,7 @@ sub create_jobset : Chained('projectChain') PathPart('create-jobset') Args(0) {
|
||||
$c->stash->{template} = 'edit-jobset.tt';
|
||||
$c->stash->{create} = 1;
|
||||
$c->stash->{edit} = 1;
|
||||
}
|
||||
|
||||
|
||||
sub create_jobset_submit : Chained('projectChain') PathPart('create-jobset/submit') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
$c->stash->{jobsetName} = trim $c->stash->{params}->{name};
|
||||
|
||||
Hydra::Controller::Jobset::jobset_PUT($self, $c);
|
||||
$c->stash->{totalShares} = getTotalShares($c->model('DB')->schema);
|
||||
}
|
||||
|
||||
|
||||
@ -218,15 +139,18 @@ sub updateProject {
|
||||
my $owner = $project->owner;
|
||||
if ($c->check_user_roles('admin') and defined $c->stash->{params}->{owner}) {
|
||||
$owner = trim $c->stash->{params}->{owner};
|
||||
error($c, "Invalid owner: $owner")
|
||||
unless defined $c->model('DB::Users')->find({username => $owner});
|
||||
error($c, "The user name ‘$owner’ does not exist.")
|
||||
unless defined $c->model('DB::Users')->find($owner);
|
||||
}
|
||||
|
||||
my $projectName = $c->stash->{projectName} or $project->name;
|
||||
error($c, "Invalid project name: ‘$projectName’") if $projectName !~ /^$projectNameRE$/;
|
||||
my $projectName = $c->stash->{params}->{name};
|
||||
error($c, "Invalid project identifier ‘$projectName’.") if $projectName !~ /^$projectNameRE$/;
|
||||
|
||||
error($c, "Cannot rename project to ‘$projectName’ since that identifier is already taken.")
|
||||
if $projectName ne $project->name && defined $c->model('DB::Projects')->find($projectName);
|
||||
|
||||
my $displayName = trim $c->stash->{params}->{displayname};
|
||||
error($c, "Invalid display name: $displayName") if $displayName eq "";
|
||||
error($c, "You must specify a display name.") if $displayName eq "";
|
||||
|
||||
$project->update(
|
||||
{ name => $projectName
|
||||
@ -244,10 +168,6 @@ sub updateProject {
|
||||
sub get_builds : Chained('projectChain') PathPart('') CaptureArgs(0) {
|
||||
my ($self, $c) = @_;
|
||||
$c->stash->{allBuilds} = $c->stash->{project}->builds;
|
||||
$c->stash->{jobStatus} = $c->model('DB')->resultset('JobStatusForProject')
|
||||
->search({}, {bind => [$c->stash->{project}->name]});
|
||||
$c->stash->{allJobsets} = $c->stash->{project}->jobsets;
|
||||
$c->stash->{allJobs} = $c->stash->{project}->jobs;
|
||||
$c->stash->{latestSucceeded} = $c->model('DB')->resultset('LatestSucceededForProject')
|
||||
->search({}, {bind => [$c->stash->{project}->name]});
|
||||
$c->stash->{channelBaseName} = $c->stash->{project}->name;
|
||||
|
@ -38,7 +38,7 @@ sub updateRelease {
|
||||
, description => trim $c->request->params->{description}
|
||||
});
|
||||
|
||||
$release->releasemembers->delete_all;
|
||||
$release->releasemembers->delete;
|
||||
foreach my $param (keys %{$c->request->params}) {
|
||||
next unless $param =~ /^member-(\d+)-description$/;
|
||||
my $buildId = $1;
|
||||
@ -72,7 +72,7 @@ sub submit : Chained('release') PathPart('submit') Args(0) {
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
updateRelease($c, $c->stash->{release});
|
||||
});
|
||||
$c->res->redirect($c->uri_for($self->action_for("project"),
|
||||
$c->res->redirect($c->uri_for($self->action_for("view"),
|
||||
[$c->stash->{project}->name, $c->stash->{release}->name]));
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use Hydra::Helper::CatalystUtils;
|
||||
use Digest::SHA1 qw(sha1_hex);
|
||||
use Nix::Store;
|
||||
use Nix::Config;
|
||||
use Encode;
|
||||
|
||||
# Put this controller at top-level.
|
||||
__PACKAGE__->config->{namespace} = '';
|
||||
@ -33,6 +34,7 @@ sub begin :Private {
|
||||
$c->stash->{inputTypes} = {
|
||||
'string' => 'String value',
|
||||
'boolean' => 'Boolean',
|
||||
'nix' => 'Nix expression',
|
||||
'build' => 'Build output',
|
||||
'sysbuild' => 'Build output (same system)'
|
||||
};
|
||||
@ -54,12 +56,8 @@ sub index :Path :Args(0) {
|
||||
$c->stash->{template} = 'overview.tt';
|
||||
$c->stash->{projects} = [$c->model('DB::Projects')->search(isAdmin($c) ? {} : {hidden => 0}, {order_by => 'name'})];
|
||||
$c->stash->{newsItems} = [$c->model('DB::NewsItems')->search({}, { order_by => ['createtime DESC'], rows => 5 })];
|
||||
$self->status_ok(
|
||||
$c,
|
||||
entity => [$c->model('DB::Projects')->search(isAdmin($c) ? {} : {hidden => 0}, {
|
||||
order_by => 'name',
|
||||
columns => [ 'name', 'displayname' ]
|
||||
})]
|
||||
$self->status_ok($c,
|
||||
entity => $c->stash->{projects}
|
||||
);
|
||||
}
|
||||
|
||||
@ -72,8 +70,7 @@ sub queue_GET {
|
||||
$c->stash->{flashMsg} //= $c->flash->{buildMsg};
|
||||
$self->status_ok(
|
||||
$c,
|
||||
entity => [$c->model('DB::Builds')->search(
|
||||
{finished => 0}, { join => ['project'], order_by => ["priority DESC", "id"], columns => [@buildListColumns], '+select' => ['project.enabled'], '+as' => ['enabled'] })]
|
||||
entity => [$c->model('DB::Builds')->search({finished => 0}, { order_by => ["priority DESC", "id"]})]
|
||||
);
|
||||
}
|
||||
|
||||
@ -100,22 +97,7 @@ sub status_GET {
|
||||
$c,
|
||||
entity => [ $c->model('DB::BuildSteps')->search(
|
||||
{ 'me.busy' => 1, 'build.finished' => 0, 'build.busy' => 1 },
|
||||
{ join => { build => [ 'project', 'job', 'jobset' ] },
|
||||
columns => [
|
||||
'me.machine',
|
||||
'me.system',
|
||||
'me.stepnr',
|
||||
'me.drvpath',
|
||||
'me.starttime',
|
||||
'build.id',
|
||||
{
|
||||
'build.project.name' => 'project.name',
|
||||
'build.jobset.name' => 'jobset.name',
|
||||
'build.job.name' => 'job.name'
|
||||
}
|
||||
],
|
||||
order_by => [ 'machine' ]
|
||||
}
|
||||
{ order_by => [ 'machine' ], join => [ 'build' ] }
|
||||
) ]
|
||||
);
|
||||
}
|
||||
@ -150,11 +132,9 @@ sub machines :Local Args(0) {
|
||||
sub get_builds : Chained('/') PathPart('') CaptureArgs(0) {
|
||||
my ($self, $c) = @_;
|
||||
$c->stash->{allBuilds} = $c->model('DB::Builds');
|
||||
$c->stash->{jobStatus} = $c->model('DB')->resultset('JobStatus');
|
||||
$c->stash->{allJobsets} = $c->model('DB::Jobsets');
|
||||
$c->stash->{allJobs} = $c->model('DB::Jobs');
|
||||
$c->stash->{latestSucceeded} = $c->model('DB')->resultset('LatestSucceeded');
|
||||
$c->stash->{channelBaseName} = "everything";
|
||||
$c->stash->{total} = $c->model('DB::NrBuilds')->find('finished')->count;
|
||||
}
|
||||
|
||||
|
||||
@ -213,35 +193,32 @@ sub default :Path {
|
||||
sub end : ActionClass('RenderView') {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
my @errors = map { encode_utf8($_); } @{$c->error};
|
||||
|
||||
if (defined $c->stash->{json}) {
|
||||
if (scalar @{$c->error}) {
|
||||
$c->stash->{json}->{error} = join "\n", @{$c->error};
|
||||
if (scalar @errors) {
|
||||
$c->stash->{json}->{error} = join "\n", @errors;
|
||||
$c->clear_errors;
|
||||
}
|
||||
$c->forward('View::JSON');
|
||||
}
|
||||
|
||||
if (scalar @{$c->error}) {
|
||||
$c->stash->{resource} = { errors => "$c->error" };
|
||||
elsif (scalar @{$c->error}) {
|
||||
$c->stash->{resource} = { error => join "\n", @{$c->error} };
|
||||
$c->stash->{template} = 'error.tt';
|
||||
$c->stash->{errors} = $c->error;
|
||||
$c->stash->{errors} = [@errors];
|
||||
$c->response->status(500) if $c->response->status == 200;
|
||||
if ($c->response->status >= 300) {
|
||||
$c->stash->{httpStatus} =
|
||||
$c->response->status . " " . HTTP::Status::status_message($c->response->status);
|
||||
}
|
||||
$c->clear_errors;
|
||||
} elsif (defined $c->stash->{resource} and
|
||||
(ref $c->stash->{resource} eq ref {}) and
|
||||
defined $c->stash->{resource}->{error}) {
|
||||
$c->stash->{template} = 'error.tt';
|
||||
$c->stash->{httpStatus} =
|
||||
$c->response->status . " " . HTTP::Status::status_message($c->response->status);
|
||||
}
|
||||
|
||||
$c->forward('serialize');
|
||||
$c->forward('serialize') if defined $c->stash->{resource};
|
||||
}
|
||||
|
||||
|
||||
sub serialize : ActionClass('Serialize') { }
|
||||
|
||||
|
||||
@ -282,6 +259,7 @@ sub narinfo :LocalRegex('^([a-z0-9]+).narinfo$') :Args(0) {
|
||||
my $path = queryPathFromHashPart($hash);
|
||||
|
||||
if (!$path) {
|
||||
$c->response->status(404);
|
||||
$c->response->content_type('text/plain');
|
||||
$c->stash->{plain}->{data} = "does not exist\n";
|
||||
$c->forward('Hydra::View::Plain');
|
||||
|
@ -182,15 +182,11 @@ sub currentUser :Path('/current-user') :ActionClass('REST') { }
|
||||
sub currentUser_GET {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
requireLogin($c) if !$c->user_exists;
|
||||
requireUser($c);
|
||||
|
||||
$self->status_ok(
|
||||
$c,
|
||||
entity => $c->model('DB::Users')->find({ 'me.username' => $c->user->username}, {
|
||||
columns => [ "me.fullname", "me.emailaddress", "me.username", "userroles.role" ]
|
||||
, join => [ "userroles" ]
|
||||
, collapse => 1
|
||||
})
|
||||
entity => $c->model("DB::Users")->find($c->user->username)
|
||||
);
|
||||
}
|
||||
|
||||
@ -198,9 +194,9 @@ sub currentUser_GET {
|
||||
sub user :Chained('/') PathPart('user') CaptureArgs(1) {
|
||||
my ($self, $c, $userName) = @_;
|
||||
|
||||
requireLogin($c) if !$c->user_exists;
|
||||
requireUser($c);
|
||||
|
||||
error($c, "You do not have permission to edit other users.")
|
||||
accessDenied($c, "You do not have permission to edit other users.")
|
||||
if $userName ne $c->user->username && !isAdmin($c);
|
||||
|
||||
$c->stash->{user} = $c->model('DB::Users')->find($userName)
|
||||
@ -287,7 +283,7 @@ sub edit_POST {
|
||||
}
|
||||
|
||||
if (isAdmin($c)) {
|
||||
$user->userroles->delete_all;
|
||||
$user->userroles->delete;
|
||||
$user->userroles->create({ role => $_})
|
||||
foreach paramToList($c, "roles");
|
||||
}
|
||||
@ -303,4 +299,19 @@ sub edit_POST {
|
||||
}
|
||||
|
||||
|
||||
sub dashboard :Chained('user') :Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
$c->stash->{template} = 'dashboard.tt';
|
||||
|
||||
# Get the N most recent builds for each starred job.
|
||||
$c->stash->{starredJobs} = [];
|
||||
foreach my $j ($c->stash->{user}->starredjobs->search({}, { order_by => ['project', 'jobset', 'job'] })) {
|
||||
my @builds = $j->job->builds->search(
|
||||
{ },
|
||||
{ rows => 20, order_by => "id desc" });
|
||||
push $c->stash->{starredJobs}, { job => $j->job, builds => [@builds] };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
@ -41,7 +41,7 @@ sub updateView {
|
||||
{ name => $viewName
|
||||
, description => trim $c->request->params->{description} });
|
||||
|
||||
$view->viewjobs->delete_all;
|
||||
$view->viewjobs->delete;
|
||||
|
||||
foreach my $param (keys %{$c->request->params}) {
|
||||
next unless $param =~ /^job-(\d+)-name$/;
|
||||
|
Reference in New Issue
Block a user