doesn't scale), and include links for jobset/job specific pages. The main page now lists the projects.
ODNCGFQ5FPKFI624BVMLW7PJ2EFJOR3TY66OCZM42UNNTWBCF2TQC G2YVJTFT3CBKH4LZQNXLYWUMHHXG546HAP4AKIYH3WT3OKBHPFQAC CV5HAOSHQSJHVCH2METLWNE4PUTOU2C6A3ORC7QJ3DQ5BFSPELBQC KVPYKWZ76TKVZMXWKVXVS3VJANPPSDLNPMITJRO64YDZ443ZOUZAC 6ULJO5NIWL2PDPQW3MBQJNLYHU5STO4EUCD4ZPVQKBOCRNUPMC6QC T2232OBSNVDGHY46RY5BBB2IET5LV5OLKSSBDYUF7KAEDBUOTC7QC DEMSSSB22UI2ZN26C56UYO4NZWMDCRYKLAQARGOTSORSDXX3ZNNQC 3HZY24CX4U2TO74HOY4YX3LBJIYF4DLXHCIY7J2RASAC4COMSMZAC UWVMQIAC2HQNSG2JQOPZGUOCQ5V2JFP2F7RCTF3WJLK7NHSD5PAAC FPK5LF53CFUEKFYJ3IYXT4UTVC6IITWJOCFATMC4PLHEUP5SIEAAC NLJJZVHO3UXBURL2P7VGGCVUOMKFUYT3UX5JXQU3FFFAHUGEKO3AC 7ZHHVD6QVZCR7OHF3OIS52DF7ZIQDIS27OXCKOGMFDO2CTIOGI5AC 7UJ5YV4VUSXMKB57KKXDG6KLIXMQXO45VO5UTOT46BHQJBFZ7XKQC JLDUSNUOOQNL63BOPXIWZOWFRQ5X35RWG33PJB3J3KMR6QR7TN7QC UAPS46BQNEZACYO2LYXEXIK5YESDG2N2B3BYIZGVETGP6LOM4OGAC J5UVLXOK6EDIL5I7VKWH4V2QDS4DPD7FHRK6XBWSXFRQS4JKXFZQC IE3SRMWZ4YT6EYKAOG2HH54MXG5TYYBW7OEBHNYJUGW7KILFXX2AC FHF6IZJQPUQHY5QWQYRPZVDBRLHREWRHGNKVQDT7F3GQKKLZXJKQC 5NO7NCKTMM5ZW7JYOETUFOSWK2ACTXWDZGJAFXZN6L3OF6BFTNOQC LQ5QEDVVHVGX67NJZQ46HOSY336VCLN63L3DT6QNSDV6ARVARITAC LBNVQXUBEZ45SOTGVXK5UEZXIAIZTJLWZNUYFI4JZ6J65N3KPDVQC US27ZTX5HCH53SQN2KFSCV2GL2625XL5J3WCDR7D5HLPA7ZSPX5AC D5QIOJGPKQJIYBUCSC3MFJ3TXLPNZ2XMI37GXMFRVRFWWR2VMTFAC TQKGQ5R36VOLLJ3SR2WWIQ6VSN245N3QG4JHI2ZCP5T6RXN3PJWAC BVOPAMLSAU4UTV3DUX53OYDMXP2SETAQVUKAYE2OTCVVN4RD7LLQC WHAFVCEIHCWHKY2UVRGRQORERUAK6SZJI7ZENA367ZX4EXDIQMDQC YTSIRIMKV2EN6CMO6LFX5J3NBJJYWBJKYDU3Z53CGXCIWP3FYI2QC 7Z3YOKCVJE242IDO4HQVOBBLHFOXXCQIBIKDIUXQLTU5LY5QAORQC 67P45PY4GTWQXZRCMR734D5YYN2OERZM57NBB2CZXEULQT2GRFNAC RP6WDKE2VGNWZU3XBJSESTJWVV5BKZZ5SXPKT5N6XZIJ6XRTNWZQC G6HJY2V4CSSZF6BPIMRMRAJYRKPLTCBUFTJWZWK3BSNT235CZQ6QC 5IK6NYKFNY25MHV4SHACJMVGFZBRQICVSOUWNGZFAQ2GO6QUBYNAC IGNQFFV767W2N53L634LEZSBJC35OVC3BDLB52RIRAUEFTPX3ZYQC GNIEG2GCT6BUYHY2WXUAQVKHSYB6TVQT52O5GEY6COLKFK4ODCCQC L2E6EVE2RVFVDCUNRJ4CZYSQNS2DZUA5DTBETHBDUQUV2KQQRAOQC 7LKUAIGCURNGNBBOAGK4EYIUF6KNWHJNLA6ZJEXFLA5HUADAJP6QC KOTB7BKVML6T6S5ZNTQ6456FMGCRZCP3E3KVWCOW7T6SPRHC53LAC IK53RV4VGOHLCZGQCCIKPB45M3C7M7YMNBOJFBGZJ4LWIZNU4QNQC JFZNAYJXKCMXYHGCLTRH7Q6TOFGJ4BT6332GONCWVYRLNMDDG3KAC KR7QIG6ZMETIRAEYRHI26FVSFGJJEE2OT46XCHRI6RBBZA36GNXAC PBFZEQLZXMOBQIWL5WQWH7D62XG6M7ROEEAH6PWAKKKOM3W5I3RQC HCKFTNQ6J62JYGWKRSO2523L67JXRKG64NFHUMTHDO2JXIDVUIHQC NDL67SQT5CLURSU5H27MJ5COTEQNU5ZMVNAMD4V2BA3G6MQT5VSQC DGVPFNCUZCMOYMY2THYRZ4WDNCBVCUMJHMCQ7Q3WQVZ6MGY47JUAC 2AUODJBTW2UJ47UB3SREZ7O7UTTJDQ4AGUQMPZAI4F3Y6BUF6LUQC 2AIIYGI54TEFYBCGWMUA7JG2DMJXBKMYUZ7CG6ETIXF4ENNP74CQC JD27RBKM7ROBUKBEQDFSCPO3MDE2AFZ6DSSKFUPJ7IC2MCGDVFGAC S66BOMVUACAUDSGSDWP7ZIXVMZSQHWXOZYVTB7ILUCWZ7DDFAKVAC 7YBYT2LQML2PKEO6UO4444AGSASS664UCDXW2YO3ALB7THQHCEBQC W6DC6K4INJQOJYR553ISCKZV7YIOGHEM3FZQPOLAPSZQ3KSJDMRQC YTZOC7C5N3JI3AOFPLT2UM7567XIKPSCMFKSESB6J4LNVVW3DOCAC I35CIZI7RMBRNCRF2Z3WWFWNMDSSDKU7YMBHGPPGZKPZ6PSRTV7AC YAPITGB3ENS6PXRBFC647ESCQUYG442DKHUPM46PJKCKTMHWGVXAC SZZ3PFYFHYC6YUGS3VMTUZ7CKKKJHRBTA52P7ONZRSDJWW23SO5QC KFFNFE4D7OTXSHUI5W5FD4SZNZIUCOFNY44ZCQXG5SNAPHYQYQHAC HK32XC42XOS7JKJMFT3XJKPXVSI44SFGKUUSJIL5VGPIHYNNWXLAC LQNBKF3DKVYK3NPDGK7OLOTIWIF4HR3EKLRYNLZJB4G3NMIVBCEAC 3ZCEPLNOOWRHKM75FJJVQHHYIAHXAYKFF5SEKQGVSMLRAF4NOBIAC SHZLOM5M2NVH2J7CJJMS562EV5NPCTNWQ5P3K2SSL2YFYUI7PMIQC H7CNGK4OJNRYZQGPLBGR72DULLEPFQ5UISF5J24D7IMA7SYW5LGQC HJLYC7537T37E5AQ4Y4PYDBRLVIZVWYS4R7WPSLGO6SJPS6M6LMAC GCHNNFZPCYM3BPOCU5GASALJ6ONWMEVEEGVD7KKLTWC7YQKJ5KZAC OIUIYIV223PEQ47UPM5FKE24OTYCCZRHS2UON4XSEKQWRVZWJCIAC B72GLND4UQKSGNGIY4FA6HLQNFS42SKRXV5NHTHADUJRU73NELAAC DVNWJXWWZAR4LGE3FGGY64EW5U6GWH6MXXAALWPO3EMI4NO42ZXAC $c->go($self->action_for("all"));
$c->stash->{template} = 'jobset.tt';getBuildStats($c, scalar $c->stash->{jobset}->builds);$c->stash->{activeJobs} = [$c->stash->{jobset}->jobs->search({active => 1})];$c->stash->{inactiveJobs} = [$c->stash->{jobset}->jobs->search({active => 0})];
}sub edit : Chained('jobset') PathPart Args(0) {my ($self, $c) = @_;requireProjectOwner($c, $c->stash->{project});$c->stash->{template} = 'jobset.tt';$c->stash->{edit} = 1;}sub submit : Chained('jobset') PathPart Args(0) {my ($self, $c) = @_;requireProjectOwner($c, $c->stash->{project});requirePost($c);$c->model('DB')->schema->txn_do(sub {updateJobset($c, $c->stash->{jobset});});$c->res->redirect($c->uri_for($self->action_for("index"),[$c->stash->{project}->name, $c->stash->{jobset}->name]));}sub delete : Chained('jobset') PathPart Args(0) {my ($self, $c) = @_;requireProjectOwner($c, $c->stash->{project});requirePost($c);$c->model('DB')->schema->txn_do(sub {$c->stash->{jobset}->delete;});$c->res->redirect($c->uri_for($c->controller('Project')->action_for("view"),[$c->stash->{project}->name]));}sub updateJobset {my ($c, $jobset) = @_;my $jobsetName = trim $c->request->params->{"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->{"nixexprpath"};error($c, "Invalid Nix expression path: $nixExprPath") if $nixExprPath !~ /^$relPathRE$/;my $nixExprInput = trim $c->request->params->{"nixexprinput"};error($c, "Invalid Nix expression input name: $nixExprInput") unless $nixExprInput =~ /^\w+$/;$jobset->update({ name => $jobsetName, description => trim($c->request->params->{"description"}), nixexprpath => $nixExprPath, nixexprinput => $nixExprInput});my %inputNames;# Process the inputs of this jobset.foreach my $param (keys %{$c->request->params}) {next unless $param =~ /^input-(\w+)-name$/;my $baseName2 = $1;next if $baseName2 eq "template";print STDERR "GOT INPUT: $baseName2\n";my $inputName = trim $c->request->params->{"input-$baseName2-name"};error($c, "Invalid input name: $inputName") unless $inputName =~ /^[[:alpha:]]\w*$/;my $inputType = trim $c->request->params->{"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" ||$inputType eq "build";$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 input$input = ($jobset->jobsetinputs->search({name => $baseName2}))[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;my $values = $c->request->params->{"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};}
}sub create_jobset : Chained('project') PathPart('create-jobset') Args(0) {my ($self, $c) = @_;requireProjectOwner($c, $c->stash->{project});$c->stash->{template} = 'jobset.tt';$c->stash->{create} = 1;$c->stash->{edit} = 1;}sub create_jobset_submit : Chained('project') PathPart('create-jobset/submit') Args(0) {my ($self, $c) = @_;requireProjectOwner($c, $c->stash->{project});my $jobsetName = trim $c->request->params->{name};$c->model('DB')->schema->txn_do(sub {# Note: $jobsetName is validated in updateProject, which will# abort the transaction if the name isn't valid.my $jobset = $c->stash->{project}->jobsets->create({name => $jobsetName, nixexprinput => "", nixexprpath => ""});Hydra::Controller::Jobset::updateJobset($c, $jobset);});$c->res->redirect($c->uri_for($c->controller('Jobset')->action_for("index"),[$c->stash->{project}->name, $jobsetName]));
$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 jobsetmy $oldName = trim $c->request->params->{"jobset-$baseName-oldName"};$jobset = ($project->jobsets->search({name => $oldName}))[0] or die;$jobset->update({ name => $jobsetName, description => $description, nixexprpath => $nixExprPath, nixexprinput => $nixExprInput });}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" ||$inputType eq "build";$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->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;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};}
$project->update({ name => $projectName, displayname => $displayName, description => trim($c->request->params->{description}), homepage => trim($c->request->params->{homepage}), enabled => trim($c->request->params->{enabled}) eq "1" ? 1 : 0, owner => $owner});
sub releasesets :Local {my ($self, $c, $projectName) = @_;$c->stash->{template} = 'releasesets.tt';my $project = $c->model('DB::Projects')->find($projectName);notFound($c, "Project $projectName doesn't exist.") if !defined $project;$c->stash->{project} = $project;$c->stash->{releaseSets} = [$project->releasesets->all];}
<h1>Hydra Overview</h1>[% PROCESS common.tt %][% END %][% INCLUDE showBuildStats %]<h2>Statistics</h2>[% WRAPPER layout.tt title="Overview" %]
[% WRAPPER layout.tt title="Release Sets" %][% PROCESS common.tt %]<h1>Release Sets</h1><ul>[% FOREACH releaseSet IN releaseSets %][% END %]</ul>[% END %][% ELSE %][% END %]<p>[<a href="[% c.uri_for('/create_releaseset' project.name) %]">Create a new release set</a>]</p><p>Project <tt>[% project.name %]</tt> has no release sets.</p><li></li><a href="[% c.uri_for('/releases' project.name releaseSet.name) %]"><tt>[% releaseSet.name %]</tt></a>[<a href="[% c.uri_for('/releases' project.name releaseSet.name "edit") %]">Edit</a>]<p>Project <tt>[% project.name %]</tt> has the following release sets:</p>[% IF releaseSets.size > 0 %]
[% END -%][% BLOCK maybeLink -%][% IF uri %]<a [% HTML.attributes(href => uri) %]>[% content %]</a>[% ELSE; content; END -%][% END -%][% BLOCK renderSelection %][% IF edit %]<select [% HTML.attributes(id => param, name => param) %]>[% FOREACH name IN options.keys.sort %]<option [% HTML.attributes(value => name) %] [% IF name == curValue; "selected='selected'"; END %]>[% options.$name %]</option>[% END %]</select>[% ELSE %][% options.$curValue %][% END %][% END %][% BLOCK maybeEditString;IF edit -%]<input type="text" class="string [% extraClass %]" [% HTML.attributes(id => param, name => param, value => value) %] />[% ELSE;HTML.escape(value);END -%]
[% WRAPPER layout.tt title="Job ‘$project.name:$jobset.name:$job.name’" %][% PROCESS common.tt %]<h1>Job <tt>[% project.name %]:[% jobset.name %]:[% job.name %]</tt></h1><h2>Channels</h2><p>This job provides the following Nix channels:</p><ul><li><a href="[% c.uri_for('/job' project.name jobset.name job.name'channel' 'latest') %]"><tt>latest</tt></a> — contains the latestsuccessful build for each platform.</li><li><a href="[% c.uri_for('/job' project.name jobset.name job.name'channel' 'all') %]"><tt>all</tt></a> — contains every successfulbuild of this job.</li></ul><h2>Statistics</h2>[% INCLUDE showBuildStats %][% END %]
[% WRAPPER layout.tt title=(edit ? (create ? "New Jobset in Project ‘$project.name’" : "Editing Jobset ‘$project.name:$jobset.name’") : "Jobset ‘$project.name:$jobset.name’") %][% PROCESS common.tt %][% IF edit %]<form action="[% IF create %][% c.uri_for('/project' project.name 'create-jobset/submit') %][% ELSE %][% c.uri_for('/jobset' project.name jobset.name 'submit') %][% END %]" method="post">[% END %][% IF create %]<h1>New Jobset in Project <tt>[% project.name %]</tt></h1>[% ELSE %]<h1>Jobset <tt>[% project.name %]:[% jobset.name %]</tt></h1>[% END %][% BLOCK renderInputAlt %][% IF edit %]<button type="button" onclick='$(this).parents(".inputalt").remove()'><img src="/static/images/failure.gif" alt="Delete value" /></button>[% INCLUDE maybeEditString param=param value=alt.value %]<br />[% ELSE %][% INCLUDE maybeEditString param=param value=alt.value %][% END %][% END %][% BLOCK renderInput %]<tr class="input [% extraClass %]" [% IF id %]id="[% id %]"[% END %]><td>[% IF edit %]<button type="button" onclick='$(this).parents(".input").remove()'><img src="/static/images/failure.gif" alt="Delete input" /></button>[% END -%]<tt>[% INCLUDE maybeEditString param="$baseName-name" value=input.name extraClass="shortString" %]</tt></td><td>[% INCLUDE renderSelection curValue=input.type param="$baseName-type" options=inputTypes %]</td><td class="inputalts" id="[% baseName %]">[% FOREACH alt IN input.jobsetinputalts -%]<tt class="inputalt">[% IF input.type == "string" && !edit %]"[% HTML.escape(alt.value) %]"[% ELSE %][% INCLUDE renderInputAlt alt=alt param="$baseName-values" %][% END %]</tt>[% END %][% IF edit %]<button type="button" onclick='return false' class="add-inputalt">+</button>[% END %]</td></tr>[% END %]<h2>Information[% IF !edit %] <a class="smallLink" href="[% c.uri_for('/jobset' project.name jobset.name 'edit') %]">[Edit]</a>[% END %]</h2><table class="layoutTable">[% IF edit %]<tr><th>Identifier:</th><td>[% INCLUDE maybeEditString param="name" value=jobset.name %]</td></tr>[% END %]<tr><th>Description:</th><td>[% INCLUDE maybeEditString param="description" value=jobset.description %]</td></tr><tr><th>Nix expression:</th><td><tt>[% INCLUDE maybeEditString param="nixexprpath" value=jobset.nixexprpath extraClass="shortString" %]</tt> in input<tt>[% INCLUDE maybeEditString param="nixexprinput" value=jobset.nixexprinput extraClass="shortString" %]</tt></td></tr>[% IF !edit %]<tr><th>Last checked:</th><td>[% IF jobset.lastcheckedtime %][% INCLUDE renderDateTime timestamp = jobset.lastcheckedtime -%][% IF jobset.errormsg -%]<em>, evaluation error</em>:<pre class="multiLineMsg error">[% HTML.escape(jobset.errormsg) %]</pre>[% ELSE %], <em>no errors</em>[% END %][% ELSE %]<em>never</em>[% END %]</td></tr>[% END %]</table><h3>Inputs</h3><table class="tablesorter"><thead><tr><th>Input name</th><th>Type</th><th>Values</th></tr></thead><tbody class="inputs">[% FOREACH input IN jobset.jobsetinputs -%][% INCLUDE renderInput input=input baseName="input-$input.name" %][% END %][% IF edit %]<tr><td colspan="3"><button type="button" class="add-input">Add a new input</button></td></tr>[% END %]</tbody></table>[% IF !edit %]<h2>Jobs</h2><p>This jobset currently contains the following [% activeJobs.size %] jobs:<blockquote>[% IF activeJobs.size == 0 %]<em>(none)</em>[% END %][% FOREACH j IN activeJobs %] [% INCLUDE renderJobName project=project.name jobset=jobset.name job=j.name %] [% END %]</blockquote></p><p>This jobset used to contain the following [% inactiveJobs.size %] jobs:<blockquote>[% IF inactiveJobs.size == 0 %]<em>(none)</em>[% END %][% FOREACH j IN inactiveJobs %] [% INCLUDE renderJobName project=project.name jobset=jobset.name job=j.name %] [% END %]</blockquote></p><h2>Channels</h2><p>This jobset provides the following Nix channels:</p><ul><li><a href="[% c.uri_for('/jobset' project.name jobset.name 'channel''latest') %]"><tt>latest</tt></a> — contains the latest successfulbuild of every job in this jobset.</li><li><a href="[% c.uri_for('/jobset' project.name jobset.name 'channel''all') %]"><tt>all</tt></a> — contains every successful,non-garbage-collected build of every job in this project.</li></ul><h2>Statistics</h2>[% INCLUDE showBuildStats %][% END %][% IF edit %]<table class="template"> <!-- dummy wrapper needed because “hidden” trs are visible anyway -->[% INCLUDE renderInput input="" extraClass="template" id="input-template" baseName="input-template" %]</table><tt class="inputalt template" id="inputalt-template">[% INCLUDE renderInputAlt alt=alt %]</tt><script type="text/javascript">$(document).ready(function() {var id = 0;$(".add-input").click(function() {var newid = "input-" + id++;var x = $("#input-template").clone(true).attr("id", "").insertBefore($(this).parents("tr")).show();$("#input-template-name", x).attr("name", newid + "-name");$("#input-template-type", x).attr("name", newid + "-type");$("#input-template", x).attr("id", newid);return false;});$(".add-inputalt").click(function() {var x = $("#inputalt-template").clone(true).insertBefore($(this)).attr("id", "").show();$("input", x).attr("name", x.parents(".inputalts").attr("id") + "-values");});});</script><p><button type="submit"><img src="/static/images/success.gif" />[%IF create %]Create[% ELSE %]Apply changes[% END %]</button></p></form>[% IF !create %]<form action="[% c.uri_for('/jobset' project.name jobset.name 'delete') %]" method="post"><p><button id="delete-jobset" type="submit"><img src="/static/images/failure.gif" />Delete this jobset</button></p></form><script type="text/javascript">$("#delete-jobset").click(function() {return confirm("Are you sure you want to delete this jobset?");});</script>[% END %][% END %][% END %]
[% BLOCK makeLinkWrapped %]<li [% IF curUri == uri %]class="active"[% END %]><div class="title"><a href="[% uri %]">[% title %]</a></div>[% content %]</li>[% END %][% BLOCK makeLink -%][% INCLUDE makeLinkWrapped content="" -%][% END %]
<div id="logo"><img src="/static/images/hydra.png" alt="Hydra" /></div><ul class="menu"><li><div class="title">Hydra</div><ul class="submenu">[% INCLUDE makeLink uri = c.uri_for('/') title = "Overview" %][% INCLUDE makeLink uri = c.uri_for('/queue') title = "Queue" %][% INCLUDE makeLink uri = c.uri_for('/jobstatus') title = "Job status" %][% INCLUDE makeLink uri = c.uri_for('/all') title = "All builds" %]</ul></li><li><div class="title">Projects</div><ul class="submenu">[% FOREACH project_ IN projects %]<li [% IF curUri == c.uri_for('/project' project_.name) %]class="active"[% END %]><div class="title"><a href="[% c.uri_for('/project' project_.name) %]">[% HTML.escape(project_.displayname) %]</a></div>[% IF project.name == project_.name %]<ul class="subsubmenu">[% INCLUDE makeLink uri = c.uri_for('/project' project.name 'jobstatus') title = "Job status" %][% INCLUDE makeLink uri = c.uri_for('/project' project.name 'all') title = "All builds" %][% WRAPPER makeLinkWrapped uri = c.uri_for('/releasesets' project.name) title = "Releases" %][% IF project.releasesets && project.releasesets.size > 0 %]<ul class="subsubsubmenu">[% FOREACH releaseset IN project.releasesets %][% myUri = c.uri_for('/releases' project.name releaseset.name) %]<li [% IF curUri == myUri %]class="active"[% END %]><div class="title"><a href="[% myUri %]">[% HTML.escape(releaseset.description ? releaseset.description : releaseset.name) %]</a></div></li>[% END %]</ul>[% END %][% END %][% INCLUDE makeLink uri = c.uri_for('/project' project.name 'edit') title = "Edit" %]</ul>[% END %]</li>[% END %]</ul></li><li><div class="title">Admin</div><ul class="submenu">[% IF c.user_exists %][% INCLUDE makeLink uri = c.uri_for('/logout') title = "Logout" %][% ELSE %][% INCLUDE makeLink uri = c.uri_for('/login') title = "Login" %][% END %][% INCLUDE makeLink uri = c.uri_for('/create-project') title = "Create project" %]</ul></li></ul>
<div id="logo"><a class="no-hover" href="/"><img src="/static/images/hydra.png" alt="Hydra" /></a></div>[% PROCESS navbar.tt %]
[% BLOCK makeLinkWrapped %]<li [% IF curUri == uri %]class="active"[% END %]><div class="title"><a href="[% uri %]">[% title %]</a></div>[% content %]</li>[% END %][% BLOCK makeLink -%][% INCLUDE makeLinkWrapped content="" -%][% END %][% BLOCK makeSubMenu %][% extra = collapsed ? "collapsed" : "" %]<li class="submenu"><div class="title [% extra %]"><a class="[% collapsed ? "submenuToggleCollapsed" : "submenuToggleExpanded" %]"href="javascript:">[% HTML.escape(title) %]</a></div><ul class="submenu [% extra %]">[% content %]</ul></li>[% END %]<ul class="menu">[% WRAPPER makeSubMenu title="Hydra" collapsed=0 %][% INCLUDE makeLinkuri = c.uri_for(c.controller('Root').action_for('index'))title = "Overview" %][% INCLUDE makeLinkuri = c.uri_for(c.controller('Root').action_for('queue'))title = "Queue" %][% INCLUDE makeLinkuri = c.uri_for(c.controller('Root').action_for('all'))title = "All builds" %][% INCLUDE makeLinkuri = c.uri_for(c.controller('Root').action_for('jobstatus'))title = "Job status" %][% END %][% IF project %][% WRAPPER makeSubMenu title="Project" collapsed=(jobset || job) %][% INCLUDE makeLinkuri = c.uri_for(c.controller('Project').action_for('view'), [project.name])title = "Overview" %][% INCLUDE makeLinkuri = c.uri_for(c.controller('Project').action_for('all'), [project.name])title = "All builds" %][% INCLUDE makeLinkuri = c.uri_for(c.controller('Project').action_for('jobstatus'), [project.name])title = "Job status" %][% END %][% END %][% IF jobset %][% WRAPPER makeSubMenu title="Jobset" collapsed=job %][% INCLUDE makeLinkuri = c.uri_for(c.controller('Jobset').action_for('index'), [project.name, jobset.name])title = "Overview" %][% INCLUDE makeLinkuri = c.uri_for(c.controller('Jobset').action_for('all'), [project.name, jobset.name])title = "All builds" %][% INCLUDE makeLinkuri = c.uri_for(c.controller('Jobset').action_for('jobstatus'), [project.name, jobset.name])title = "Job status" %][% END %][% END %][% IF job %][% WRAPPER makeSubMenu title="Job" %][% INCLUDE makeLinkuri = c.uri_for(c.controller('Job').action_for('index'), [project.name, jobset.name, job.name])title = "Overview" %][% INCLUDE makeLinkuri = c.uri_for(c.controller('Job').action_for('all'), [project.name, jobset.name, job.name])title = "All builds" %][% INCLUDE makeLinkuri = c.uri_for(c.controller('Job').action_for('jobstatus'), [project.name, jobset.name, job.name])title = "Job status" %][% END %][% END %][% WRAPPER makeSubMenu title="Admin" collapsed=0 %][% IF c.user_exists %][% INCLUDE makeLinkuri = c.uri_for(c.controller('Root').action_for('logout'))title = "Logout" %][% ELSE %][% INCLUDE makeLinkuri = c.uri_for(c.controller('Root').action_for('login'))title = "Login" %][% END %][% INCLUDE makeLinkuri = c.uri_for(c.controller('Project').action_for('create'))title = "Create project" %][% END %]</ul><script type="text/javascript">$(document).ready(function() {$('.submenuToggleExpanded').toggle(function () { $(".submenu", $(this).parents(".submenu")).slideUp(); },function () { $(".submenu", $(this).parents(".submenu")).slideDown(); });$('.submenuToggleCollapsed').toggle(function () { $(".submenu", $(this).parents(".submenu")).slideDown(); },function () { $(".submenu", $(this).parents(".submenu")).slideUp(); });});</script>
[% WRAPPER layout.tt title="Overview" %][% PROCESS common.tt %]<h1>Hydra Overview</h1><p>Welcome to Hydra, the <a href="http://nixos.org/">Nix-based</a>continuous build system. Hydra continuously builds, tests andreleases software packages from source repositories. <ahref="http://nixos.org/hydra"><em>[Read more...]</em></a></p><h2>Projects</h2><p>The following projects are hosted on this server:</p><table class="tablesorter"><thead><tr><th>Id</th><th>Name</th><th>Description</th></tr></thead><tbody>[% FOREACH p IN projects %]<tr class="clickable [% IF odd %] odd [% END; odd = !odd %]"onclick="window.location = '[% c.uri_for('/project' p.name) %]'"><td>[% INCLUDE renderProjectName project = p.name %]</td><td>[% HTML.escape(p.displayname) %]</td><td>[% WRAPPER maybeLink uri=p.homepage %][% HTML.escape(p.description) %][% END %]</td></tr>[% END %]</tbody></table><h2>Channels</h2><p>This server provides the following global Nix channels:</p><ul><li><a href="[% c.uri_for('channel' 'latest') %]"><tt>latest</tt></a> —contains the latest successful build of every job.</li><li><a href="[% c.uri_for('channel' 'all') %]"><tt>all</tt></a> —contains every successful, non-garbage-collected build of every job.</li></ul><h2>Statistics</h2>[% INCLUDE showBuildStats %][% END %]
[% BLOCK renderSelection %][% IF edit %]<select [% HTML.attributes(id => param, name => param) %]>[% FOREACH name IN options.keys.sort %]<option [% HTML.attributes(value => name) %] [% IF name == curValue; "selected='selected'"; END %]>[% options.$name %]</option>[% END %]</select>[% ELSE %][% options.$curValue %][% END %][% END %][% BLOCK maybeEditString;IF edit -%]<input type="text" class="string [% extraClass %]" [% HTML.attributes(id => param, name => param, value => value) %] />[% ELSE;HTML.escape(value);END -%][% END -%][% BLOCK renderInputAlt %][% IF edit %]<button type="button" onclick='$(this).parents(".inputalt").remove()'><img src="/static/images/failure.gif" alt="Delete value" /></button>[% INCLUDE maybeEditString param=param value=alt.value %]<br />[% ELSE %][% INCLUDE maybeEditString param=param value=alt.value %][% END %][% END %][% BLOCK renderInput %]<tr class="input [% extraClass %]" [% IF id %]id="[% id %]"[% END %]><td>[% IF edit %]<button type="button" onclick='$(this).parents(".input").remove()'><img src="/static/images/failure.gif" alt="Delete input" /></button>[% END -%]<tt>[% INCLUDE maybeEditString param="$baseName-name" value=input.name extraClass="shortString" %]</tt></td><td>[% INCLUDE renderSelection curValue=input.type param="$baseName-type" options=inputTypes %]</td><td class="inputalts" id="[% baseName %]">[% FOREACH alt IN input.jobsetinputalts -%]<tt class="inputalt">[% IF input.type == "string" && !edit %]"[% HTML.escape(alt.value) %]"[% ELSE %][% INCLUDE renderInputAlt alt=alt param="$baseName-values" %][% END %]</tt>[% END %][% IF edit %]<button type="button" onclick='return false' class="add-inputalt">+</button>[% END %]</td></tr>[% END %][% BLOCK renderJobset %]<div class="jobset[% IF edit %] jobset-edit[% END %]" id="[% "jobset-$baseName" %]"><input type="hidden" [% HTML.attributes(name => "jobset-$baseName-oldName", value => jobset.name) %] /><h3>[% IF edit %]<button type="button" onclick='$(this).parents(".jobset").remove()'><img src="/static/images/failure.gif" alt="Delete value" /></button>[% END %][% IF jobset %]Jobset <tt>[% jobset.name %]</tt>[% ELSE %]New jobset[% END %]</h3><h4>Information</h4><table class="layoutTable">[% IF edit %]<tr><th>Identifier:</th><td>[% INCLUDE maybeEditString param="jobset-$baseName-name" value=jobset.name %]</td></tr>[% END %]<tr><th>Description:</th><td>[% INCLUDE maybeEditString param="jobset-$baseName-description" value=jobset.description %]</td></tr><tr><th>Nix expression:</th><td><tt>[% INCLUDE maybeEditString param="jobset-$baseName-nixexprpath" value=jobset.nixexprpath extraClass="shortString" %]</tt> in input<tt>[% INCLUDE maybeEditString param="jobset-$baseName-nixexprinput" value=jobset.nixexprinput extraClass="shortString" %]</tt></td></tr>[% IF !edit %]<tr><th>Last checked:</th><td>[% IF jobset.lastcheckedtime %][% INCLUDE renderDateTime timestamp = jobset.lastcheckedtime -%][% IF jobset.errormsg -%]<em>, evaluation error</em>:<pre class="multiLineMsg error">[% HTML.escape(jobset.errormsg) %]</pre>[% ELSE %], <em>no errors</em>[% END %][% ELSE %]<em>never</em>[% END %]</td></tr>[% END %]</table><h4>Inputs</h4><table class="tablesorter"><thead><tr><th>Input name</th><th>Type</th><th>Values</th></tr></thead><tbody class="inputs">[% FOREACH input IN jobset.jobsetinputs -%][% INCLUDE renderInput input=input baseName="jobset-$baseName-input-$input.name" %][% END %][% IF edit %]<tr><td colspan="3"><button type="button" class="add-input">Add a new input</button></td></tr>[% END %]</tbody></table></div>[% END %]
<h2>Jobsets</h2>
<table class="tablesorter"><thead><tr><th>Id</th><th>Description</th><th>Last evaluated</th></tr></thead><tbody>[% FOREACH j IN project.jobsets %]<tr class="clickable [% IF odd %] odd [% END; odd = !odd %]"onclick="window.location = '[% c.uri_for('/jobset' project.name j.name) %]'"><td>[% INCLUDE renderJobsetName project = project.name jobset = j.name %]</td><td>[% HTML.escape(j.description) %]</td><td>[% INCLUDE renderDateTime timestamp = j.lastcheckedtime %]</td></tr>[% END %]</tbody></table>
<table class="template"> <!-- dummy wrapper needed because “hidden” trs are visible anyway -->[% INCLUDE renderInput input="" extraClass="template" id="input-template" baseName="input-template" %]</table><tt class="inputalt template" id="inputalt-template">[% INCLUDE renderInputAlt alt=alt %]</tt>
[% END %]
<script type="text/javascript">$(document).ready(function() {var id = 0;$("#add-jobset").click(function() {var newid = "jobset-" + id++;var x = $("#jobset-template").clone(true).attr("id", newid).insertAfter($("#jobset-template")).slideDown("fast");$("#jobset-template", x).attr("id", newid);$("#jobset-template-name", x).attr("name", newid + "-name");$("#jobset-template-description", x).attr("name", newid + "-description");$("#jobset-template-nixexprpath", x).attr("name", newid + "-nixexprpath");$("#jobset-template-nixexprinput", x).attr("name", newid + "-nixexprinput");return false;});
<a href="[% c.uri_for(c.controller('Project').action_for('create_jobset'), [project.name]) %]">[Create a new jobset]</a>
$(".add-input").click(function() {var jobset = $(this).parents(".jobset");var inputid = jobset.attr("id");var newid = inputid + "-input-" + id++;var x = $("#input-template").clone(true).attr("id", "").insertBefore($(this).parents("tr")).show();$("#input-template-name", x).attr("name", newid + "-name");$("#input-template-type", x).attr("name", newid + "-type");$("#input-template", x).attr("id", newid);return false;});
$(".add-inputalt").click(function() {var x = $("#inputalt-template").clone(true).insertBefore($(this)).attr("id", "").show();$("input", x).attr("name", x.parents(".inputalts").attr("id") + "-values");});});</script>[% END %]
[% n = 0; FOREACH jobset IN project.jobsets -%][% INCLUDE renderJobset jobset=jobset baseName="e$n"; n = n + 1 %][% END -%]
<h2>Releases</h2>[% IF releaseSets.size > 0 %]<p>Project <tt>[% project.name %]</tt> has the following release sets:</p><ul>[% FOREACH releaseSet IN releaseSets %]<li><a href="[% c.uri_for('/releases' project.name releaseSet.name) %]"><tt>[% releaseSet.name %]</tt></a>[<a href="[% c.uri_for('/releases' project.name releaseSet.name "edit") %]">Edit</a>]</li>[% END %]</ul>
<hr />
<p>This project provides the following Nix channels:</p><ul><li><a href="[% c.uri_for('/project' project.name 'channel' 'latest') %]"><tt>latest</tt></a> —contains the latest successful build of every job in this project.</li><li><a href="[% c.uri_for('/project' project.name 'channel' 'all') %]"><tt>all</tt></a> —contains every successful, non-garbage-collected build of everyjob in this project.</li></ul><h2>Statistics</h2>[% INCLUDE showBuildStats %][% END %][% IF edit %]
[% IF job.build %]<a href="[% c.uri_for('/build' job.build.id) %]">[% END %][% INCLUDE renderReleaseJobName job=job.job %][% IF job.build %]</a>[% END %]
[% IF j.build %]<a href="[% c.uri_for('/build' job.build.id) %]">[% END %][% INCLUDE renderReleaseJobName job=j.job %][% IF j.build %]</a>[% END %]
[% FOREACH job IN jobs %]<th class="releaseSetJobName">[% INCLUDE renderReleaseJobName %]</th>
[% FOREACH j IN jobs %]<th class="releaseSetJobName">[% INCLUDE renderReleaseJobName job=j %]</th>
[% IF job.build %]<a href="[% c.uri_for('/build' job.build.id) %]">[% IF job.build.get_column('buildstatus') == 0 %]
[% IF j.build %]<a href="[% c.uri_for('/build' j.build.id) %]">[% IF j.build.get_column('buildstatus') == 0 %]
}div.jobset {border: solid black 1px;-moz-border-radius: 1em;border-radius: 1em;padding-left: 1em;padding-right: 1em;margin-bottom: 1em;}div.jobset-edit {background-color: #f8f8f8;}div.jobset-edit h3, div.jobset h3 {margin-top: 0.5em;