The catalyst-action-rest branch from shlevy/hydra was an exploration of using Catalyst::Action::REST to create a JSON API for hydra. This commit merges in the best bits from that experiment, with the goal that further API endpoints can be added incrementally.
In addition to migrating more endpoints, there is potential for improvement in what's already been done:
Fixes NixOS/hydra#98
Signed-off-by: Shea Levy <shea@shealevy.com>
LZVO64YG43JD7YMZSCTZNOBS5ROZA4FMPKJW2YOMHX2V5PTGBVWQC BJAJLOT3TD2VHISB7RAF2BIZ74AP6AWB6UTGBUMPHGTIJYNOVADQC P2NOTX5D7PII2PJOCPO265BTNZEEMI2BWA3JK7BP6VYDBSRVWHGAC S4V64WMTM6ZPIRCUASPXKBFTY545PNMZGYNEPQGGNTXA7YJ2ZGPAC NS5GKQHW26OSSNT722GGKKMIQLTS7UKSWG2CUSV5QYBUF3A24I7AC PHJF4FUTRWDFKS4RTYGT2PKASDZAPPA3JY3VF2YANHFI5AUTIZAQC FHHFNCCR2IOCRTCGF4ZLHVKAGL7E2ZN3B75E4JEMCPNB5GN4MYLQC 4HPT4SDDNU24OX2P4FDP2KXKINDCJLZEBN2VIEEKWFEVT4TNWXZAC HZWUT4YNGQE3RSTS7XAZK4UQCUHZ5KHA7CHL2JNSXQIPPB6UGKTQC FAUCW4NHW33VMQIEIZQFUKOEKQDR33HX3ETI2PF7X4LCV2CFV53QC QLOLZHRXOUSNVLJG2SWVC5EISFUOUKJIT32XN6DNW4VFFJMN5PMAC OQNFJWAGTY775SICOZKEUX42DWA4Y2BOMUZTTA5HQO3BIWAQM7ZAC R6B5CAFF3BWJPW6I5YGKXOKKCW6S7D7AHO2HLZVFUW3NL77ZW44QC E7M2WP7AQPTJR3DUI3ONEEG3GW36EA4CM77HTZ4KYVETJ7VA3LEAC 6K5PBUUN4GQAMOVX5BS6YYMHJ3PIF2PPZBTIEQ4R7BQNHC23GS3AC JAH3UPWAVSHXIPNGL6PROQPZBYZHPJNFONWBDZX4HCX646USZXUQC M6UBWKN23RAUWXCHSZOUL3YBBZWBB5II3N56CLGWRVRYVFGN3QVAC 225GEK4NN53NJ6EBDAJNECR76FILAHV7KNEG23TDQ2KYMG7NFEUAC T4LLYESZ2HUXSLKZ6GNBLVWUVG7R5IDFHYHYO773QIZ6QTOOXR2AC 7C6HSXUQ6HFILY35VPZWCJOVBAL7IJ2QXOU2X5DTIFJ6FQ2RHOQAC YQWH4POV22KYCCKOTZXD36QKUOWEQ2DSPYPO5DNZDZ344RI25OAAC 3PNG7NIBQQURUUPRVQXYL342OT7JUUYOMY2JJNP6YDX7SYJDZMYAC XJFHFZCA2VXGYSRDJDLAZZX3MLRH2KDT242DHWRU5XQRJ6XIHZUQC FPK5LF53CFUEKFYJ3IYXT4UTVC6IITWJOCFATMC4PLHEUP5SIEAAC BTUDUY6FY6UTRZOGLSPFNA4MVTSP7JXBD44VYF6BPKNFF6Y3CY2QC AKAZKCR6GFCZQBR2ZJSZEI3SXW4S25V7X7JGHUYNUITQQSAVAF5AC 5NO7NCKTMM5ZW7JYOETUFOSWK2ACTXWDZGJAFXZN6L3OF6BFTNOQC PMNWRTGJ4GVSMSSAWSUD57B26PCRAHMZIQ5SIWJIK7A74ENKEQLAC D5QIOJGPKQJIYBUCSC3MFJ3TXLPNZ2XMI37GXMFRVRFWWR2VMTFAC BKOIYITRBRVU4D7XFAZPV5QHAPCBMIO3SBNHYJ6TVT43WR32CTHQC PCKLFRT5IZVLG47GQQ23GBSROKUR4CUEZW4PRVGREHNDFTCZ7VBAC EVZTBKEFHKIT6LIDRNAYFNELPYS7L2YQBLPQUB3LHLYSP3UM4OCQC LBNVQXUBEZ45SOTGVXK5UEZXIAIZTJLWZNUYFI4JZ6J65N3KPDVQC FU4GO5VN3PKQ3DKOHVHJTMHUKOKMKWS27TIULQ3FMEFXRED5Q44AC 6F4UNDTCAW7LYSKTUUUCX32BLAAGYPUPK2LXMMZ32Y6H3CBWP6TQC NUIKDEHLDR4ANFZFRCWBAWZGFVMHCZ7PSMPPFVQJ3AAAAWAQQD4AC D3DIBMOKXK2E65267BEEWQL4S4NSHGZBCY7YTU34JSEPZ7AKNBRQC H5REHM3MZKP6SZPMSTMO7SHAVS6W756Q6P2AEJ27R4FZB2IZ2HVAC YPDYBK5GXGNHZMJWC2EHO3T4BIMYK5LMP2G3ABGHOKGIWV4ONIYAC 36M6DGITWMQQGZ7VROEHUOY4FP7OHPJ5DXMQMHHISBIAJLQE24YAC XDDCO6CHPWGXFV3RSMMUYY45DTXJXQNYYHUT2PCYAGRODK43MD6AC OD5FSS5AJ3XRTV5Q4UQQF6JMBT6UUL7UUOMPPUBNOM6ZMVIHW35QC A52HEFHQNVNF2OUSWDSUYVVXLYR2UFCGOORPCN27CJJYA4UDJA3AC ZILILXXKP4Z64UGIXIPTQ3KXPV76LFGSXVFLMLCO2HJBCQ5GP6AQC GJFYEU3SVP7TDSYXVZEYGKN4NVWSZX4754PPPTOYPRHUO5RMDWPQC NEWDDAOFCDLYBXQCZNQ2GDH7HPAHVN3YRDL52ZYEMVA4YH6LBDXAC Z6MDQIGODVE7RXX5U5D64EEC2ZDEZ36Z4ZMPFYIHWBG42IXUDYFQC WRIU3S5EO3RB3IM5PUDNHLOOMUPD5UKWNUL4YMAKD3C6O4KELJCAC ND75XNSQ6MWGOAOZGEBMED6QQKLZAU47TEQK3OKRSYOSAUVL6WNAC HSVVEKTY2U5XJ4ZDHF43YEQXRPRWNZLZDDD4W7JFJ55UWD7CEFBQC 3HZY24CX4U2TO74HOY4YX3LBJIYF4DLXHCIY7J2RASAC4COMSMZAC UWVMQIAC2HQNSG2JQOPZGUOCQ5V2JFP2F7RCTF3WJLK7NHSD5PAAC UMFB2767NMAG46NAG3XHMYQMCLF5TMVN46SST4AM2CFUEMNZH3WQC 75XUS62YF7OK4S45RCZ5OOASXEBIEDNDBYEEMOCBDHVXV4GA3NLQC QTC3SYBMNNTPZHQGXRMV5GU5KDODGXPIM3TIGWBAF7HDKNDKTCXQC 6QRHXIM3XHCDLSIIBEGETDV67V6LTV55QMHC64ZPBMLTAECM5N3QC ODNCGFQ5FPKFI624BVMLW7PJ2EFJOR3TY66OCZM42UNNTWBCF2TQC ZRRPBEI4ZSISCMWV62JGB35QNRJVA7SZUR4F52WTQRECCG2GCEAQC LZO3C2KIACZ3HN72RBGWWIT5ED4RJMYKI3SAHXT6RIUPHDFL3STQC OOQ2D3KCLFPYNAN253PHWLBQMB6OMO2KYQWQXLTP65SQAYZWQ5LAC XEKWCIDR3GZECQWYUSMB2UWWP25NBJ34MFZVQVIUGT6KQOW7M6SAC YFPZ46YK4BOI6VH2H3F757UEGEYONURUAEDAYEIBLRY33PLSSO4AC JTRG7RDQXKPSO4ESGDLSVAT5WIFGKDL424MN6YYCVTKCOR2FTXRQC HQGXL4MXGHICQ3IRRQUR6KQHS2RKVOTDKQ5ZETXHQ56DANR2YADQC QMW24O5S43MYF5ZTBULUEZNDW2FKJ3QP2GL46P6B2SVGSQLVEACAC CQTN62OHT4DY35E2MJEG7GFTVNEE5KRDMV6ASBQLBHN7BUDK7WHAC OX6NYJDVCDX2UCJ4QQMTRJVZ535NOMCJGYCHDLCADJPQI7RMCXPQC VS3OUK7DARCUQWR6NC2MIKHBLOVPXDUGITCNYS5VHYYGHS3OQBNQC FYO6NECE4YJC76HQIG35NNJABODV7KNQKREK5YKZU6O2MNNSQTMAC PVIRRARJJC2Z3PYC6XPHJUZPT5IS4IKXUUDGMXXV57PFVVFCUUMAC 6LETVKRAATQS7Z63ED6FD3IMGLYIEDBQ55DAWHTMTGAFLOTC4PMQC RSEGBU6CIEDRNTL7R6Q7RF2IZGCN3HH6VTPGFBKD77LSHEUD6EKAC TNMOG2ZQTXJJ6TEPEDB6D4XPIIGMAV3DUDWO33WD23CGDVT4LO2QC U4TD3AIQXBJFFUORTMIC4IHZTVBORRKL2TZ2FSP4G665ECZOEMNAC VH5ZABDRP565VZIG55YHNYYPST53NQ2J6YM362NSLXCAHI5WPH4AC UXXFND4UYVY6WHWPRL3YIRKWTGFM3PR32JZCYKYNQOX7MZAFEG5AC GNIEG2GCT6BUYHY2WXUAQVKHSYB6TVQT52O5GEY6COLKFK4ODCCQC OR5SJ42Y6HYDJL3UTEXMINLNY2T3LYSVJ6FSYFMHP4K4W2RZAMIQC JARRBLZDQ2JZWY7IUVPTOT7WJMBPMLFLF2MGLVGOYROAAISYGLSAC QTFVCDIFHTF36AGU4UGNCTWSHQYTM2KERXS26TPAJK7IDJHL7BVAC BHJ62LYETTTVB53IGPMYJ4H2BT26AKZBGDI32DIVHBX7ZMFOXILAC A6XVP6FE3CO7BVZR23HQS2GFDNF3FK7KBDSOB4YIPRISZHEWQJKAC 2BUX775ILK47LEDXNPSVUAKVLEKG54ODCXRG3474DHY5PHJJBNBQC KXGOUX7PH4BOXPJRYRGDS4RY7RTXYWUXCRAB5JR3BLVS62PVKOQAC RBNQKATLSAKTGW2IRNB5CRV3SEH5F6E4BPVWX4BII7MH5TCIPINQC RJICSUYGE5RVDXYAPCOTS436V67HB222GG6GAWEATJEQAAKDU5SAC J5UVLXOK6EDIL5I7VKWH4V2QDS4DPD7FHRK6XBWSXFRQS4JKXFZQC ZWCTAZGLJZQNTYWTC2XQUKMILJF6JGDL5IND6QNYWK4FIGMLRFXAC S66BOMVUACAUDSGSDWP7ZIXVMZSQHWXOZYVTB7ILUCWZ7DDFAKVAC JLDUSNUOOQNL63BOPXIWZOWFRQ5X35RWG33PJB3J3KMR6QR7TN7QC XJRJ4J7M6BC433TBLWHHKX7UYYCFX6M7ZQLUEYYTREPCSM6M3RDQC JFZNAYJXKCMXYHGCLTRH7Q6TOFGJ4BT6332GONCWVYRLNMDDG3KAC 6FRLEP4PY7HKDWDD7TWQ7HXILOWRMIKXHJRXXXS65Q5CXMQ5CSMQC EFWN7JBV7YIHNMCA6ZGFRSHZIUQJ2EX57SWYGVT7ZRJCHPIM2R3QC LSZLZHJYGXZTCNH4JUXU7W23MW5PBVM4OBMWRRVNEDROMIBUVQNAC SMM4HQTPAY45254O7GQOJVLHP5LZ6BXDKGNE7IKUJHB5XKH244ZAC MVB7RRLTOLZALMUVUNSCMXDB76QLICJAV2GYQQZO7E6S327LHMSQC P5X4P6VKS5CJOOLJRVL66GRJLDLVC3EKAVAHP2RJOXQJ7WTYAUBQC JM3DPYOMVNMCL5GMEYC3Y4NDRGTNIFBBFTPGPVT66GPENVPU7EVQC PHX2HIVGHHKCAX6VNN2WXD4LRGSA74KQMJCCTMHK7HS6JPELVECAC SJLEZFC472OWVCR7WEUUYNS6BJDDR77SHWADKDCPIS2INMTPTIVAC Y6AHH4THYQA43V77L43YM42DYRPCMDSWLUV4NKWAQYMPL4NTUIPQC 7ZSVXUGFXOI3BOJE37SXIT6MDN6AJRBCCJNEICYDMBJF5QOEY6BAC CLJQCY2XHIDNNMFBJ5PK3GQEN6RFALEFKXBJRWZPEIKR4PR5ZQ5AC RFE6T5LGBFFNEPHZOPF4UNMFC2L4CGD5TPAMOXDLRPH3TZJ43UBAC YTIDBFGUR65L6D6OKGQWTKHJJQK3NCC6WDNQFTA7E6EATVOX4Y5AC L2E6EVE2RVFVDCUNRJ4CZYSQNS2DZUA5DTBETHBDUQUV2KQQRAOQC DHMTPGSTO5WBG7CCOY6GO6J5FDHL6OGXBFENFTQJ7GXPAANTLIDAC SB2V735VJ2CDHGCXRUA5FOYHDRXQFVOZ3KXC3YKXWRNW6DIX7RXQC JJT5QG3KOCWGMOTBN7BJFCUKDCLMB4O6K2PP32KL7WKXKABSZXFAC QQ4STW3SZRZLTYANG5QNDDL7DIPRW3HWKOEMQC6QJHTMXWJDKB7QC ZVTSOVHNQNQCRF3N44RKDQSL3UM7HSLTAXICMWEE6EIA6SWJXZCQC S5PV6IIMKJ7PGWIFLLXERHYF3BCP2UEGFRZEZLD6UUBLVEZXJLUAC G2T4WAHINIIKTEKR4JBIRXO5IEKO7MPYD7U5PQ4AIX3WL54EPP3AC 5SHCWE7XPQORSOLY7HGAIK2ODKBFPY4KVXRL2W7X6D4WALU544HQC KPZNJ33UUF6TK5OPB6K5KLM3ZEK7YV3IF32HTLJFURCO6ICDMGYAC M552HLIAP52D42AVXVC5SGROAYN2TBCEUZOXESWEMBBUX7G3U6TAC X27GNHDV5KPZ5GSH6DCAJMNCEMZLCP7M43JWF2X3O5QWXMOX273AC NABL63FIFXIY2VIPFSK3RE4DLUVBVDJ7H2V6QA7LVY3RRQ3UWVGAC MOX7XJ2E3XISXA7V7T4W6GEAGECGWBZ4PYSLTYBVVR4VAKOI33CQC FD76WVTQR5QM6ZP7GH2R6YSBRI3LFMB54YSGHURE4RAICAKFWGOAC RU7AQO7U4HCWJNQTR2KRGDLLG24WYD47MWIHREV6SIAPCPDQHAWQC FHF6IZJQPUQHY5QWQYRPZVDBRLHREWRHGNKVQDT7F3GQKKLZXJKQC FV2M6MOTAP4BJMEKU5XUDVEACWEJGEIRCCE2MRY3F6SF2SFOE3MQC TP3PFR5KVAORTBLZI6EMZYZSQJI3BUEH3YRVTQJ5EM4M6VCSH5EAC 7Z3YOKCVJE242IDO4HQVOBBLHFOXXCQIBIKDIUXQLTU5LY5QAORQC PZL3SZM3U3BYJX2RGYXC6NMBG7WQHFWHSYDYXZ7Q5VZA3EDYVPIQC OEPUOUNBNTHTFZVDXREGBQCKFRCWMVP2MDVK4OA47VK2DBKEWVYAC QL55ECJ6KMMBUOWQ6LKSOVN7L43CH4S6SPE2AQ3VX3KSGC32RP4AC JZVRK5QJGPDT7HNQLC2UOJRG6V6QO4NJJ5V5QEMQ6VUTUIQWHG5AC 4JS4DWHDRVNPIV3QCHLVJP6ZEAO5FHOK7NQ5FGO2JVZE6BZDZAIAC NRSKJPP4FYTEKWNLVRK2QZDL6P24VJAC6R6NUBVX77JSUEBVRISAC KN3VYE5P2RJB3KZ355LA5C2T2D5S2IR3QZFE53AJIWUVMETEEYDAC G2ZB6464XGPBIMSZIPSB24EIXSCCGV4XWC3IWPS2CXYPDSUZSU5QC tests.api = genAttrs' (system:with import <nixos/lib/testing.nix> { inherit system; };let hydra = builtins.getAttr system build; in # build.${system}simpleTest {machine ={ config, pkgs, ... }:{ services.postgresql.enable = true;services.postgresql.package = pkgs.postgresql92;environment.systemPackages = [ hydra pkgs.perlPackages.LWP pkgs.perlPackages.JSON ];virtualisation.memorySize = 2048;};testScript =''$machine->waitForJob("postgresql");# Initialise the database and the state.$machine->mustSucceed( "createdb -O root hydra", "psql hydra -f ${hydra}/libexec/hydra/sql/hydra-postgresql.sql", "mkdir /var/lib/hydra", "echo \"insert into Users(userName, emailAddress, password) values('root', 'e.dolstra\@tudelft.nl', '\$(echo -n foobar | sha1sum | cut -c1-40)');\" | psql hydra", "echo \"insert into UserRoles(userName, role) values('root', 'admin');\" | psql hydra", "mkdir /run/jobset", "chmod 755 /run/jobset", "cp ${./tests/api-test.nix} /run/jobset/default.nix", "chmod 644 /run/jobset/default.nix");# Start the web interface.$machine->mustSucceed("NIX_STORE_DIR=/run/nix NIX_LOG_DIR=/run/nix/var/log/nix NIX_STATE_DIR=/run/nix/var/nix HYDRA_DATA=/var/lib/hydra HYDRA_DBI='dbi:Pg:dbname=hydra;user=root;' LOGNAME=root DBIC_TRACE=1 hydra-server -d >&2 &");$machine->waitForOpenPort("3000");$machine->mustSucceed("perl ${./tests/api-test.pl} >&2");'';});
package Hydra::Base::Controller::REST;use strict;use warnings;use base 'Catalyst::Controller::REST';__PACKAGE__->config(map => {'text/html' => [ 'View', 'TT' ]},default => 'text/html','stash_key' => 'resource',);sub begin { my ( $self, $c ) = @_; $c->forward('Hydra::Controller::Root::begin'); }sub end { my ( $self, $c ) = @_; $c->forward('Hydra::Controller::Root::end'); }1;
use utf8;package Hydra::Component::ToJSON;use strict;use warnings;use base 'DBIx::Class';sub TO_JSON {my $self = shift;my $json = { $self->get_columns };my $rs = $self->result_source;my @relnames = $rs->relationships;RELLOOP: foreach my $relname (@relnames) {my $relinfo = $rs->relationship_info($relname);next unless defined $relinfo->{attrs}->{accessor};my $accessor = $relinfo->{attrs}->{accessor};if ($accessor eq "single" and exists $self->{_relationship_data}{$relname}) {$json->{$relname} = $self->$relname->TO_JSON;} else {unless (defined $self->{related_resultsets}{$relname}) {my $cond = $relinfo->{cond};if (ref $cond eq 'HASH') {foreach my $k (keys %{$cond}) {my $v = $cond->{$k};$v =~ s/^self\.//;next RELLOOP unless $self->has_column_loaded($v);}} #!!! TODO: Handle ARRAY conditions}if (defined $self->related_resultset($relname)->get_cache) {if ($accessor eq "multi") {$json->{$relname} = [ map { $_->TO_JSON } $self->$relname ];} else {$json->{$relname} = $self->$relname->TO_JSON;}}}}return $json;}1;
$c->stash->{jobset_} = $project->jobsets->search({name => $jobsetName});$c->stash->{jobset} = $c->stash->{jobset_}->singleor notFound($c, "Jobset $jobsetName doesn't exist.");
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;}
$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"}));
sub jobs_tab : Chained('jobset') PathPart('jobs-tab') Args(0) {
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}});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$/;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 => ""});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" });}}}sub jobs_tab : Chained('jobsetChain') PathPart('jobs-tab') Args(0) {
return $c->res->redirect($c->uri_for($c->controller('Project')->action_for("view"), [$c->stash->{project}->name]));
return $c->res->redirect($c->uri_for($c->controller('Project')->action_for("project"), [$c->stash->{project}->name]));
txn_do($c->model('DB')->schema, sub {updateJobset($c, $c->stash->{jobset});});$c->res->redirect($c->uri_for($self->action_for("index"),[$c->stash->{project}->name, $c->stash->{jobset}->name]) . "#tabs-configuration");
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);
}sub checkInput {my ($c, $baseName) = @_;my $inputName = trim $c->request->params->{"input-$baseName-name"};error($c, "Invalid input name: $inputName") unless $inputName =~ /^[[:alpha:]]\w*$/;my $inputType = trim $c->request->params->{"input-$baseName-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" ;return ($inputName, $inputType);
, enabled => defined $c->request->params->{enabled} ? 1 : 0, enableemail => defined $c->request->params->{enableemail} ? 1 : 0, emailoverride => trim($c->request->params->{emailoverride}) || "", hidden => defined $c->request->params->{visible} ? 0 : 1, keepnr => int(trim($c->request->params->{keepnr})) || 3, checkinterval => int(trim($c->request->params->{checkinterval}))
, enabled => defined $c->stash->{params}->{enabled} ? 1 : 0, 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, checkinterval => int(trim($c->stash->{params}->{checkinterval}))
foreach my $param (keys %{$c->request->params}) {next unless $param =~ /^input-(\w+)-name$/;my $baseName = $1;next if $baseName eq "template";
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;}}}
my ($inputName, $inputType) = checkInput($c, $baseName);
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*$/;
$inputNames{$inputName} = 1;
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" ;
if ($baseName =~ /^\d+$/) { # numeric base name is auto-generated, i.e. a new entry$input = $jobset->jobsetinputs->create(
unless (defined $inputData->{oldName}) {$input = $jobset->jobsetinputs->update_or_create(
$c->stash->{evals} = getEvals($self, $c, $evals, ($page - 1) * $resultsPerPage, $resultsPerPage)
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",}) ],first => "?page=1",last => "?page=" . POSIX::ceil($c->stash->{total}/$resultsPerPage));if ($page > 1) {$entity{previous} = "?page=" . ($page - 1);}if ($page < POSIX::ceil($c->stash->{total}/$resultsPerPage)) {$entity{next} = "?page=" . ($page + 1);}$self->status_ok($c,entity => \%entity);
my $project = $c->model('DB::Projects')->find($projectName)or notFound($c, "Project $projectName doesn't exist.");
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} = $project;
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;}}
sub edit : Chained('project') PathPart Args(0) {
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"), [$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" });}} else {$self->status_not_found($c,message => "Project $c->stash->{params}->{oldName} doesn't exist.");}} 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 {# Note: $projectName is validated in updateProject,# which will abort the transaction if the name isn't# valid. Idem for the owner.my $owner = $c->user->username;$project = $c->model('DB::Projects')->create({name => $c->stash->{projectName}, 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" });}}}sub edit : Chained('projectChain') PathPart Args(0) {
txn_do($c->model('DB')->schema, sub {updateProject($c, $c->stash->{project});});$c->res->redirect($c->uri_for($self->action_for("view"), [$c->stash->{project}->name]) . "#tabs-configuration");
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);
requireMayCreateProjects($c);my $projectName = trim $c->request->params->{name};error($c, "Invalid project name: ‘$projectName’") if $projectName !~ /^$projectNameRE$/;
txn_do($c->model('DB')->schema, sub {# Note: $projectName is validated in updateProject,# which will abort the transaction if the name isn't# valid. Idem for the owner.my $owner = $c->check_user_roles('admin')? trim $c->request->params->{owner} : $c->user->username;my $project = $c->model('DB::Projects')->create({name => $projectName, displayname => "", owner => $owner});updateProject($c, $project);});
$c->stash->{projectName} = trim $c->stash->{params}->{name};
my $jobsetName = trim $c->request->params->{name};my $exprType =$c->request->params->{"nixexprpath"} =~ /.scm$/ ? "guile" : "nix";error($c, "Invalid jobset name: ‘$jobsetName’") if $jobsetName !~ /^$jobsetNameRE$/;txn_do($c->model('DB')->schema, 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 => "", emailoverride => ""});Hydra::Controller::Jobset::updateJobset($c, $jobset);});$c->res->redirect($c->uri_for($c->controller('Jobset')->action_for("index"),[$c->stash->{project}->name, $jobsetName]));
Hydra::Controller::Jobset::jobset_PUT($self, $c);
if ($c->check_user_roles('admin')) {$owner = trim $c->request->params->{owner};
if ($c->check_user_roles('admin') and defined $c->stash->{params}->{owner}) {$owner = trim $c->stash->{params}->{owner};
, description => trim($c->request->params->{description}), homepage => trim($c->request->params->{homepage}), enabled => defined $c->request->params->{enabled} ? 1 : 0, hidden => defined $c->request->params->{visible} ? 0 : 1
, description => trim($c->stash->{params}->{description}), homepage => trim($c->stash->{params}->{homepage}), enabled => defined $c->stash->{params}->{enabled} ? 1 : 0, hidden => defined $c->stash->{params}->{visible} ? 0 : 1
$c->stash->{steps} = [ $c->model('DB::BuildSteps')->search({ 'me.busy' => 1, 'build.finished' => 0, 'build.busy' => 1 },{ join => [ 'build' ], order_by => [ 'machine' ]} ) ];
$self->status_ok($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' ]}) ]);
my $username = $c->request->params->{username} || "";my $password = $c->request->params->{password} || "";
my $baseurl = $c->uri_for('/');my $referer = $c->request->referer;$c->session->{referer} = $referer if defined $referer && $referer =~ m/^($baseurl)/;
if ($username eq "" && $password eq "" && !defined $c->session->{referer}) {my $baseurl = $c->uri_for('/');my $referer = $c->request->referer;$c->session->{referer} = $referer if defined $referer && $referer =~ m/^($baseurl)/;}
$c->stash->{template} = 'login.tt';}sub login_POST {my ($self, $c) = @_;my $username;my $password;$username = $c->stash->{params}->{username};$password = $c->stash->{params}->{password};
backToReferer($c) if $c->authenticate({username => $username, password => $password});$c->stash->{errorMsg} = "Bad username or password.";
if ($c->authenticate({username => $username, password => $password})) {if ($c->request->looks_like_browser) {backToReferer($c);} else {currentUser_GET($self, $c);}} else {$self->status_forbidden($c, message => "Bad username or password.");if ($c->request->looks_like_browser) {login_GET($self, $c);}}
sub currentUser :Path('/current-user') :ActionClass('REST') { }sub currentUser_GET {my ($self, $c) = @_;requireLogin($c) if !$c->user_exists;$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}));}
if (($c->request->params->{submit} // "") eq "delete") {
$c->stash->{emailonerror} = $user->emailonerror;}sub edit_POST {my ($self, $c) = @_;my $user = $c->stash->{user};$c->stash->{template} = 'user.tt';$c->session->{referer} = $c->request->referer if !defined $c->session->{referer};if (($c->stash->{params}->{submit} // "") eq "delete") {
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:byU/SLN03zNJlSFbi/3Bcg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:tKZAybbNaRIMs9n5tHkqPw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-30 16:22:11# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:UpVoKdd3OwMvlvyMjcYNVA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:V8MbzKvZNEaeHBJV67+ZMQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:KHwh/Np40jxKXc3ijMImEQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:+0LkZiaRL5tGJvbLxnwD/g
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-30 16:22:11# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:dC1yX7arRVu9K3wG9dAjCg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:A/4v3ugXYbuYoKPlOvC6mg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-30 16:36:03# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ZiA1nv73Fpp0/DTi4sLfEQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:OZsXJniZ/7EB2iSz7p5y4A
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-05-03 14:35:11# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:aYVEk+AeDsgTRi5GAqOhEw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:isCEXACY/PwkvgKHcXvAIg
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ONhBo6Xhq7uwYFdEzbp3dg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:zvun8uhxwrr7B8EsqBoCjA
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:IcSVN/tlfQQtX88Ix+aKnw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Vi1qzjW52Lnsl0JSmGzy0w
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:fx3yosWMmJ+MnvL/dSWtFA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:I4hI02FKRMkw76WV/KBocA
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:xFLnuCBAcJCg+N3b4aajZQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:qS/eiiZXmpc7KpTHdtaT7g
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:4KzXhMnUldVgNuuNXWIYjw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:28rja0vR1glJJ15hzVfjsQ
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1rjwWtZXGEowHqhfjLqjmA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:3qXfnvkOVj25W94bfhQ65w
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-05-23 16:09:46# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:JgxEaCz/TW9YKa+HavRzXw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:t2CCfUjFEz/lO4szROz1AQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ng+Q6tMX5EJMD7DxRWVy7Q
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1Dp8B58leBLh4GK0GPw2zg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:EVwSR9WBqbBdIHq1ANQMHg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ccPNQe/QnSjTAC3uGWe8Ng
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:qElGj6zzuI0xo426np3r1w
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:SlEiF8oN6FBK262uSiMKiw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:M3pNBRLfxgSScrPj1zaajA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:UUO37lIuEYm0GiR92m/fyA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:xjioYUPo6visoLAVDkDZ0Q
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:UXBzqO0vHPql4LYyXpgEQg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-05-02 14:50:55# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:q4amPCWRoWMThnRa/n/y1w
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:tsGR8MhZRIUeNwpcVczMUw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:lnA5Utkwk5WTyKA/M5mlyg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:3CRNsvd+YnZp9c80tuZREQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:zW87n6E7xWaShcFbgFkVuw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:imPoiaitrTbX0vVNlF6dPA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:OCuhmxs8pZxvmk81eVLLcQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:RffghAo9jAaqYk41y1Sdqw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:eP00w5UJp1uTtiB7D5IhTQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:7M7WPlGQT6rNHKJ+82/KSA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:UTUE3Hb89fT7prwnwwBgvQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:qISBiwvboB8dIdinaE45mg
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2012-02-29 00:47:18# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:LFD28W0GvvrOOylCM98SEQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:08/7gbEQp1TqBiWFJXVY0w
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:zg8db3Cbi0QOv+gLJqH8cQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:8cC34cEw9T3+x+7uRs4KHQ
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:hzKzGAgAiCfU0nBOiDnjWw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:G2GAF/Rb7cRkRegH94LwIA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:KArPHyemtnm/siwE4x5mGQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:aS+ivlFpndqIv8U578zz9A
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:OAUFl/teGpfeleb6D8FPlw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:hy3MKvFxfL+1bTc7Hcb1zA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:cbSUw113ENPypbd/sICfgg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:hz912vBfYw0rHslBPqJW2w
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Vyd2+0RAF3XGTpq3KswfAQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:U23GZ3k5KZk2go6j2LYLHA
[% INCLUDE menuItem uri = c.uri_for(c.controller('Project').action_for('view'), [project.name]) title = "Overview" %]
[% INCLUDE menuItem uri = c.uri_for(c.controller('Project').action_for('project'), [project.name]) title = "Overview" %]
perl -MDBIx::Class::Schema::Loader=make_schema_at,dump_to_dir:../lib -e 'make_schema_at("Hydra::Schema", { naming => { ALL => "v5" }, relationships => 1, moniker_map => sub {return "$$_";} }, ["dbi:SQLite:tmp.sqlite"])'
perl -I ../lib -MDBIx::Class::Schema::Loader=make_schema_at,dump_to_dir:../lib -e 'make_schema_at("Hydra::Schema", { naming => { ALL => "v5" }, relationships => 1, moniker_map => sub {return "$$_";}, components => [ "+Hydra::Component::ToJSON" ], }, ["dbi:SQLite:tmp.sqlite"])'
letbuilder = builtins.toFile "builder.sh" ''echo -n ${builtins.readFile ./default.nix} > $out'';in {job = derivation {name = "job";system = builtins.currentSystem;builder = "/bin/sh";args = [ builder ];};}
use LWP::UserAgent;use JSON;use Test::Simple tests => 15;my $ua = LWP::UserAgent->new;$ua->cookie_jar({});sub request_json {my ($opts) = @_;my $req = HTTP::Request->new;$req->method($opts->{method} or "GET");$req->uri("http://localhost:3000$opts->{uri}");$req->header(Accept => "application/json");$req->content(encode_json($opts->{data})) if defined $opts->{data};my $res = $ua->request($req);print $res->as_string();return $res;}my $result = request_json({ uri => "/login", method => "POST", data => { username => "root", password => "foobar" } });my $user = decode_json($result->content());ok($user->{username} eq "root", "The root user is named root");ok($user->{userroles}->[0]->{role} eq "admin", "The root user is an admin");$user = decode_json(request_json({ uri => "/current-user" })->content());ok($user->{username} eq "root", "The current user is named root");ok($user->{userroles}->[0]->{role} eq "admin", "The current user is an admin");ok(request_json({ uri => '/project/sample' })->code() == 404, "Non-existent projects don't exist");$result = request_json({ uri => '/project/sample', method => 'PUT', data => { displayname => "Sample", enabled => "1", } });ok($result->code() == 201, "PUTting a new project creates it");my $project = decode_json(request_json({ uri => '/project/sample' })->content());ok((not @{$project->{jobsets}}), "A new project has no jobsets");$result = request_json({ uri => '/jobset/sample/default', method => 'PUT', data => { nixexprpath => "default.nix", nixexprinput => "src", inputs => { src => { type => "path", values => "/run/jobset" } }, enabled => "1", checkinterval => "3600"} });ok($result->code() == 201, "PUTting a new jobset creates it");my $jobset = decode_json(request_json({ uri => '/jobset/sample/default' })->content());ok($jobset->{jobsetinputs}->[0]->{name} eq "src", "The new jobset has an 'src' input");ok($jobset->{jobsetinputs}->[0]->{jobsetinputalts}->[0]->{value} eq "/run/jobset", "The 'src' input is in /run/jobset");system("LOGNAME=root NIX_STORE_DIR=/run/nix/store NIX_LOG_DIR=/run/nix/var/log/nix NIX_STATE_DIR=/run/nix/var/nix HYDRA_DATA=/var/lib/hydra HYDRA_DBI='dbi:Pg:dbname=hydra;user=root;' hydra-evaluator sample default");$result = request_json({ uri => '/jobset/sample/default/evals' });ok($result->code() == 200, "Can get evals of a jobset");my $evals = decode_json($result->content())->{evals};my $eval = $evals->[0];ok($eval->{hasnewbuilds} == 1, "The first eval of a jobset has new builds");# Ugh, cached for 30ssleep 30;system("echo >> /run/jobset/default.nix; LOGNAME=root NIX_STORE_DIR=/run/nix/store NIX_LOG_DIR=/run/nix/var/log/nix NIX_STATE_DIR=/run/nix/var/nix HYDRA_DATA=/var/lib/hydra HYDRA_DBI='dbi:Pg:dbname=hydra;user=root;' hydra-evaluator sample default");my $evals = decode_json(request_json({ uri => '/jobset/sample/default/evals' })->content())->{evals};ok($evals->[0]->{jobsetevalinputs}->[0]->{revision} != $evals->[1]->{jobsetevalinputs}->[0]->{revision}, "Changing a jobset source changes its revision");my $build = decode_json(request_json({ uri => "/build/" . $evals->[0]->{jobsetevalmembers}->[0]->{build} })->content());ok($build->{job} eq "job", "The build's job name is job");ok($build->{finished} == 0, "The build isn't finished yet");