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]
4FWDVNWAPass additional attributes for Git inputs - [18]
LBNVQXUB* Build the /build stuff in a separate controller. - [19]
PXUCXYZI* Pass `-j 1' to hydra_eval_jobs to ensure that it can make progress - [20]
ARD6Z67TDo incremental SVN checkouts - [21]
JAH3UPWASupport revision control systems via plugins - [22]
TPNHTE5VRemove obsolete Builds columns and provide accurate "Running builds" - [23]
7DWCXNC7Use the new Nix Perl bindings - [24]
IMQRX4MPhydra-eval-jobs: Use JSON instead of XML - [25]
BKOIYITRadded some json responses - [26]
H7CNGK4O* Log evaluation errors etc. in the DB. - [27]
P5XCKTFDFix sysbuild input type handling - [28]
FM4O2L4Mhydra: if evaluator sees cached build, also add the buildproducts - [29]
OOQ2D3KC* Refactoring: move fetchInput out of hydra_scheduler into a separate - [30]
PGSSRA7CAdd an input type "nix" for passing arbitrary Nix expressions - [31]
HPEG2RHVMerge the BuildResultInfo table into the Builds table - [32]
Y6FFX4YBMake evaluation fail with proper error when a input of type build is not available. - [33]
OSVLMLCQhydra: factored out build restart and - [34]
E7M2WP7ARemove unused Jobs columns - [35]
Z52T2BC4Support passing a jobset evaluation as an input - [36]
6GJSWZ4FCopy-paste error - [37]
JYZEGF56Create Builds with iscurrent set - [38]
RWIBJ5L4* Autoflush stdout. - [39]
ZWCTAZGLadded newsitems, added some admin options to clear various caches. - [40]
X27GNHDV* Basic job info in the database. - [41]
LZVO64YGMerge in the first bits of the API work - [42]
SM5M2J3APass inputs to release expressions using -I - [43]
JOVVHIJXRemove all entry points to modify machines - [44]
FAIJDQKZ - [45]
KHYZVPBRPropagate checkresponsible from JobsetInput to BuildInput - [46]
5NO7NCKT* Refactoring. - [47]
RFE6T5LG* Store jobset evaluations in the database explicitly. This includes - [48]
DDGBLKENUpdate isCurrent properly - [49]
NFVN7JRBHandle missing "build" inputs - [50]
IEXUBVNBallow dots (.) in job names when used as build input - [51]
A43SLRSHFix handling of IPC::Run::run exit status - [52]
6WRGCITDEnable declarative projects. - [53]
WWUOQ7V4* hydra: indentation and fixed duplicate key in cachedgitinputs bug - [54]
R4MHON3Opass svn/bzr revisions as integers - [55]
JOYONH2KPrevent multiple builds with the same (job, outPath) tuple from being added - [56]
DVLDHOUZhydra-evaluator: Reduce verbosity - [57]
QMW24O5SAdd support for Guile & Guix. - [58]
RXVJFQ5AEvaluator cleanups - [59]
SHYRGAWZhydra: when no external url is given for diff's of git/hg input, revert to a diff done on a local available clone - [60]
SS4TZXNUDistinguish between permanent evaluation errors and transient input errors - [61]
4OXJRURPbuildInputToString: Pass along the input's type and urr - [62]
JZE7DC2FWhitespace - [63]
FXW2UR7Finitial bzr support - [64]
WVD3YYONhydra-evaluator: Add some debug code - [65]
HE3GX5IPOptimize fetch-git. - [66]
Q24QXGSM* Don't do pretty printing for large logs, because the XSLT processing - [67]
5SHCWE7X* Prevent repeated evaluation of a jobset with the same inputs. This - [68]
PIMGMGAFRename hydra_eval_jobs to hydra-eval-jobs - [69]
MRJCQ4EODon't use given/when - [70]
MOX7XJ2EMerge the BuildSchedulingInfo table into the Builds table - [71]
H3S3H752Pass along drvPath and outputName for inputs that are previous builds. - [72]
INNOEHO6* Fix getBuildLog for bzip2'd files. - [73]
SMCOU72Fhydra: add some admin for adding/enabling/etc build machines - [74]
ZDENAYQI* email notification of evaluation errors to project owner (if desired) - [75]
QT4FO2HPrefactored admin controller, using chains, to avoid using requireadmin on each endpoint - [76]
CHQEG6WYHydra/29: Added timeout to svn/git checkouts, job evaluation - [77]
CPMIKBDTAllow dots in job specifier of input type 'Previous build' - [78]
5GPK54IVLog segfaults from the evaluator - [79]
DDMYFZ5XFix the jobset unchanged check - [80]
XDDCO6CH* hydra: add dependency list with links to logfiles - [81]
NP4OLR5TDon't pass an undefined input - [82]
QW7GTTXIDon't maintain BuildInputs anymore - [83]
AFTXA575* $HYDRA_DATA environment variable. - [84]
3XTHEUMP* Implemented the clone feature. - [85]
HX4QYOYAadd first evaluations tests - [86]
POPU2ATH* hydra_scheduler: use eval-jobs. - [87]
AKRVETP5Handle UTF-8 characters in eval error messages - [88]
A22P7HCOhydra: at evaluation, check if path is already built, and mark as built in stead of adding to the queue. - [89]
RX5IIZMTUse Email::MIME instead of Email::Simple - [90]
5SMQ2PLKFix tests - [91]
JK2QWPH6 - [92]
QS4OX6Z7Huuuge speedup in the Hydra evaluator - [93]
JCJJKRWQHandle job aliases in AggregateConstituents - [94]
FYWE74AA - [95]
X5EOJLNAAllow passing a specific build as an input - [96]
NS7SND6Rhydra-evaluator: Send statistics to statsd - [97]
N22GPKYT* Put info about logs / build products in the DB. - [98]
2WRTOU2ZCleanup - [99]
BMSQD2ZHIndentation - [100]
PMNWRTGJAdd multiple output support - [101]
K3HODXGHCheck all inputs for blame but only email selected inputs - [102]
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. - [103]
VHV6GI4LAdd a jobset eval action to restart all aborted/cancelled builds - [104]
D3YETHSBOnly pass the drv path if it is still valid - [105]
SKQXOQ7Thydra: fixed missing argument to restartbuild function - [106]
PHNLYPKBCall buildFinished when a cached build is added - [107]
TJK27WSBOpen the DB using Hydra::Model::DB->new - [108]
EDRUQ4UKDie TABs die - [109]
FTPCV25MStore aggregate members in the database - [110]
KQS7DSKJ* Clean up indentation. - [111]
JTRG7RDQadd support for git as jobinput - [112]
ZTQEU5QSHydra: Add support for maxSilent meta attribute (also already added timeout, but not implemented the actual timeout for the build yet) - [113]
24BMQDZAStart of single-process hydra-queue-runner - [114]
SGNXIOI4Hydra/32: Add option to force evaluation of a certain jobset via web interface (for admins only) - [115]
CQTN62OHDie tabs die - [*]
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;