Move most of AddBuilds to hydra-eval-jobset
[?]
Feb 21, 2017, 3:17 PM
VU2OLHD246DFWL2WPFSKWPLXTN3WRL25TSRU4PJMJBNWDF35OFRACDependencies
- [2]
MSDSOL5LFix same system inputs - [3]
ZTQ5WWHDPass project and jobset to fetchInput - [4]
E3IEH57URevert "Make evaluation fail with proper error when a input of type build is not available." - [5]
GREPCKGIDie tabs die - [6]
5KWIPCUKUTF-8 fix - [7]
O2BEFBIObuildInputToString: Use inputType attribute instead of type attribute - [8]
GXKMNJ26Pass a build's drv path as a store path - [9]
BGDZAEU7Remove unneeded camelcase - [10]
KLFSX2R6hydra-evaluator: Fix input change check - [11]
5TZGO7RJStore unset descriptions etc. as nulls - [12]
53IMJNBBAdd isChannel column and meta attribute. - [13]
I6QMKSISMove getBaseUrl - [14]
DKIA7GADBetter fix for dots in jobset names - [15]
NY77R3JJBump evaluation timeout to 6 hours - [16]
MPFSVI5XPedantry: CLOCK_REALTIME -> CLOCK_MONOTONIC - [17]
JYZEGF56Create Builds with iscurrent set - [18]
FM4O2L4Mhydra: if evaluator sees cached build, also add the buildproducts - [19]
HE3GX5IPOptimize fetch-git. - [20]
AFTXA575* $HYDRA_DATA environment variable. - [21]
NFVN7JRBHandle missing "build" inputs - [22]
INNOEHO6* Fix getBuildLog for bzip2'd files. - [23]
PHNLYPKBCall buildFinished when a cached build is added - [24]
FTPCV25MStore aggregate members in the database - [25]
KQS7DSKJ* Clean up indentation. - [26]
A43SLRSHFix handling of IPC::Run::run exit status - [27]
5GPK54IVLog segfaults from the evaluator - [28]
DDGBLKENUpdate isCurrent properly - [29]
CQTN62OHDie tabs die - [30]
X5EOJLNAAllow passing a specific build as an input - [31]
CPMIKBDTAllow dots in job specifier of input type 'Previous build' - [32]
5SHCWE7X* Prevent repeated evaluation of a jobset with the same inputs. This - [33]
R4MHON3Opass svn/bzr revisions as integers - [34]
FYWE74AA - [35]
RWIBJ5L4* Autoflush stdout. - [36]
4FWDVNWAPass additional attributes for Git inputs - [37]
PGSSRA7CAdd an input type "nix" for passing arbitrary Nix expressions - [38]
JK2QWPH6 - [39]
XDDCO6CH* hydra: add dependency list with links to logfiles - [40]
PIMGMGAFRename hydra_eval_jobs to hydra-eval-jobs - [41]
3XTHEUMP* Implemented the clone feature. - [42]
OOQ2D3KC* Refactoring: move fetchInput out of hydra_scheduler into a separate - [43]
LBNVQXUB* Build the /build stuff in a separate controller. - [44]
X27GNHDV* Basic job info in the database. - [45]
QW7GTTXIDon't maintain BuildInputs anymore - [46]
PXUCXYZI* Pass `-j 1' to hydra_eval_jobs to ensure that it can make progress - [47]
SMCOU72Fhydra: add some admin for adding/enabling/etc build machines - [48]
N22GPKYT* Put info about logs / build products in the DB. - [49]
AKRVETP5Handle UTF-8 characters in eval error messages - [50]
Z52T2BC4Support passing a jobset evaluation as an input - [51]
NP4OLR5TDon't pass an undefined input - [52]
YFPZ46YK* hydra: added variant of build input type, 'build output (same system)' to allow better continous integration in one jobset for multiple system. it makes sure that the system of the build that is passed as input for a job has the same system as the job. - [53]
2WRTOU2ZCleanup - [54]
SM5M2J3APass inputs to release expressions using -I - [55]
CHQEG6WYHydra/29: Added timeout to svn/git checkouts, job evaluation - [56]
FAIJDQKZ - [57]
6GJSWZ4FCopy-paste error - [58]
JZE7DC2FWhitespace - [59]
JTRG7RDQadd support for git as jobinput - [60]
SGNXIOI4Hydra/32: Add option to force evaluation of a certain jobset via web interface (for admins only) - [61]
BKOIYITRadded some json responses - [62]
RXVJFQ5AEvaluator cleanups - [63]
SKQXOQ7Thydra: fixed missing argument to restartbuild function - [64]
LZVO64YGMerge in the first bits of the API work - [65]
JCJJKRWQHandle job aliases in AggregateConstituents - [66]
24BMQDZAStart of single-process hydra-queue-runner - [67]
ZWCTAZGLadded newsitems, added some admin options to clear various caches. - [68]
E7M2WP7ARemove unused Jobs columns - [69]
MRJCQ4EODon't use given/when - [70]
JAH3UPWASupport revision control systems via plugins - [71]
5SMQ2PLKFix tests - [72]
OSVLMLCQhydra: factored out build restart and - [73]
QT4FO2HPrefactored admin controller, using chains, to avoid using requireadmin on each endpoint - [74]
IMQRX4MPhydra-eval-jobs: Use JSON instead of XML - [75]
4OXJRURPbuildInputToString: Pass along the input's type and urr - [76]
D3YETHSBOnly pass the drv path if it is still valid - [77]
DVLDHOUZhydra-evaluator: Reduce verbosity - [78]
P5XCKTFDFix sysbuild input type handling - [79]
PMNWRTGJAdd multiple output support - [80]
ZTQEU5QSHydra: Add support for maxSilent meta attribute (also already added timeout, but not implemented the actual timeout for the build yet) - [81]
MOX7XJ2EMerge the BuildSchedulingInfo table into the Builds table - [82]
H7CNGK4O* Log evaluation errors etc. in the DB. - [83]
KHYZVPBRPropagate checkresponsible from JobsetInput to BuildInput - [84]
WVD3YYONhydra-evaluator: Add some debug code - [85]
QS4OX6Z7Huuuge speedup in the Hydra evaluator - [86]
RFE6T5LG* Store jobset evaluations in the database explicitly. This includes - [87]
POPU2ATH* hydra_scheduler: use eval-jobs. - [88]
A22P7HCOhydra: at evaluation, check if path is already built, and mark as built in stead of adding to the queue. - [89]
SHYRGAWZhydra: when no external url is given for diff's of git/hg input, revert to a diff done on a local available clone - [90]
TJK27WSBOpen the DB using Hydra::Model::DB->new - [91]
HX4QYOYAadd first evaluations tests - [92]
IEXUBVNBallow dots (.) in job names when used as build input - [93]
Y6FFX4YBMake evaluation fail with proper error when a input of type build is not available. - [94]
ARD6Z67TDo incremental SVN checkouts - [95]
7DWCXNC7Use the new Nix Perl bindings - [96]
VHV6GI4LAdd a jobset eval action to restart all aborted/cancelled builds - [97]
6WRGCITDEnable declarative projects. - [98]
BMSQD2ZHIndentation - [99]
JOYONH2KPrevent multiple builds with the same (job, outPath) tuple from being added - [100]
K3HODXGHCheck all inputs for blame but only email selected inputs - [101]
WWUOQ7V4* hydra: indentation and fixed duplicate key in cachedgitinputs bug - [102]
H3S3H752Pass along drvPath and outputName for inputs that are previous builds. - [103]
Q24QXGSM* Don't do pretty printing for large logs, because the XSLT processing - [104]
5NO7NCKT* Refactoring. - [105]
ZDENAYQI* email notification of evaluation errors to project owner (if desired) - [106]
FXW2UR7Finitial bzr support - [107]
QMW24O5SAdd support for Guile & Guix. - [108]
JOVVHIJXRemove all entry points to modify machines - [109]
RX5IIZMTUse Email::MIME instead of Email::Simple - [110]
HPEG2RHVMerge the BuildResultInfo table into the Builds table - [111]
TPNHTE5VRemove obsolete Builds columns and provide accurate "Running builds" - [112]
EDRUQ4UKDie TABs die - [113]
SS4TZXNUDistinguish between permanent evaluation errors and transient input errors - [114]
DDMYFZ5XFix the jobset unchanged check - [115]
NS7SND6Rhydra-evaluator: Send statistics to statsd - [*]
2GK5DOU7* Downloading closures.
Change contents
- edit in src/lib/Hydra/Controller/API.pm at line 8
use Hydra::Helper::AddBuilds; - edit in src/lib/Hydra/Controller/Admin.pm at line 8
use Hydra::Helper::AddBuilds; - edit in src/lib/Hydra/Controller/Build.pm at line 9
use Hydra::Helper::AddBuilds; - edit in src/lib/Hydra/Helper/AddBuilds.pm at line 7
use IPC::Run; - replacement in src/lib/Hydra/Helper/AddBuilds.pm at line 22
fetchInput evalJobs checkBuild inputsToArgsrestartBuild getPrevJobsetEval updateDeclarativeJobsetupdateDeclarativeJobset - edit in src/lib/Hydra/Helper/AddBuilds.pm at line 25[17.3175]→[17.0:1](∅→∅),[17.1]→[17.3175:3176](∅→∅),[17.3175]→[17.3175:3176](∅→∅),[17.3]→[17.3177:3502](∅→∅),[17.3177]→[17.3177:3502](∅→∅),[17.3502]→[14.0:82](∅→∅),[17.68]→[17.3568:4413](∅→∅),[17.70]→[17.3568:4413](∅→∅),[14.82]→[17.3568:4413](∅→∅),[17.3568]→[17.3568:4413](∅→∅),[17.3727]→[17.262:265](∅→∅),[17.8701]→[17.262:265](∅→∅),[17.265]→[17.3728:3729](∅→∅),[17.3729]→[17.265:287](∅→∅),[17.265]→[17.265:287](∅→∅),[17.287]→[17.165:218](∅→∅),[17.218]→[17.8707:8708](∅→∅),[17.347]→[17.8707:8708](∅→∅),[17.8707]→[17.8707:8708](∅→∅),[17.8708]→[17.110:129](∅→∅),[17.129]→[17.8900:8901](∅→∅),[17.3879]→[17.8900:8901](∅→∅),[17.8900]→[17.8900:8901](∅→∅),[17.8901]→[17.130:783](∅→∅),[17.783]→[17.9287:9288](∅→∅),[17.4242]→[17.9287:9288](∅→∅),[17.9287]→[17.9287:9288](∅→∅),[17.9288]→[4.0:87](∅→∅),[4.87]→[17.9475:9476](∅→∅),[17.87]→[17.9475:9476](∅→∅),[17.126]→[17.9475:9476](∅→∅),[17.4414]→[17.9475:9476](∅→∅),[17.9475]→[17.9475:9476](∅→∅),[17.9476]→[17.4415:4492](∅→∅),[17.4492]→[17.9557:9558](∅→∅),[17.9557]→[17.9557:9558](∅→∅),[17.9558]→[17.4493:4593](∅→∅),[17.4593]→[17.9666:9667](∅→∅),[17.9666]→[17.9666:9667](∅→∅),[17.9667]→[17.4477:4545](∅→∅),[17.4545]→[17.4674:4741](∅→∅),[17.4674]→[17.4674:4741](∅→∅),[17.4741]→[17.0:49](∅→∅),[17.49]→[17.222:223](∅→∅),[17.4741]→[17.222:223](∅→∅),[17.223]→[17.0:17](∅→∅),[17.17]→[17.50:91](∅→∅),[17.234]→[17.50:91](∅→∅),[17.91]→[17.4823:4884](∅→∅),[17.262]→[17.4823:4884](∅→∅),[17.4807]→[17.4823:4884](∅→∅),[17.4823]→[17.4823:4884](∅→∅),[17.4884]→[17.92:134](∅→∅),[17.175]→[17.4884:4895](∅→∅),[17.4884]→[17.4884:4895](∅→∅),[17.4895]→[9.0:94](∅→∅),[17.50]→[17.111:138](∅→∅),[9.94]→[17.111:138](∅→∅),[17.111]→[17.111:138](∅→∅),[17.138]→[17.134:136](∅→∅),[17.4895]→[17.134:136](∅→∅),[17.10008]→[17.134:136](∅→∅),[17.136]→[17.4896:4897](∅→∅),[17.4897]→[17.136:165](∅→∅),[17.136]→[17.136:165](∅→∅),[17.165]→[17.263:316](∅→∅),[17.316]→[17.225:226](∅→∅),[17.225]→[17.225:226](∅→∅),[17.226]→[17.4898:5047](∅→∅),[17.5047]→[17.387:388](∅→∅),[17.387]→[17.387:388](∅→∅),[17.388]→[17.5048:5182](∅→∅),[17.5182]→[17.528:529](∅→∅),[17.528]→[17.528:529](∅→∅),[17.529]→[17.5183:5249](∅→∅),[17.5249]→[2.0:80](∅→∅),[2.80]→[17.5317:5323](∅→∅),[17.4889]→[17.5317:5323](∅→∅),[17.5317]→[17.5317:5323](∅→∅)
sub parseJobName {# Parse a job specification of the form `<project>:<jobset>:<job># [attrs]'. The project, jobset and attrs may be omitted. The# attrs have the form `name = "value"'.my ($s) = @_;our $key;our %attrs = ();# hm, maybe I should stop programming Perl before it's too late...$s =~ / ^ (?: (?: ($projectNameRE) : )? ($jobsetNameRE) : )? ($jobNameRE) \s*(\[ \s* (([\w]+) (?{ $key = $^N; }) \s* = \s* \"([\w\-]+) (?{ $attrs{$key} = $^N; }) \"\s* )* \])? $/xor die "invalid job specifier `$s'";return ($1, $2, $3, \%attrs);}sub attrsToSQL {my ($attrs, $id) = @_;my $query = "1 = 1";foreach my $name (keys %{$attrs}) {my $value = $attrs->{$name};$name =~ /^[\w\-]+$/ or die;$value =~ /^[\w\-]+$/ or die;# !!! Yes, this is horribly injection-prone... (though# name/value are filtered above). Should use SQL::Abstract,# but it can't deal with subqueries. At least we should use# placeholders.$query .= " and exists (select 1 from buildinputs where build = $id and name = '$name' and value = '$value')";}return $query;}sub fetchInputBuild {my ($db, $project, $jobset, $name, $value) = @_;my $prevBuild;if ($value =~ /^\d+$/) {$prevBuild = $db->resultset('Builds')->find({ id => int($value) });} else {my ($projectName, $jobsetName, $jobName, $attrs) = parseJobName($value);$projectName ||= $project->name;$jobsetName ||= $jobset->name;# Pick the most recent successful build of the specified job.$prevBuild = $db->resultset('Builds')->search({ finished => 1, project => $projectName, jobset => $jobsetName, job => $jobName, buildStatus => 0 },{ order_by => "me.id DESC", rows => 1, where => \ attrsToSQL($attrs, "me.id") })->single;}return () if !defined $prevBuild || !isValidPath(getMainOutput($prevBuild)->path);#print STDERR "input `", $name, "': using build ", $prevBuild->id, "\n";my $pkgNameRE = "(?:(?:[A-Za-z0-9]|(?:-[^0-9]))+)";my $versionRE = "(?:[A-Za-z0-9\.\-]+)";my $relName = ($prevBuild->releasename or $prevBuild->nixname);my $version = $2 if $relName =~ /^($pkgNameRE)-($versionRE)$/;my $mainOutput = getMainOutput($prevBuild);my $result ={ storePath => $mainOutput->path, id => $prevBuild->id, version => $version, outputName => $mainOutput->name};if (isValidPath($prevBuild->drvpath)) {$result->{drvPath} = $prevBuild->drvpath;}return $result;}sub fetchInputSystemBuild {my ($db, $project, $jobset, $name, $value) = @_;my ($projectName, $jobsetName, $jobName, $attrs) = parseJobName($value);$projectName ||= $project->name;$jobsetName ||= $jobset->name;my @latestBuilds = $db->resultset('LatestSucceededForJob')->search({}, {bind => [$projectName, $jobsetName, $jobName]});my @validBuilds = ();foreach my $build (@latestBuilds) {push(@validBuilds, $build) if isValidPath(getMainOutput($build)->path);} - edit in src/lib/Hydra/Helper/AddBuilds.pm at line 26[17.236]→[17.5332:5444](∅→∅),[17.5332]→[17.5332:5444](∅→∅),[17.5444]→[17.674:693](∅→∅),[17.693]→[17.5466:5472](∅→∅),[17.5466]→[17.5466:5472](∅→∅)
if (scalar(@validBuilds) == 0) {print STDERR "input `", $name, "': no previous build available\n";return ();} - edit in src/lib/Hydra/Helper/AddBuilds.pm at line 27[17.238]→[17.5477:5498](∅→∅),[17.5477]→[17.5477:5498](∅→∅),[17.5498]→[17.239:240](∅→∅),[17.240]→[17.5503:5654](∅→∅),[17.5503]→[17.5503:5654](∅→∅),[17.5654]→[17.241:242](∅→∅),[17.242]→[17.4546:4618](∅→∅),[17.714]→[17.4546:4618](∅→∅),[17.4618]→[17.5739:5810](∅→∅),[17.5739]→[17.5739:5810](∅→∅),[17.5810]→[17.243:244](∅→∅),[17.244]→[17.5827:5847](∅→∅),[17.5827]→[17.5827:5847](∅→∅),[17.5847]→[17.4890:4949](∅→∅),[17.364]→[17.5927:6091](∅→∅),[17.4949]→[17.5927:6091](∅→∅),[17.5927]→[17.5927:6091](∅→∅),[17.6091]→[17.245:266](∅→∅),[17.266]→[17.6137:6139](∅→∅),[17.6137]→[17.6137:6139](∅→∅),[17.6139]→[17.923:1014](∅→∅),[17.1014]→[17.0:1](∅→∅),[17.3441]→[17.0:1](∅→∅),[17.7419]→[17.0:1](∅→∅),[17.1]→[17.1015:2932](∅→∅),[17.2932]→[17.2204:2221](∅→∅),[17.2204]→[17.2204:2221](∅→∅),[17.2221]→[17.111:200](∅→∅),[17.89]→[17.0:16](∅→∅),[17.200]→[17.0:16](∅→∅),[17.764]→[17.0:16](∅→∅),[17.2281]→[17.0:16](∅→∅),[17.16]→[17.2281:2282](∅→∅),[17.622]→[17.2281:2282](∅→∅),[17.2281]→[17.2281:2282](∅→∅),[17.2282]→[17.765:867](∅→∅),[17.333]→[17.1515:1555](∅→∅),[17.867]→[17.1515:1555](∅→∅),[17.919]→[17.1515:1555](∅→∅),[17.8814]→[17.1515:1555](∅→∅),[17.2591]→[17.1515:1555](∅→∅),[17.1555]→[17.868:948](∅→∅),[17.948]→[17.2933:3042](∅→∅),[17.418]→[17.2591:2597](∅→∅),[17.948]→[17.2591:2597](∅→∅),[17.999]→[17.2591:2597](∅→∅),[17.1632]→[17.2591:2597](∅→∅),[17.3042]→[17.2591:2597](∅→∅),[17.8899]→[17.2591:2597](∅→∅),[17.2591]→[17.2591:2597](∅→∅),[17.2597]→[17.36:86](∅→∅),[17.86]→[17.10051:10086](∅→∅),[17.10051]→[17.10051:10086](∅→∅),[17.10086]→[17.949:988](∅→∅),[17.988]→[17.312:318](∅→∅),[17.781]→[17.312:318](∅→∅),[17.318]→[17.10146:10257](∅→∅),[17.2714]→[17.10146:10257](∅→∅),[17.10146]→[17.10146:10257](∅→∅),[17.10257]→[17.989:1028](∅→∅),[17.1028]→[17.319:325](∅→∅),[17.825]→[17.319:325](∅→∅),[17.325]→[17.10317:10328](∅→∅),[17.2725]→[17.10317:10328](∅→∅),[17.10317]→[17.10317:10328](∅→∅),[17.10328]→[17.1029:1095](∅→∅),[17.1095]→[3.0:84](∅→∅),[3.84]→[17.1160:1341](∅→∅),[17.1160]→[17.1160:1341](∅→∅),[17.1341]→[17.10391:10397](∅→∅),[17.2968]→[17.10391:10397](∅→∅),[17.10391]→[17.10391:10397](∅→∅),[17.10397]→[17.1377:1378](∅→∅),[17.1378]→[17.90:156](∅→∅),[17.156]→[17.201:257](∅→∅),[17.257]→[17.212:218](∅→∅),[17.212]→[17.212:218](∅→∅),[17.218]→[17.910:931](∅→∅),[17.1382]→[17.910:931](∅→∅),[17.910]→[17.910:931](∅→∅),[17.931]→[17.633:740](∅→∅),[17.740]→[17.2832:2973](∅→∅),[17.2973]→[17.863:876](∅→∅),[17.863]→[17.863:876](∅→∅),[17.876]→[17.2974:3000](∅→∅),[17.3000]→[17.899:925](∅→∅),[17.899]→[17.899:925](∅→∅),[17.925]→[17.1356:1358](∅→∅),[17.931]→[17.1356:1358](∅→∅),[17.1425]→[17.1356:1358](∅→∅),[17.10397]→[17.1356:1358](∅→∅),[17.1358]→[17.1383:1384](∅→∅),[17.1384]→[17.1358:1359](∅→∅),[17.1358]→[17.1358:1359](∅→∅),[17.1359]→[17.926:1032](∅→∅),[17.1032]→[17.3001:3570](∅→∅),[17.3570]→[17.1552:1565](∅→∅),[17.1552]→[17.1552:1565](∅→∅),[17.1565]→[17.3571:3652](∅→∅),[17.3652]→[7.0:57](∅→∅),[7.57]→[17.52:133](∅→∅),[17.52]→[17.52:133](∅→∅),[17.133]→[17.1385:1474](∅→∅),[17.3652]→[17.1385:1474](∅→∅),[17.1474]→[17.3652:4114](∅→∅),[17.3652]→[17.3652:4114](∅→∅),[17.4114]→[17.176:278](∅→∅),[17.278]→[8.0:108](∅→∅),[8.108]→[17.4114:4132](∅→∅),[17.367]→[17.4114:4132](∅→∅),[17.4114]→[17.4114:4132](∅→∅),[17.4132]→[17.2077:2105](∅→∅),[17.2077]→[17.2077:2105](∅→∅),[17.2105]→[17.4950:4951](∅→∅),[17.2105]→[17.1359:1379](∅→∅),[17.4951]→[17.1359:1379](∅→∅),[17.1359]→[17.1359:1379](∅→∅),[17.1379]→[17.2106:2143](∅→∅),[17.2143]→[17.1405:1424](∅→∅),[17.1405]→[17.1405:1424](∅→∅),[17.1424]→[10.0:50](∅→∅),[10.50]→[17.220:293](∅→∅),[17.1469]→[17.220:293](∅→∅),[17.293]→[17.361:412](∅→∅),[17.412]→[17.345:411](∅→∅),[17.345]→[17.345:411](∅→∅),[17.411]→[17.1469:1521](∅→∅),[17.1469]→[17.1469:1521](∅→∅),[17.1521]→[5.0:269](∅→∅),[5.269]→[17.213:257](∅→∅),[17.213]→[17.213:257](∅→∅),[17.257]→[5.270:452](∅→∅),[5.452]→[17.404:449](∅→∅),[17.404]→[17.404:449](∅→∅),[17.449]→[5.453:988](∅→∅),[17.851]→[17.2259:2295](∅→∅),[5.988]→[17.2259:2295](∅→∅),[17.2259]→[17.2259:2295](∅→∅),[17.2296]→[17.19:20](∅→∅),[17.450]→[17.450:451](∅→∅),[17.1379]→[17.2474:2489](∅→∅),[17.2474]→[17.2474:2489](∅→∅),[17.2489]→[17.2322:2392](∅→∅),[17.2392]→[17.2548:2609](∅→∅),[17.2548]→[17.2548:2609](∅→∅),[17.2609]→[14.83:155](∅→∅),[14.155]→[17.3249:3346](∅→∅),[17.3249]→[17.3249:3346](∅→∅),[17.3346]→[17.2779:2913](∅→∅),[17.2779]→[17.2779:2913](∅→∅),[17.2913]→[17.413:414](∅→∅),[17.414]→[17.2393:2483](∅→∅),[17.2528]→[17.2528:2529](∅→∅),[17.2529]→[17.0:379](∅→∅),[17.379]→[15.0:76](∅→∅),[15.76]→[17.2016:2107](∅→∅),[17.455]→[17.2016:2107](∅→∅),[17.3465]→[17.2016:2107](∅→∅),[17.2107]→[6.13:86](∅→∅),[6.86]→[17.2163:2180](∅→∅),[17.2163]→[17.2163:2180](∅→∅),[17.178]→[17.3181:3211](∅→∅),[17.2180]→[17.3181:3211](∅→∅),[17.3549]→[17.3181:3211](∅→∅),[17.3181]→[17.3181:3211](∅→∅),[17.3211]→[17.2181:2233](∅→∅),[17.1513]→[17.90:94](∅→∅),[17.94]→[17.0:137](∅→∅),[17.137]→[17.216:240](∅→∅),[17.216]→[17.216:240](∅→∅),[17.240]→[17.138:224](∅→∅),[17.224]→[17.423:478](∅→∅),[17.478]→[17.280:327](∅→∅),[17.280]→[17.280:327](∅→∅),[17.327]→[17.372:394](∅→∅),[17.372]→[17.372:394](∅→∅),[17.394]→[17.2316:2319](∅→∅),[17.1513]→[17.2316:2319](∅→∅),[17.2316]→[17.2316:2319](∅→∅),[17.2319]→[17.25:26](∅→∅),[17.26]→[17.3432:3507](∅→∅),[17.2319]→[17.3432:3507](∅→∅),[17.3432]→[17.3432:3507](∅→∅),[17.3507]→[17.1083:1198](∅→∅),[17.248]→[17.7099:7100](∅→∅),[17.1198]→[17.7099:7100](∅→∅),[17.186]→[17.7099:7100](∅→∅),[17.7100]→[17.2234:2292](∅→∅),[17.2292]→[17.7157:7193](∅→∅),[17.7157]→[17.7157:7193](∅→∅),[17.186]→[17.3600:3601](∅→∅),[17.494]→[17.3600:3601](∅→∅),[17.7193]→[17.3600:3601](∅→∅),[17.3600]→[17.3600:3601](∅→∅),[17.3601]→[17.7194:7402](∅→∅),[17.7402]→[17.2293:2362](∅→∅),[17.2362]→[17.7478:7575](∅→∅),[17.7478]→[17.7478:7575](∅→∅),[17.7575]→[17.3724:3725](∅→∅),[17.3724]→[17.3724:3725](∅→∅),[17.3860]→[17.3860:3898](∅→∅),[17.3898]→[17.1199:1272](∅→∅),[17.1272]→[17.4090:4091](∅→∅),[17.4090]→[17.4090:4091](∅→∅),[17.4186]→[17.4186:4677](∅→∅),[17.4677]→[17.7576:7803](∅→∅),[17.7803]→[17.495:528](∅→∅),[17.4892]→[17.495:528](∅→∅),[17.528]→[17.7804:7933](∅→∅),[17.7933]→[17.0:57](∅→∅),[17.528]→[17.0:57](∅→∅),[17.57]→[17.4133:4387](∅→∅),[17.4387]→[17.1273:1368](∅→∅),[17.1368]→[17.8023:8172](∅→∅),[17.8023]→[17.8023:8172](∅→∅),[17.391]→[17.663:701](∅→∅),[17.4550]→[17.663:701](∅→∅),[17.8172]→[17.663:701](∅→∅),[17.663]→[17.663:701](∅→∅),[17.701]→[17.0:93](∅→∅),[17.93]→[17.0:124](∅→∅),[17.793]→[17.0:124](∅→∅),[17.124]→[17.842:880](∅→∅),[17.1450]→[17.842:880](∅→∅),[17.842]→[17.842:880](∅→∅),[17.880]→[17.5165:5175](∅→∅),[17.5165]→[17.5165:5175](∅→∅),[17.5175]→[17.187:279](∅→∅),[17.279]→[17.1451:1523](∅→∅),[17.1523]→[17.345:374](∅→∅),[17.8247]→[17.345:374](∅→∅),[17.345]→[17.345:374](∅→∅),[17.374]→[17.94:168](∅→∅),[17.168]→[17.447:477](∅→∅),[17.447]→[17.447:477](∅→∅),[17.477]→[17.578:579](∅→∅),[17.579]→[17.411:438](∅→∅),[17.411]→[17.411:438](∅→∅),[17.438]→[17.580:581](∅→∅),[17.581]→[11.0:98](∅→∅),[11.98]→[17.9004:9045](∅→∅),[17.690]→[17.9004:9045](∅→∅),[17.690]→[17.5211:5250](∅→∅),[17.1383]→[17.5211:5250](∅→∅),[17.9045]→[17.5211:5250](∅→∅),[17.5211]→[17.5211:5250](∅→∅),[17.5250]→[17.691:724](∅→∅),[17.724]→[11.99:329](∅→∅),[11.329]→[17.261:359](∅→∅),[17.5581]→[17.261:359](∅→∅),[17.359]→[17.5581:5662](∅→∅),[17.5581]→[17.5581:5662](∅→∅),[17.5696]→[17.5696:5741](∅→∅),[17.5770]→[17.5770:5872](∅→∅),[17.5872]→[17.2419:2478](∅→∅),[17.2478]→[17.19661:19689](∅→∅),[17.4638]→[17.0:29](∅→∅),[17.29]→[12.80:131](∅→∅),[12.131]→[17.5872:5888](∅→∅),[17.1504]→[17.5872:5888](∅→∅),[17.4664]→[17.5872:5888](∅→∅),[17.5872]→[17.5872:5888](∅→∅),[17.5888]→[17.9046:9047](∅→∅),[17.9047]→[17.2479:2569](∅→∅),[17.2569]→[17.9144:9178](∅→∅),[17.9144]→[17.9144:9178](∅→∅),[17.9178]→[17.5888:5889](∅→∅),[17.5888]→[17.5888:5889](∅→∅),[17.5889]→[17.125:233](∅→∅),[17.233]→[17.1593:1667](∅→∅),[17.1593]→[17.1593:1667](∅→∅),[17.1667]→[17.725:726](∅→∅),[17.9255]→[17.725:726](∅→∅),[17.546]→[17.725:726](∅→∅),[17.726]→[17.19690:19799](∅→∅),[17.19799]→[17.6965:6996](∅→∅),[17.6965]→[17.6965:6996](∅→∅),[17.3068]→[17.459:460](∅→∅),[17.9374]→[17.459:460](∅→∅),[17.459]→[17.459:460](∅→∅),[17.9432]→[17.588:589](∅→∅),[17.588]→[17.588:589](∅→∅)
my @inputs = ();foreach my $prevBuild (@validBuilds) {my $pkgNameRE = "(?:(?:[A-Za-z0-9]|(?:-[^0-9]))+)";my $versionRE = "(?:[A-Za-z0-9\.\-]+)";my $relName = ($prevBuild->releasename or $prevBuild->nixname);my $version = $2 if $relName =~ /^($pkgNameRE)-($versionRE)$/;my $input ={ storePath => getMainOutput($prevBuild)->path, id => $prevBuild->id, version => $version, system => $prevBuild->system};push(@inputs, $input);}return @inputs;}sub fetchInputEval {my ($db, $project, $jobset, $name, $value) = @_;my $eval;if ($value =~ /^\d+$/) {$eval = $db->resultset('JobsetEvals')->find({ id => int($value) });die "evaluation $eval->{id} does not exist\n" unless defined $eval;} elsif ($value =~ /^($projectNameRE):($jobsetNameRE)$/) {my $jobset = $db->resultset('Jobsets')->find({ project => $1, name => $2 });die "jobset ‘$value’ does not exist\n" unless defined $jobset;$eval = getLatestFinishedEval($jobset);die "jobset ‘$value’ does not have a finished evaluation\n" unless defined $eval;} elsif ($value =~ /^($projectNameRE):($jobsetNameRE):($jobNameRE)$/) {$eval = $db->resultset('JobsetEvals')->find({ project => $1, jobset => $2, hasnewbuilds => 1 },{ order_by => "id DESC", rows => 1, where =>\ [ # All builds in this jobset should be finished..."not exists (select 1 from JobsetEvalMembers m join Builds b on m.build = b.id where m.eval = me.id and b.finished = 0) "# ...and the specified build must have succeeded.. "and exists (select 1 from JobsetEvalMembers m join Builds b on m.build = b.id where m.eval = me.id and b.job = ? and b.buildstatus = 0)", [ 'name', $3 ] ]});die "there is no successful build of ‘$value’ in a finished evaluation\n" unless defined $eval;} else {die;}my $jobs = {};foreach my $build ($eval->builds) {next unless $build->finished == 1 && $build->buildstatus == 0;# FIXME: Handle multiple outputs.my $out = $build->buildoutputs->find({ name => "out" });next unless defined $out;# FIXME: Should we fail if the path is not valid?next unless isValidPath($out->path);$jobs->{$build->get_column('job')} = $out->path;}return { jobs => $jobs };}sub fetchInput {my ($plugins, $db, $project, $jobset, $name, $type, $value, $emailresponsible) = @_;my @inputs;if ($type eq "build") {@inputs = fetchInputBuild($db, $project, $jobset, $name, $value);}elsif ($type eq "sysbuild") {@inputs = fetchInputSystemBuild($db, $project, $jobset, $name, $value);}elsif ($type eq "eval") {@inputs = fetchInputEval($db, $project, $jobset, $name, $value);}elsif ($type eq "string" || $type eq "nix") {die unless defined $value;@inputs = { value => $value };}elsif ($type eq "boolean") {die unless defined $value && ($value eq "true" || $value eq "false");@inputs = { value => $value };}else {my $found = 0;foreach my $plugin (@{$plugins}) {@inputs = $plugin->fetchInput($type, $name, $value, $project, $jobset);if (defined $inputs[0]) {$found = 1;last;}}die "input `$name' has unknown type `$type'." unless $found;}foreach my $input (@inputs) {$input->{type} = $type;$input->{emailresponsible} = $emailresponsible;}return @inputs;}sub booleanToString {my ($exprType, $value) = @_;my $result;if ($exprType eq "guile") {if ($value eq "true") {$result = "#t";} else {$result = "#f";}$result = $value;} else {$result = $value;}return $result;}sub buildInputToString {my ($exprType, $input) = @_;my $result;if ($exprType eq "guile") {$result = "'((file-name . \"" . ${input}->{storePath} . "\")" .(defined $input->{revision} ? "(revision . \"" . $input->{revision} . "\")" : "") .(defined $input->{revCount} ? "(revision-count . " . $input->{revCount} . ")" : "") .(defined $input->{gitTag} ? "(git-tag . \"" . $input->{gitTag} . "\")" : "") .(defined $input->{shortRev} ? "(short-revision . \"" . $input->{shortRev} . "\")" : "") .(defined $input->{version} ? "(version . \"" . $input->{version} . "\")" : "") .")";} else {$result = "{ outPath = builtins.storePath " . $input->{storePath} . "" ."; inputType = \"" . $input->{type} . "\"" .(defined $input->{uri} ? "; uri = \"" . $input->{uri} . "\"" : "") .(defined $input->{revNumber} ? "; rev = " . $input->{revNumber} . "" : "") .(defined $input->{revision} ? "; rev = \"" . $input->{revision} . "\"" : "") .(defined $input->{revCount} ? "; revCount = " . $input->{revCount} . "" : "") .(defined $input->{gitTag} ? "; gitTag = \"" . $input->{gitTag} . "\"" : "") .(defined $input->{shortRev} ? "; shortRev = \"" . $input->{shortRev} . "\"" : "") .(defined $input->{version} ? "; version = \"" . $input->{version} . "\"" : "") .(defined $input->{outputName} ? "; outputName = \"" . $input->{outputName} . "\"" : "") .(defined $input->{drvPath} ? "; drvPath = builtins.storePath " . $input->{drvPath} . "" : "") .";}";}return $result;}sub inputsToArgs {my ($inputInfo, $exprType) = @_;my @res = ();foreach my $input (sort keys %{$inputInfo}) {push @res, "-I", "$input=$inputInfo->{$input}->[0]->{storePath}"if scalar @{$inputInfo->{$input}} == 1&& defined $inputInfo->{$input}->[0]->{storePath};foreach my $alt (@{$inputInfo->{$input}}) {if ($alt->{type} eq "string") {push @res, "--argstr", $input, $alt->{value};}elsif ($alt->{type} eq "boolean") {push @res, "--arg", $input, booleanToString($exprType, $alt->{value});}elsif ($alt->{type} eq "nix") {die "input type ‘nix’ only supported for Nix-based jobsets\n" unless $exprType eq "nix";push @res, "--arg", $input, $alt->{value};}elsif ($alt->{type} eq "eval") {die "input type ‘eval’ only supported for Nix-based jobsets\n" unless $exprType eq "nix";my $s = "{ ";# FIXME: escape $_. But dots should not be escaped.$s .= "$_ = builtins.storePath ${\$alt->{jobs}->{$_}}; "foreach keys %{$alt->{jobs}};$s .= "}";push @res, "--arg", $input, $s;}else {push @res, "--arg", $input, buildInputToString($exprType, $alt);}}}return @res;}sub evalJobs {my ($inputInfo, $exprType, $nixExprInputName, $nixExprPath) = @_;my $nixExprInput = $inputInfo->{$nixExprInputName}->[0]or die "cannot find the input containing the job expression\n";die "multiple alternatives for the input containing the Nix expression are not supported.\n"if scalar @{$inputInfo->{$nixExprInputName}} != 1;my $nixExprFullPath = $nixExprInput->{storePath} . "/" . $nixExprPath;my $evaluator = ($exprType eq "guile") ? "hydra-eval-guile-jobs" : "hydra-eval-jobs";my @cmd = ($evaluator, $nixExprFullPath, "--gc-roots-dir", getGCRootsDir, "-j", 1, inputsToArgs($inputInfo, $exprType));if (defined $ENV{'HYDRA_DEBUG'}) {sub escape {my $s = $_;$s =~ s/'/'\\''/g;return "'" . $s . "'";}my @escaped = map escape, @cmd;print STDERR "evaluator: @escaped\n";}(my $res, my $jobsJSON, my $stderr) = captureStdoutStderr(21600, @cmd);die "$evaluator returned " . ($res & 127 ? "signal $res" : "exit code " . ($res >> 8)). ":\n" . ($stderr ? decode("utf-8", $stderr) : "(no output)\n")if $res;print STDERR "$stderr";return (decode_json($jobsJSON), $nixExprInput);}# Return the most recent evaluation of the given jobset (that# optionally had new builds), or undefined if no such evaluation# exists.sub getPrevJobsetEval {my ($db, $jobset, $hasNewBuilds) = @_;my ($prevEval) = $jobset->jobsetevals(($hasNewBuilds ? { hasnewbuilds => 1 } : { }),{ order_by => "id DESC", rows => 1 });return $prevEval;}# Check whether to add the build described by $buildInfo.sub checkBuild {my ($db, $jobset, $inputInfo, $nixExprInput, $buildInfo, $buildMap, $prevEval, $jobOutPathMap, $plugins) = @_;my @outputNames = sort keys %{$buildInfo->{outputs}};die unless scalar @outputNames;# In various checks we can use an arbitrary output (the first)# rather than all outputs, since if one output is the same, the# others will be as well.my $firstOutputName = $outputNames[0];my $firstOutputPath = $buildInfo->{outputs}->{$firstOutputName};my $jobName = $buildInfo->{jobName} or die;my $drvPath = $buildInfo->{drvPath} or die;my $build;txn_do($db, sub {my $job = $jobset->jobs->update_or_create({ name => $jobName });# Don't add a build that has already been scheduled for this# job, or has been built but is still a "current" build for# this job. Note that this means that if the sources of a job# are changed from A to B and then reverted to A, three builds# will be performed (though the last one will probably use the# cached result from the first). This ensures that the builds# with the highest ID will always be the ones that we want in# the channels. FIXME: Checking the output paths doesn't take# meta-attributes into account. For instance, do we want a# new build to be scheduled if the meta.maintainers field is# changed?if (defined $prevEval) {# Only check one output: if it's the same, the other will be as well.my $firstOutput = $outputNames[0];my ($prevBuild) = $prevEval->builds->search(# The "project" and "jobset" constraints are# semantically unnecessary (because they're implied by# the eval), but they give a factor 1000 speedup on# the Nixpkgs jobset with PostgreSQL.{ project => $jobset->project->name, jobset => $jobset->name, job => $jobName,name => $firstOutputName, path => $firstOutputPath },{ rows => 1, columns => ['id'], join => ['buildoutputs'] });if (defined $prevBuild) {#print STDERR " already scheduled/built as build ", $prevBuild->id, "\n";$buildMap->{$prevBuild->id} = { id => $prevBuild->id, jobName => $jobName, new => 0, drvPath => $drvPath };return;}}# Prevent multiple builds with the same (job, outPath) from# being added.my $prev = $$jobOutPathMap{$jobName . "\t" . $firstOutputPath};if (defined $prev) {#print STDERR " already scheduled as build ", $prev, "\n";return;}my $time = time();sub null {my ($s) = @_;return $s eq "" ? undef : $s;}# Add the build to the database.$build = $job->builds->create({ timestamp => $time, description => null($buildInfo->{description}), license => null($buildInfo->{license}), homepage => null($buildInfo->{homepage}), maintainers => null($buildInfo->{maintainers}), maxsilent => $buildInfo->{maxSilent}, timeout => $buildInfo->{timeout}, nixname => $buildInfo->{nixName}, drvpath => $drvPath, system => $buildInfo->{system}, nixexprinput => $jobset->nixexprinput, nixexprpath => $jobset->nixexprpath, priority => $buildInfo->{schedulingPriority}, finished => 0, iscurrent => 1, ischannel => $buildInfo->{isChannel}});$build->buildoutputs->create({ name => $_, path => $buildInfo->{outputs}->{$_} })foreach @outputNames;$buildMap->{$build->id} = { id => $build->id, jobName => $jobName, new => 1, drvPath => $drvPath };$$jobOutPathMap{$jobName . "\t" . $firstOutputPath} = $build->id;print STDERR "added build ${\$build->id} (${\$jobset->project->name}:${\$jobset->name}:$jobName)\n";});return $build;}; - edit in src/lib/Hydra/Helper/Nix.pm at line 13
use IPC::Run; - replacement in src/script/hydra-eval-jobset at line 5[17.151]→[17.473:492](∅→∅),[17.1363]→[17.473:492](∅→∅),[17.492]→[17.19854:19873](∅→∅),[17.19873]→[17.1746:1770](∅→∅),[17.492]→[17.1746:1770](∅→∅)
use Hydra::Schema;use Hydra::Plugin;use Hydra::Helper::Nix;use Config::General;use Data::Dump qw(dump);use Digest::SHA qw(sha256_hex);use Encode;use File::Slurp; - edit in src/script/hydra-eval-jobset at line 11
use Hydra::Helper::CatalystUtils; - edit in src/script/hydra-eval-jobset at line 13
use Hydra::Helper::Nix; - replacement in src/script/hydra-eval-jobset at line 15[17.710]→[17.2013:2045](∅→∅),[17.10946]→[17.2013:2045](∅→∅),[17.396]→[17.396:417](∅→∅),[17.417]→[17.2426:2451](∅→∅),[17.2451]→[17.2457:2472](∅→∅)
use Digest::SHA qw(sha256_hex);use Config::General;use Data::Dump qw(dump);use Try::Tiny;use Hydra::Plugin;use Hydra::Schema;use JSON; - edit in src/script/hydra-eval-jobset at line 19
use Nix::Store; - replacement in src/script/hydra-eval-jobset at line 21
use JSON;use File::Slurp;use Try::Tiny; - edit in src/script/hydra-eval-jobset at line 33
sub parseJobName {# Parse a job specification of the form `<project>:<jobset>:<job># [attrs]'. The project, jobset and attrs may be omitted. The# attrs have the form `name = "value"'.my ($s) = @_;our $key;our %attrs = ();# hm, maybe I should stop programming Perl before it's too late...$s =~ / ^ (?: (?: ($projectNameRE) : )? ($jobsetNameRE) : )? ($jobNameRE) \s*(\[ \s* (([\w]+) (?{ $key = $^N; }) \s* = \s* \"([\w\-]+) (?{ $attrs{$key} = $^N; }) \"\s* )* \])? $/xor die "invalid job specifier `$s'";return ($1, $2, $3, \%attrs);}sub attrsToSQL {my ($attrs, $id) = @_;my $query = "1 = 1";foreach my $name (keys %{$attrs}) {my $value = $attrs->{$name};$name =~ /^[\w\-]+$/ or die;$value =~ /^[\w\-]+$/ or die;# !!! Yes, this is horribly injection-prone... (though# name/value are filtered above). Should use SQL::Abstract,# but it can't deal with subqueries. At least we should use# placeholders.$query .= " and exists (select 1 from buildinputs where build = $id and name = '$name' and value = '$value')";}return $query;}sub fetchInputBuild {my ($db, $project, $jobset, $name, $value) = @_;my $prevBuild;if ($value =~ /^\d+$/) {$prevBuild = $db->resultset('Builds')->find({ id => int($value) });} else {my ($projectName, $jobsetName, $jobName, $attrs) = parseJobName($value);$projectName ||= $project->name;$jobsetName ||= $jobset->name;# Pick the most recent successful build of the specified job.$prevBuild = $db->resultset('Builds')->search({ finished => 1, project => $projectName, jobset => $jobsetName, job => $jobName, buildStatus => 0 },{ order_by => "me.id DESC", rows => 1, where => \ attrsToSQL($attrs, "me.id") })->single;}return () if !defined $prevBuild || !isValidPath(getMainOutput($prevBuild)->path);#print STDERR "input `", $name, "': using build ", $prevBuild->id, "\n";my $pkgNameRE = "(?:(?:[A-Za-z0-9]|(?:-[^0-9]))+)";my $versionRE = "(?:[A-Za-z0-9\.\-]+)";my $relName = ($prevBuild->releasename or $prevBuild->nixname);my $version = $2 if $relName =~ /^($pkgNameRE)-($versionRE)$/;my $mainOutput = getMainOutput($prevBuild);my $result ={ storePath => $mainOutput->path, id => $prevBuild->id, version => $version, outputName => $mainOutput->name};if (isValidPath($prevBuild->drvpath)) {$result->{drvPath} = $prevBuild->drvpath;}return $result;}sub fetchInputSystemBuild {my ($db, $project, $jobset, $name, $value) = @_;my ($projectName, $jobsetName, $jobName, $attrs) = parseJobName($value);$projectName ||= $project->name;$jobsetName ||= $jobset->name;my @latestBuilds = $db->resultset('LatestSucceededForJob')->search({}, {bind => [$projectName, $jobsetName, $jobName]});my @validBuilds = ();foreach my $build (@latestBuilds) {push(@validBuilds, $build) if isValidPath(getMainOutput($build)->path);}if (scalar(@validBuilds) == 0) {print STDERR "input `", $name, "': no previous build available\n";return ();}my @inputs = ();foreach my $prevBuild (@validBuilds) {my $pkgNameRE = "(?:(?:[A-Za-z0-9]|(?:-[^0-9]))+)";my $versionRE = "(?:[A-Za-z0-9\.\-]+)";my $relName = ($prevBuild->releasename or $prevBuild->nixname);my $version = $2 if $relName =~ /^($pkgNameRE)-($versionRE)$/;my $input ={ storePath => getMainOutput($prevBuild)->path, id => $prevBuild->id, version => $version, system => $prevBuild->system};push(@inputs, $input);}return @inputs;}sub fetchInputEval {my ($db, $project, $jobset, $name, $value) = @_;my $eval;if ($value =~ /^\d+$/) {$eval = $db->resultset('JobsetEvals')->find({ id => int($value) });die "evaluation $eval->{id} does not exist\n" unless defined $eval;} elsif ($value =~ /^($projectNameRE):($jobsetNameRE)$/) {my $jobset = $db->resultset('Jobsets')->find({ project => $1, name => $2 });die "jobset ‘$value’ does not exist\n" unless defined $jobset;$eval = getLatestFinishedEval($jobset);die "jobset ‘$value’ does not have a finished evaluation\n" unless defined $eval;} elsif ($value =~ /^($projectNameRE):($jobsetNameRE):($jobNameRE)$/) {$eval = $db->resultset('JobsetEvals')->find({ project => $1, jobset => $2, hasnewbuilds => 1 },{ order_by => "id DESC", rows => 1, where =>\ [ # All builds in this jobset should be finished..."not exists (select 1 from JobsetEvalMembers m join Builds b on m.build = b.id where m.eval = me.id and b.finished = 0) "# ...and the specified build must have succeeded.. "and exists (select 1 from JobsetEvalMembers m join Builds b on m.build = b.id where m.eval = me.id and b.job = ? and b.buildstatus = 0)", [ 'name', $3 ] ]});die "there is no successful build of ‘$value’ in a finished evaluation\n" unless defined $eval;} else {die;}my $jobs = {};foreach my $build ($eval->builds) {next unless $build->finished == 1 && $build->buildstatus == 0;# FIXME: Handle multiple outputs.my $out = $build->buildoutputs->find({ name => "out" });next unless defined $out;# FIXME: Should we fail if the path is not valid?next unless isValidPath($out->path);$jobs->{$build->get_column('job')} = $out->path;}return { jobs => $jobs };}sub fetchInput {my ($plugins, $db, $project, $jobset, $name, $type, $value, $emailresponsible) = @_;my @inputs;if ($type eq "build") {@inputs = fetchInputBuild($db, $project, $jobset, $name, $value);}elsif ($type eq "sysbuild") {@inputs = fetchInputSystemBuild($db, $project, $jobset, $name, $value);}elsif ($type eq "eval") {@inputs = fetchInputEval($db, $project, $jobset, $name, $value);}elsif ($type eq "string" || $type eq "nix") {die unless defined $value;@inputs = { value => $value };}elsif ($type eq "boolean") {die unless defined $value && ($value eq "true" || $value eq "false");@inputs = { value => $value };}else {my $found = 0;foreach my $plugin (@{$plugins}) {@inputs = $plugin->fetchInput($type, $name, $value, $project, $jobset);if (defined $inputs[0]) {$found = 1;last;}}die "input `$name' has unknown type `$type'." unless $found;}foreach my $input (@inputs) {$input->{type} = $type;$input->{emailresponsible} = $emailresponsible;}return @inputs;}sub booleanToString {my ($exprType, $value) = @_;my $result;if ($exprType eq "guile") {if ($value eq "true") {$result = "#t";} else {$result = "#f";}$result = $value;} else {$result = $value;}return $result;}sub buildInputToString {my ($exprType, $input) = @_;my $result;if ($exprType eq "guile") {$result = "'((file-name . \"" . ${input}->{storePath} . "\")" .(defined $input->{revision} ? "(revision . \"" . $input->{revision} . "\")" : "") .(defined $input->{revCount} ? "(revision-count . " . $input->{revCount} . ")" : "") .(defined $input->{gitTag} ? "(git-tag . \"" . $input->{gitTag} . "\")" : "") .(defined $input->{shortRev} ? "(short-revision . \"" . $input->{shortRev} . "\")" : "") .(defined $input->{version} ? "(version . \"" . $input->{version} . "\")" : "") .")";} else {$result = "{ outPath = builtins.storePath " . $input->{storePath} . "" ."; inputType = \"" . $input->{type} . "\"" .(defined $input->{uri} ? "; uri = \"" . $input->{uri} . "\"" : "") .(defined $input->{revNumber} ? "; rev = " . $input->{revNumber} . "" : "") .(defined $input->{revision} ? "; rev = \"" . $input->{revision} . "\"" : "") .(defined $input->{revCount} ? "; revCount = " . $input->{revCount} . "" : "") .(defined $input->{gitTag} ? "; gitTag = \"" . $input->{gitTag} . "\"" : "") .(defined $input->{shortRev} ? "; shortRev = \"" . $input->{shortRev} . "\"" : "") .(defined $input->{version} ? "; version = \"" . $input->{version} . "\"" : "") .(defined $input->{outputName} ? "; outputName = \"" . $input->{outputName} . "\"" : "") .(defined $input->{drvPath} ? "; drvPath = builtins.storePath " . $input->{drvPath} . "" : "") .";}";}return $result;}sub inputsToArgs {my ($inputInfo, $exprType) = @_;my @res = ();foreach my $input (sort keys %{$inputInfo}) {push @res, "-I", "$input=$inputInfo->{$input}->[0]->{storePath}"if scalar @{$inputInfo->{$input}} == 1&& defined $inputInfo->{$input}->[0]->{storePath};foreach my $alt (@{$inputInfo->{$input}}) {if ($alt->{type} eq "string") {push @res, "--argstr", $input, $alt->{value};}elsif ($alt->{type} eq "boolean") {push @res, "--arg", $input, booleanToString($exprType, $alt->{value});}elsif ($alt->{type} eq "nix") {die "input type ‘nix’ only supported for Nix-based jobsets\n" unless $exprType eq "nix";push @res, "--arg", $input, $alt->{value};}elsif ($alt->{type} eq "eval") {die "input type ‘eval’ only supported for Nix-based jobsets\n" unless $exprType eq "nix";my $s = "{ ";# FIXME: escape $_. But dots should not be escaped.$s .= "$_ = builtins.storePath ${\$alt->{jobs}->{$_}}; "foreach keys %{$alt->{jobs}};$s .= "}";push @res, "--arg", $input, $s;}else {push @res, "--arg", $input, buildInputToString($exprType, $alt);}}}return @res;} - edit in src/script/hydra-eval-jobset at line 332
sub evalJobs {my ($inputInfo, $exprType, $nixExprInputName, $nixExprPath) = @_; - edit in src/script/hydra-eval-jobset at line 335
my $nixExprInput = $inputInfo->{$nixExprInputName}->[0]or die "cannot find the input containing the job expression\n";die "multiple alternatives for the input containing the Nix expression are not supported.\n"if scalar @{$inputInfo->{$nixExprInputName}} != 1;my $nixExprFullPath = $nixExprInput->{storePath} . "/" . $nixExprPath;my $evaluator = ($exprType eq "guile") ? "hydra-eval-guile-jobs" : "hydra-eval-jobs";my @cmd = ($evaluator, $nixExprFullPath, "--gc-roots-dir", getGCRootsDir, "-j", 1, inputsToArgs($inputInfo, $exprType));if (defined $ENV{'HYDRA_DEBUG'}) {sub escape {my $s = $_;$s =~ s/'/'\\''/g;return "'" . $s . "'";}my @escaped = map escape, @cmd;print STDERR "evaluator: @escaped\n";}(my $res, my $jobsJSON, my $stderr) = captureStdoutStderr(21600, @cmd);die "$evaluator returned " . ($res & 127 ? "signal $res" : "exit code " . ($res >> 8)). ":\n" . ($stderr ? decode("utf-8", $stderr) : "(no output)\n")if $res;print STDERR "$stderr";return (decode_json($jobsJSON), $nixExprInput);}# Return the most recent evaluation of the given jobset (that# optionally had new builds), or undefined if no such evaluation# exists.sub getPrevJobsetEval {my ($db, $jobset, $hasNewBuilds) = @_;my ($prevEval) = $jobset->jobsetevals(($hasNewBuilds ? { hasnewbuilds => 1 } : { }),{ order_by => "id DESC", rows => 1 });return $prevEval;}# Check whether to add the build described by $buildInfo.sub checkBuild {my ($db, $jobset, $inputInfo, $nixExprInput, $buildInfo, $buildMap, $prevEval, $jobOutPathMap, $plugins) = @_;my @outputNames = sort keys %{$buildInfo->{outputs}};die unless scalar @outputNames;# In various checks we can use an arbitrary output (the first)# rather than all outputs, since if one output is the same, the# others will be as well.my $firstOutputName = $outputNames[0];my $firstOutputPath = $buildInfo->{outputs}->{$firstOutputName};my $jobName = $buildInfo->{jobName} or die;my $drvPath = $buildInfo->{drvPath} or die;my $build;txn_do($db, sub {my $job = $jobset->jobs->update_or_create({ name => $jobName });# Don't add a build that has already been scheduled for this# job, or has been built but is still a "current" build for# this job. Note that this means that if the sources of a job# are changed from A to B and then reverted to A, three builds# will be performed (though the last one will probably use the# cached result from the first). This ensures that the builds# with the highest ID will always be the ones that we want in# the channels. FIXME: Checking the output paths doesn't take# meta-attributes into account. For instance, do we want a# new build to be scheduled if the meta.maintainers field is# changed?if (defined $prevEval) {# Only check one output: if it's the same, the other will be as well.my $firstOutput = $outputNames[0];my ($prevBuild) = $prevEval->builds->search(# The "project" and "jobset" constraints are# semantically unnecessary (because they're implied by# the eval), but they give a factor 1000 speedup on# the Nixpkgs jobset with PostgreSQL.{ project => $jobset->project->name, jobset => $jobset->name, job => $jobName,name => $firstOutputName, path => $firstOutputPath },{ rows => 1, columns => ['id'], join => ['buildoutputs'] });if (defined $prevBuild) {#print STDERR " already scheduled/built as build ", $prevBuild->id, "\n";$buildMap->{$prevBuild->id} = { id => $prevBuild->id, jobName => $jobName, new => 0, drvPath => $drvPath };return;}}# Prevent multiple builds with the same (job, outPath) from# being added.my $prev = $$jobOutPathMap{$jobName . "\t" . $firstOutputPath};if (defined $prev) {#print STDERR " already scheduled as build ", $prev, "\n";return;}my $time = time();sub null {my ($s) = @_;return $s eq "" ? undef : $s;}# Add the build to the database.$build = $job->builds->create({ timestamp => $time, description => null($buildInfo->{description}), license => null($buildInfo->{license}), homepage => null($buildInfo->{homepage}), maintainers => null($buildInfo->{maintainers}), maxsilent => $buildInfo->{maxSilent}, timeout => $buildInfo->{timeout}, nixname => $buildInfo->{nixName}, drvpath => $drvPath, system => $buildInfo->{system}, nixexprinput => $jobset->nixexprinput, nixexprpath => $jobset->nixexprpath, priority => $buildInfo->{schedulingPriority}, finished => 0, iscurrent => 1, ischannel => $buildInfo->{isChannel}});$build->buildoutputs->create({ name => $_, path => $buildInfo->{outputs}->{$_} })foreach @outputNames;$buildMap->{$build->id} = { id => $build->id, jobName => $jobName, new => 1, drvPath => $drvPath };$$jobOutPathMap{$jobName . "\t" . $firstOutputPath} = $build->id;print STDERR "added build ${\$build->id} (${\$jobset->project->name}:${\$jobset->name}:$jobName)\n";});return $build;}; - edit in tests/Setup.pm at line 7
use Hydra::Helper::AddBuilds; - edit in tests/evaluation-tests.pl at line 4
use Hydra::Helper::AddBuilds;