diff --git a/src/hydra-eval-jobs/hydra-eval-jobs.cc b/src/hydra-eval-jobs/hydra-eval-jobs.cc index 8f57fd8c..1fe94874 100644 --- a/src/hydra-eval-jobs/hydra-eval-jobs.cc +++ b/src/hydra-eval-jobs/hydra-eval-jobs.cc @@ -129,6 +129,7 @@ static void findJobsWrapped(EvalState & state, JSONObject & top, res.attr("schedulingPriority", drv.queryMetaInt("schedulingPriority", 100)); res.attr("timeout", drv.queryMetaInt("timeout", 36000)); res.attr("maxSilent", drv.queryMetaInt("maxSilent", 7200)); + res.attr("isChannel", drv.queryMetaBool("isHydraChannel", false)); /* If this is an aggregate, then get its constituents. */ Bindings::iterator a = v.attrs->find(state.symbols.create("_hydraAggregate")); diff --git a/src/lib/Hydra/Base/Controller/ListBuilds.pm b/src/lib/Hydra/Base/Controller/ListBuilds.pm index b971d5e8..2568b117 100644 --- a/src/lib/Hydra/Base/Controller/ListBuilds.pm +++ b/src/lib/Hydra/Base/Controller/ListBuilds.pm @@ -32,23 +32,16 @@ sub all : Chained('get_builds') PathPart { } -sub nix : Chained('get_builds') PathPart('channel') CaptureArgs(1) { +sub nix : Chained('get_builds') PathPart('channel/latest') CaptureArgs(1) { my ($self, $c, $channelName) = @_; - eval { - if ($channelName eq "latest") { - $c->stash->{channelName} = $c->stash->{channelBaseName} . "-latest"; - $c->stash->{channelBuilds} = $c->stash->{latestSucceeded} - ->search_literal("exists (select 1 from buildproducts where build = me.id and type = 'nix-build')") - ->search({}, { columns => [@buildListColumns, 'drvpath', 'description', 'homepage'] - , join => ["buildoutputs"] - , order_by => ["me.id", "buildoutputs.name"] - , '+select' => ['buildoutputs.path', 'buildoutputs.name'], '+as' => ['outpath', 'outname'] }); - } - else { - notFound($c, "Unknown channel `$channelName'."); - } - }; - error($c, $@) if $@; + + $c->stash->{channelName} = $c->stash->{channelBaseName} . "-latest"; + $c->stash->{channelBuilds} = $c->stash->{latestSucceeded} + ->search_literal("exists (select 1 from buildproducts where build = me.id and type = 'nix-build')") + ->search({}, { columns => [@buildListColumns, 'drvpath', 'description', 'homepage'] + , join => ["buildoutputs"] + , order_by => ["me.id", "buildoutputs.name"] + , '+select' => ['buildoutputs.path', 'buildoutputs.name'], '+as' => ['outpath', 'outname'] }); } diff --git a/src/lib/Hydra/Base/Controller/NixChannel.pm b/src/lib/Hydra/Base/Controller/NixChannel.pm index 19e5bf8e..3d68220c 100644 --- a/src/lib/Hydra/Base/Controller/NixChannel.pm +++ b/src/lib/Hydra/Base/Controller/NixChannel.pm @@ -130,6 +130,7 @@ sub channel_contents : Chained('nix') PathPart('') Args(0) { # garbage-collected. That should be true for the "latest" # channel. getChannelData($c, 0); + $c->stash->{genericChannel} = 1; $c->stash->{template} = 'channel-contents.tt'; $c->stash->{nixPkgs} = [sortPkgs @{$c->stash->{nixPkgs}}]; } diff --git a/src/lib/Hydra/Controller/Channel.pm b/src/lib/Hydra/Controller/Channel.pm new file mode 100644 index 00000000..902d6019 --- /dev/null +++ b/src/lib/Hydra/Controller/Channel.pm @@ -0,0 +1,83 @@ +package Hydra::Controller::Channel; + +use strict; +use warnings; +use base 'Hydra::Base::Controller::REST'; + + +sub channel : Chained('/') PathPart('channel/custom') CaptureArgs(3) { + my ($self, $c, $projectName, $jobsetName, $channelName) = @_; + + $c->stash->{project} = $c->model('DB::Projects')->find($projectName); + + notFound($c, "Project $projectName doesn't exist.") + if !$c->stash->{project}; + + $c->stash->{jobset} = $c->stash->{project}->jobsets->find({ + name => $jobsetName + }); + + notFound($c, "Jobset $jobsetName doesn't exist.") + if !$c->stash->{jobset}; + + my $lastSuccessful = $c->model('DB::Builds')->find( + { 'eval.hasnewbuilds' => 1 + , project => $projectName + , jobset => $jobsetName + , job => $channelName + , buildstatus => 0 + }, + { rows => 1, order_by => "eval.id desc" + , join => { jobsetevalmembers => 'eval' } + } + ); + + notFound($c, "Channel $channelName either doesn't exist ". + "or was never built successfully.") + if !$lastSuccessful; + + $c->stash->{lastSuccessful} = $lastSuccessful; +} + + +sub overview : Chained('channel') PathPart('') Args(0) { + my ($self, $c) = @_; + + $c->stash->{constituents} = [ + $c->stash->{lastSuccessful}->constituents_->search( + {}, {order_by => ["job"]} + ) + ]; + + $c->stash->{genericChannel} = 0; + $c->stash->{template} = 'channel-contents.tt'; +} + + +sub nixexprs : Chained('channel') PathPart('') Args(1) { + my ($self, $c, $productName) = @_; + + my $product = $c->stash->{lastSuccessful}->buildproducts->find( + { type => "channel", name => $productName } + ); + + my $url = $c->uri_for( + $c->controller("Build")->action_for("download"), + [$c->stash->{lastSuccessful}->id], + $product->productnr, + $productName + ); + + $c->res->redirect($url); +} + + +sub binary_cache_url : Chained('channel') PathPart('binary-cache-url') Args(0) { + my ($self, $c) = @_; + $c->stash->{'plain'} = { data => $c->uri_for('/') }; + $c->response->content_type('text/plain'); + $c->forward('Hydra::View::Plain'); +} + + +1; diff --git a/src/lib/Hydra/Controller/Jobset.pm b/src/lib/Hydra/Controller/Jobset.pm index 1d1cce63..f2b801f1 100644 --- a/src/lib/Hydra/Controller/Jobset.pm +++ b/src/lib/Hydra/Controller/Jobset.pm @@ -108,43 +108,43 @@ sub jobs_tab : Chained('jobsetChain') PathPart('jobs-tab') Args(0) { $c->stash->{filter} = $c->request->params->{filter} // ""; my $filter = "%" . $c->stash->{filter} . "%"; - my @evals = $c->stash->{jobset}->jobsetevals->search({ hasnewbuilds => 1}, { order_by => "id desc", rows => 20 }); - - 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}->{timestamp} = $eval->timestamp; - $evals->{$eval->id}->{jobs}->{$jobName} = - { id => $b->id, finished => $b->finished, buildstatus => $b->buildstatus }; - $jobs{$jobName} = 1; - $nrBuilds++; - } - last if $nrBuilds >= 10000; - } + my ($evals, $builds) = searchBuildsAndEvalsForJobset( + $c->stash->{jobset}, + { job => { ilike => $filter }, ischannel => 0 }, + 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; + next if defined $builds->{$job->name}; + $c->stash->{inactiveJobs}->{$job->name} = $builds->{$job->name} = 1; } } $c->stash->{evals} = $evals; - my @jobs = sort (keys %jobs); + my @jobs = sort (keys %$builds); $c->stash->{nrJobs} = scalar @jobs; splice @jobs, 250 if $c->stash->{filter} eq ""; $c->stash->{jobs} = [@jobs]; } +sub channels_tab : Chained('jobsetChain') PathPart('channels-tab') Args(0) { + my ($self, $c) = @_; + $c->stash->{template} = 'jobset-channels-tab.tt'; + + my ($evals, $builds) = searchBuildsAndEvalsForJobset( + $c->stash->{jobset}, + { ischannel => 1 } + ); + + $c->stash->{evals} = $evals; + my @channels = sort (keys %$builds); + $c->stash->{channels} = [@channels]; +} + + # Hydra::Base::Controller::ListBuilds needs this. sub get_builds : Chained('jobsetChain') PathPart('') CaptureArgs(0) { my ($self, $c) = @_; diff --git a/src/lib/Hydra/Helper/AddBuilds.pm b/src/lib/Hydra/Helper/AddBuilds.pm index ae93e243..4d87e34c 100644 --- a/src/lib/Hydra/Helper/AddBuilds.pm +++ b/src/lib/Hydra/Helper/AddBuilds.pm @@ -453,6 +453,7 @@ sub checkBuild { , busy => 0 , locker => "" , iscurrent => 1 + , ischannel => $buildInfo->{isChannel} }); $build->buildoutputs->create({ name => $_, path => $buildInfo->{outputs}->{$_} }) diff --git a/src/lib/Hydra/Helper/CatalystUtils.pm b/src/lib/Hydra/Helper/CatalystUtils.pm index d31b3f53..db4640e9 100644 --- a/src/lib/Hydra/Helper/CatalystUtils.pm +++ b/src/lib/Hydra/Helper/CatalystUtils.pm @@ -10,6 +10,7 @@ use Hydra::Helper::Nix; our @ISA = qw(Exporter); our @EXPORT = qw( getBuild getPreviousBuild getNextBuild getPreviousSuccessfulBuild + searchBuildsAndEvalsForJobset error notFound gone accessDenied forceLogin requireUser requireProjectOwner requireAdmin requirePost isAdmin isProjectOwner trim @@ -85,6 +86,44 @@ sub getPreviousSuccessfulBuild { } +sub searchBuildsAndEvalsForJobset { + my ($jobset, $condition, $maxBuilds) = @_; + + my @evals = $jobset->jobsetevals->search( + { hasnewbuilds => 1}, + { order_by => "id desc", + rows => 20 + }); + + my $evals = {}; + my %builds; + my $nrBuilds = 0; + + foreach my $eval (@evals) { + my @allBuilds = $eval->builds->search( + $condition, + { columns => ['id', 'job', 'finished', 'buildstatus'] } + ); + + foreach my $b (@allBuilds) { + my $jobName = $b->get_column('job'); + + $evals->{$eval->id}->{timestamp} = $eval->timestamp; + $evals->{$eval->id}->{builds}->{$jobName} = { + id => $b->id, + finished => $b->finished, + buildstatus => $b->buildstatus + }; + $builds{$jobName} = 1; + $nrBuilds++; + } + last if $maxBuilds && $nrBuilds >= $maxBuilds; + } + + return ($evals, \%builds); +} + + sub error { my ($c, $msg, $status) = @_; $c->response->status($status) if defined $status; diff --git a/src/lib/Hydra/Schema/Builds.pm b/src/lib/Hydra/Schema/Builds.pm index b9dc0437..a22cd670 100644 --- a/src/lib/Hydra/Schema/Builds.pm +++ b/src/lib/Hydra/Schema/Builds.pm @@ -116,6 +116,12 @@ __PACKAGE__->table("Builds"); default_value: 36000 is_nullable: 1 +=head2 ischannel + + data_type: 'integer' + default_value: 0 + is_nullable: 1 + =head2 iscurrent data_type: 'integer' @@ -239,6 +245,8 @@ __PACKAGE__->add_columns( { data_type => "integer", default_value => 3600, is_nullable => 1 }, "timeout", { data_type => "integer", default_value => 36000, is_nullable => 1 }, + "ischannel", + { data_type => "integer", default_value => 0, is_nullable => 1 }, "iscurrent", { data_type => "integer", default_value => 0, is_nullable => 1 }, "nixexprinput", @@ -558,8 +566,8 @@ __PACKAGE__->many_to_many( ); -# Created by DBIx::Class::Schema::Loader v0.07043 @ 2015-08-10 15:10:41 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:rjifgnPtjY96MaQ7eiGzaA +# Created by DBIx::Class::Schema::Loader v0.07043 @ 2015-09-10 17:34:23 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:JRelp13Cyfi+QVxC92xuqQ __PACKAGE__->has_many( "dependents", diff --git a/src/root/build.tt b/src/root/build.tt index d56988e2..369e5648 100644 --- a/src/root/build.tt +++ b/src/root/build.tt @@ -190,7 +190,7 @@
Alternatively, if you have associated the application/nix-package MIME type with the nix-install-package program in your web browser, you can install the package simply by clicking on the packages below.
-This channel contains the following packages.
@@ -69,5 +70,17 @@ install the package simply by clicking on the packages below. +[% ELSE %] +[% PROCESS "product-list.tt" %] + +Upgrades depend on the success/failure of the following constituents:
+ +[% INCLUDE renderBuildList builds=constituents %] + +[% END %] [% END %] diff --git a/src/root/jobset-channels-tab.tt b/src/root/jobset-channels-tab.tt new file mode 100644 index 00000000..c003d70e --- /dev/null +++ b/src/root/jobset-channels-tab.tt @@ -0,0 +1,34 @@ +[% PROCESS common.tt %] + +[% IF channels.size == 0 %] + +Channel | + [% FOREACH eval IN evalIds %] ++ | + [% END %] +
---|---|
[% chan %] | + [% FOREACH eval IN evalIds %] +[% r = evals.$eval.builds.$chan; IF r.id %][% INCLUDE renderBuildStatusIcon size=16 build=r %][% END %] | + [% END %] +