Add a plugin mechanism
[?]
May 8, 2013, 3:30 PM
5EQYVRWECBDJORGI5DRIOUEJXSXMRCQNT2562BM4Z4U52LT7JUHACDependencies
- [2]
ZDNXMJ3J* Typo. - [3]
EUWLW7FYDon't use the Switch module - [4]
LSZLZHJYAllow users to edit their own settings - [5]
R6SX4KIDPut build status in front of the notification mail subject - [6]
KSD75RNJHydra/18: fixed uninitialized value error when logfile is null - [7]
LGNML7VJDon't use a prepared statement for the active build steps query - [8]
TULPZ62Y* Perform builds in parallel. - [9]
TJK27WSBOpen the DB using Hydra::Model::DB->new - [10]
7YBYT2LQ - [11]
NZYFWV6Mhydra-build: Add system info to the subject and extra headers. - [12]
WAZFSDSLusing backquote as argument resulted in only first line as first argument to removeAsciiEscapes - [13]
4R7OGJEYDo not send emails when build is cancelled/aborted. Also, ignore aborted/cancelled builds in comparing to previous build. - [14]
AFTXA575* $HYDRA_DATA environment variable. - [15]
RWIBJ5L4* Autoflush stdout. - [16]
ZCQOOAGYRemove redundant dot in status emails - [17]
PMNWRTGJAdd multiple output support - [18]
QUTWJR7P* Include more info in notification emails. - [19]
4IXVBLUIhack to try and prevent too many newlines - [20]
6US6LEC7* Add a NarSize field to Hydra manifests. This allows nix-env - [21]
PY4WQF5Gremove ascii escapes from log in tail page and emails - [22]
HPEG2RHVMerge the BuildResultInfo table into the Builds table - [23]
Y6AHH4THRemove the logfile and logSize columns from the database - [24]
TRFRGOBDRemove Twitter notification support - [25]
JK2QWPH6 - [26]
LA27PR4Uhydra: fix enable email notification bug - [27]
ALXRI3Y5hydra: removed need for HYDRA_BUILD_BASEURL env variable - [28]
WQXF2T3Dhydra-evaluator: Don't require $HYDRA_CONFIG - [29]
FHAVPTZ6Hydra/23: added some X-headers with meta info in email notifications, added more descriptive status - [30]
HDMOHBZMHydra/57: Unknown failure -> Failed - [31]
KA45EBF5* Send email if a build fails. - [32]
A22P7HCOhydra: at evaluation, check if path is already built, and mark as built in stead of adding to the queue. - [33]
VPKMUFF3hydra-build: only send email if the status differs from the previous build - [34]
XZ7ZIKCV* Allow overriding the sender email address. - [35]
RTDA3GQ4 - [36]
RBHHV7P7* Read logs using logContents function in stead of handling it everywhere separately. - [37]
FYO6NECEhydra - [38]
5O6E5SU5hydra: store logfile/output path/closure size - [39]
QBQSQOSYhydra: moved getbuildlog - [40]
MPGVCHVF* Fix an apparent incompatibility with recent DBIx::Class. - [41]
K7IRNVRFhydra: fixed email notification bug, when build is performed for the first time (it always said succeeded in the body of the mail - [42]
GAIBDEZZ* Store the name of the machine that performed a build step in the - [43]
LOMVF2KHdo not send email for builds with status 'aborted' - [44]
IS32JFKX* Typo. - [45]
IW2LHCLLfixed email bug - [46]
BOFOHCPKremoved debug print, added last 50 lines in failure emails - [47]
3BKF6P72* Use Nix's negative caching. - [48]
CXRCPDSQ* added support for twitter notification - [49]
6KCP6ODP* Get the URI for use in notification mails from the Hydra config - [50]
AJKTRRDJrename var - [51]
OG7BEM57 - [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. - [*]
D5QIOJGP* Move everything up one directory. - [*]
64K7R4Y6Forgot to change Nix to Nix::Store in one place
Change contents
- file addition: Plugin[54.53]
- file addition: EmailNotification.pm[0.1]
package Hydra::Plugin::EmailNotification;use strict;use feature qw/switch/;use POSIX qw(strftime);use Email::Sender::Simple qw(sendmail);use Email::Sender::Transport::SMTP;use Email::Simple;use Email::Simple::Creator;use Sys::Hostname::Long;use Text::Table;use File::Slurp;use Hydra::Helper::Nix;sub statusDescription {my ($buildstatus) = @_;my $status = "Failed";given ($buildstatus) {when (0) { $status = "Success"; }when (1) { $status = "Failed with non-zero exit code"; }when (2) { $status = "Dependency failed"; }when (4) { $status = "Cancelled"; }}return $status;}sub buildFinished {my ($self, $db, $config, $build, $dependents) = @_;die unless $build->finished;my $prevBuild;($prevBuild) = $db->resultset('Builds')->search({ project => $build->project->name, jobset => $build->jobset->name, job => $build->job->name, system => $build->system, finished => 1, id => { '<', $build->id }, -not => { buildstatus => { -in => [4, 3]} }}, { order_by => ["id DESC"] });# Do we want to send mail?unless ($ENV{'HYDRA_FORCE_SEND_MAIL'}) {return unless $build->jobset->enableemail && ($build->maintainers ne "" || $build->jobset->emailoverride ne "");# If build is cancelled or aborted, do not send email.return if $build->buildstatus == 4 || $build->buildstatus == 3;# If there is a previous (that is not cancelled or aborted) build# with same buildstatus, do not send email.return if defined $prevBuild && ($build->buildstatus == $prevBuild->buildstatus);}# Send mail.# !!! should use the Template Toolkit here.my $to = (!$build->jobset->emailoverride eq "") ? $build->jobset->emailoverride : $build->maintainers;print STDERR "sending mail notification to ", $to, "\n";my $jobName = $build->project->name . ":" . $build->jobset->name . ":" . $build->job->name;my $status = statusDescription($build->buildstatus);my $baseurl = hostname_long;my $sender = $config->{'notification_sender'} ||(($ENV{'USER'} || "hydra") . "@" . $baseurl);my $selfURI = $config->{'base_uri'} || "http://localhost:3000";sub showTime { my ($x) = @_; return strftime('%Y-%m-%d %H:%M:%S', localtime($x)); }my $infoTable = Text::Table->new({ align => "left" }, \ " | ", { align => "left" });my @lines = ([ "Build ID:", $build->id ],[ "Nix name:", $build->nixname ],[ "Short description:", $build->description || '(not given)' ],[ "Maintainer(s):", $build->maintainers ],[ "System:", $build->system ],[ "Derivation store path:", $build->drvpath ],[ "Output store path:", join(", ", map { $_->path } $build->buildoutputs) ],[ "Time added:", showTime $build->timestamp ],);push @lines, ([ "Build started:", showTime $build->starttime ],[ "Build finished:", showTime $build->stoptime ],[ "Duration:", $build->stoptime - $build->starttime . "s" ],) if $build->starttime;$infoTable->load(@lines);my $inputsTable = Text::Table->new({ title => "Name", align => "left" }, \ " | ",{ title => "Type", align => "left" }, \ " | ",{ title => "Value", align => "left" });@lines = ();foreach my $input ($build->inputs) {my $type = $input->type;push @lines,[ $input->name, $input->type, ( $input->type eq "build" || $input->type eq "sysbuild")? $input->dependency->id: ($input->type eq "string" || $input->type eq "boolean")? $input->value : ($input->uri . ':' . $input->revision)];}$inputsTable->load(@lines);my $loglines = 50;my $logtext = logContents($build->drvpath, $loglines);$logtext = removeAsciiEscapes($logtext);my $body = "Hi,\n". "\n". "This is to let you know that Hydra build " . $build->id. " of job " . $jobName . " " . (defined $prevBuild ? "has changed from '" . statusDescription($prevBuild->buildstatus) . "' to '$status'" : "is '$status'" ) .".\n". "\n". "Complete build information can be found on this page: ". "$selfURI/build/" . $build->id . "\n". ($build->buildstatus != 0 ? "\nThe last $loglines lines of the build log are shown at the bottom of this email.\n" : ""). "\n". "A summary of the build information follows:\n". "\n". $infoTable->body. "\n". "The build inputs were:\n". "\n". $inputsTable->title. $inputsTable->rule('-', '+'). $inputsTable->body. "\n". "Regards,\n\nThe Hydra build daemon.\n". ($build->buildstatus != 0 ? "\n---\n$logtext" : "");# stripping trailing spaces from lines$body =~ s/[\ ]+$//gm;my $email = Email::Simple->create(header => [To => $to,From => "Hydra Build Daemon <$sender>",Subject => "$status: Hydra job $jobName on " . $build->system . ", build " . $build->id,'X-Hydra-Instance' => $baseurl,'X-Hydra-Project' => $build->project->name,'X-Hydra-Jobset' => $build->jobset->name,'X-Hydra-Job' => $build->job->name,'X-Hydra-System' => $build->system],body => "",);$email->body_set($body);if (defined $ENV{'HYDRA_MAIL_SINK'}) {# For testing, redirect all mail to a file.write_file($ENV{'HYDRA_MAIL_SINK'}, { append => 1 }, $email->as_string . "\n");} else {sendmail($email);}}1; - file addition: Plugin.pm[54.53]
package Hydra::Plugin;use Module::Pluggablesearch_path => "Hydra::Plugin",require => 1;# $plugin->buildFinished($db, $config, $build, $dependents):## Called when build $build has finished. If the build failed, then# $dependents is an array ref to a list of builds that have also# failed as a result (i.e. because they depend on $build or a failed# dependeny of $build).1; - edit in src/script/hydra-build at line 8[55.16][6.54]
use Hydra::Plugin; - edit in src/script/hydra-build at line 13[6.30]→[6.276:399](∅→∅),[6.1070]→[6.276:399](∅→∅),[6.399]→[6.0:25](∅→∅),[6.25]→[6.0:21](∅→∅),[6.21]→[6.28:69](∅→∅),[6.55]→[6.2328:2353](∅→∅),[6.2353]→[3.0:24](∅→∅)
use Email::Sender::Simple qw(sendmail);use Email::Sender::Transport::SMTP;use Email::Simple;use Email::Simple::Creator;use Sys::Hostname::Long;use Config::General;use Text::Table;use POSIX qw(strftime);use Data::Dump qw(dump);use feature qw/switch/; - edit in src/script/hydra-build at line 19[6.749]→[6.401:402](∅→∅),[6.401]→[6.401:402](∅→∅),[6.935]→[6.885:886](∅→∅),[6.15667]→[6.13:66](∅→∅),[6.1033]→[6.13:66](∅→∅),[6.66]→[6.0:27](∅→∅),[6.27]→[3.25:255](∅→∅),[3.255]→[6.330:336](∅→∅),[6.330]→[6.330:336](∅→∅),[6.336]→[6.887:888](∅→∅),[6.888]→[6.341:362](∅→∅),[6.341]→[6.341:362](∅→∅),[6.362]→[6.15668:15669](∅→∅),[6.15669]→[6.362:363](∅→∅),[6.362]→[6.362:363](∅→∅),[6.363]→[6.402:452](∅→∅),[6.1033]→[6.402:452](∅→∅),[6.402]→[6.402:452](∅→∅),[6.452]→[6.26:27](∅→∅),[6.27]→[6.9719:9752](∅→∅),[6.9752]→[6.889:890](∅→∅),[6.890]→[5.0:117](∅→∅),[6.69]→[6.115:147](∅→∅),[5.117]→[6.115:147](∅→∅),[6.152]→[6.115:147](∅→∅),[6.1382]→[6.115:147](∅→∅),[6.3790]→[6.115:147](∅→∅),[6.115]→[6.115:147](∅→∅),[6.147]→[6.452:453](∅→∅),[6.452]→[6.452:453](∅→∅),[6.453]→[6.3791:3863](∅→∅),[6.3863]→[6.309:352](∅→∅),[6.352]→[6.3899:3975](∅→∅),[6.3899]→[6.3899:3975](∅→∅),[6.3975]→[6.891:926](∅→∅),[6.926]→[6.4011:4035](∅→∅),[6.4011]→[6.4011:4035](∅→∅),[6.4035]→[6.0:36](∅→∅),[6.36]→[6.0:54](∅→∅),[6.54]→[6.353:392](∅→∅),[6.68]→[6.4113:4125](∅→∅),[6.392]→[6.4113:4125](∅→∅),[6.4113]→[6.4113:4125](∅→∅),[6.4125]→[5.118:450](∅→∅),[5.450]→[6.330:348](∅→∅),[6.330]→[6.330:348](∅→∅),[6.349]→[6.349:573](∅→∅),[6.573]→[5.451:508](∅→∅),[6.434]→[6.661:662](∅→∅),[5.508]→[6.661:662](∅→∅),[6.4574]→[6.661:662](∅→∅),[6.9989]→[6.661:662](∅→∅),[6.661]→[6.661:662](∅→∅),[6.662]→[4.6942:6975](∅→∅),[4.6975]→[6.393:446](∅→∅),[6.34]→[6.393:446](∅→∅),[6.446]→[6.35:90](∅→∅),[6.51]→[6.35:90](∅→∅),[6.90]→[6.3075:3076](∅→∅),[6.118]→[6.3075:3076](∅→∅),[6.130]→[6.3075:3076](∅→∅),[6.153]→[6.3075:3076](∅→∅),[6.207]→[6.3075:3076](∅→∅),[6.503]→[6.3075:3076](∅→∅),[6.517]→[6.3075:3076](∅→∅),[6.736]→[6.3075:3076](∅→∅),[6.1093]→[6.3075:3076](∅→∅),[6.3075]→[6.3075:3076](∅→∅),[6.3076]→[6.447:515](∅→∅),[6.515]→[6.131:221](∅→∅),[6.153]→[6.131:221](∅→∅),[6.221]→[2.0:89](∅→∅),[2.89]→[6.309:623](∅→∅),[6.309]→[6.309:623](∅→∅),[6.623]→[6.15670:15755](∅→∅),[6.15755]→[6.674:759](∅→∅),[6.674]→[6.674:759](∅→∅),[6.759]→[6.9990:10203](∅→∅),[6.10203]→[6.1032:1062](∅→∅),[6.1032]→[6.1032:1062](∅→∅)
sub statusDescription {my ($buildstatus) = @_;my $status = "Failed";given ($buildstatus) {when (0) { $status = "Success"; }when (1) { $status = "Failed with non-zero exit code"; }when (2) { $status = "Dependency failed"; }when (4) { $status = "Cancelled"; }}return $status;}sub sendEmailNotification {my ($build) = @_;die unless $build->finished;return unless $build->jobset->enableemail && ($build->maintainers ne "" || $build->jobset->emailoverride ne "");# Do we want to send mail?my $prevBuild;($prevBuild) = $db->resultset('Builds')->search({ project => $build->project->name, jobset => $build->jobset->name, job => $build->job->name, system => $build->system, finished => 1, id => { '<', $build->id }, -not => { buildstatus => { -in => [4, 3]} }}, { order_by => ["id DESC"] });# If build is cancelled or aborted, do not send email.return if $build->buildstatus == 4 || $build->buildstatus == 3;# If there is a previous (that is not cancelled or aborted) build# with same buildstatus, do not send email.return if defined $prevBuild && ($build->buildstatus == $prevBuild->buildstatus);# Send mail.# !!! should use the Template Toolkit here.print STDERR "sending mail notification to ", $build->maintainers, "\n";my $jobName = $build->project->name . ":" . $build->jobset->name . ":" . $build->job->name;my $status = statusDescription($build->buildstatus);my $baseurl = hostname_long;my $sender = $config->{'notification_sender'} ||(($ENV{'USER'} || "hydra") . "@" . $baseurl);my $selfURI = $config->{'base_uri'} || "http://localhost:3000";sub showTime { my ($x) = @_; return strftime('%Y-%m-%d %H:%M:%S', localtime($x)); }my $infoTable = Text::Table->new({ align => "left" }, \ " | ", { align => "left" });my @lines = ([ "Build ID:", $build->id ],[ "Nix name:", $build->nixname ],[ "Short description:", $build->description || '(not given)' ],[ "Maintainer(s):", $build->maintainers ],[ "System:", $build->system ],[ "Derivation store path:", $build->drvpath ],[ "Output store path:", join(", ", map { $_->path } $build->buildoutputs) ],[ "Time added:", showTime $build->timestamp ],);push @lines, ([ "Build started:", showTime $build->starttime ],[ "Build finished:", showTime $build->stoptime ],[ "Duration:", $build->stoptime - $build->starttime . "s" ],) if $build->starttime;$infoTable->load(@lines); - edit in src/script/hydra-build at line 20[6.1063]→[6.1063:1103](∅→∅),[6.1103]→[2.90:200](∅→∅),[2.200]→[6.1211:1425](∅→∅),[6.1211]→[6.1211:1425](∅→∅),[6.1425]→[6.2354:2425](∅→∅),[6.2425]→[6.1463:1698](∅→∅),[6.1463]→[6.1463:1698](∅→∅)
my $inputsTable = Text::Table->new({ title => "Name", align => "left" }, \ " | ",{ title => "Type", align => "left" }, \ " | ",{ title => "Value", align => "left" });@lines = ();foreach my $input ($build->inputs) {my $type = $input->type;push @lines,[ $input->name, $input->type, ( $input->type eq "build" || $input->type eq "sysbuild")? $input->dependency->id: ($input->type eq "string" || $input->type eq "boolean")? $input->value : ($input->uri . ':' . $input->revision)];}$inputsTable->load(@lines); - edit in src/script/hydra-build at line 21[6.1]→[6.1:24](∅→∅),[6.24]→[6.10650:10709](∅→∅),[6.1020]→[6.205:250](∅→∅),[6.10709]→[6.205:250](∅→∅),[6.205]→[6.205:250](∅→∅),[6.108]→[6.807:846](∅→∅),[6.132]→[6.807:846](∅→∅),[6.153]→[6.807:846](∅→∅),[6.250]→[6.807:846](∅→∅),[6.386]→[6.807:846](∅→∅),[6.1698]→[6.807:846](∅→∅),[6.807]→[6.807:846](∅→∅),[6.846]→[6.0:67](∅→∅),[6.67]→[6.10240:10414](∅→∅),[6.183]→[6.965:980](∅→∅),[6.186]→[6.965:980](∅→∅),[6.701]→[6.965:980](∅→∅),[6.4696]→[6.965:980](∅→∅),[6.10414]→[6.965:980](∅→∅),[6.965]→[6.965:980](∅→∅),[6.980]→[2.201:268](∅→∅),[2.268]→[6.1039:1087](∅→∅),[6.1763]→[6.1039:1087](∅→∅),[6.1039]→[6.1039:1087](∅→∅),[6.1087]→[6.10415:10546](∅→∅),[6.276]→[6.1087:1102](∅→∅),[6.10546]→[6.1087:1102](∅→∅),[6.1087]→[6.1087:1102](∅→∅),[6.1102]→[6.1764:2044](∅→∅),[6.2044]→[6.277:327](∅→∅),[6.327]→[6.1021:1084](∅→∅),[6.1084]→[6.403:404](∅→∅),[6.10611]→[6.403:404](∅→∅),[6.403]→[6.403:404](∅→∅),[6.404]→[6.4697:4767](∅→∅),[6.1153]→[6.4697:4767](∅→∅),[6.540]→[6.3076:3077](∅→∅),[6.1153]→[6.3076:3077](∅→∅),[6.4767]→[6.3076:3077](∅→∅),[6.3076]→[6.3076:3077](∅→∅),[6.3077]→[6.4768:4875](∅→∅),[6.4875]→[6.541:600](∅→∅),[6.3077]→[6.541:600](∅→∅),[6.600]→[6.4876:4904](∅→∅),[6.4904]→[6.1154:1209](∅→∅),[6.644]→[6.1154:1209](∅→∅),[6.1209]→[5.509:610](∅→∅),[5.610]→[6.91:135](∅→∅),[6.782]→[6.91:135](∅→∅),[6.135]→[6.822:935](∅→∅),[6.822]→[6.822:935](∅→∅),[6.935]→[6.107:209](∅→∅),[6.209]→[6.760:771](∅→∅),[6.987]→[6.760:771](∅→∅),[6.1287]→[6.760:771](∅→∅),[6.760]→[6.760:771](∅→∅),[6.771]→[6.0:20](∅→∅),[6.20]→[6.808:815](∅→∅),[6.1311]→[6.808:815](∅→∅),[6.808]→[6.808:815](∅→∅),[6.815]→[6.21:50](∅→∅),[6.50]→[6.815:816](∅→∅),[6.815]→[6.815:816](∅→∅),[6.816]→[6.1312:1368](∅→∅),[6.1368]→[6.845:846](∅→∅),[6.845]→[6.845:846](∅→∅),[6.846]→[6.786:808](∅→∅),[6.808]→[6.868:870](∅→∅),[6.1009]→[6.868:870](∅→∅),[6.868]→[6.868:870](∅→∅),[6.870]→[6.15756:15757](∅→∅),[6.15757]→[6.870:871](∅→∅),[6.870]→[6.870:871](∅→∅)
my $loglines = 50;my $logtext = logContents($build->drvpath, $loglines);$logtext = removeAsciiEscapes($logtext);my $body = "Hi,\n". "\n". "This is to let you know that Hydra build " . $build->id. " of job " . $jobName . " " . (defined $prevBuild ? "has changed from '" . statusDescription($prevBuild->buildstatus) . "' to '$status'" : "is '$status'" ) .".\n". "\n". "Complete build information can be found on this page: ". "$selfURI/build/" . $build->id . "\n". ($build->buildstatus != 0 ? "\nThe last $loglines lines of the build log are shown at the bottom of this email.\n" : ""). "\n". "A summary of the build information follows:\n". "\n". $infoTable->body. "\n". "The build inputs were:\n". "\n". $inputsTable->title. $inputsTable->rule('-', '+'). $inputsTable->body. "\n". "Regards,\n\nThe Hydra build daemon.\n". ($build->buildstatus != 0 ? "\n---\n$logtext" : "");# stripping trailing spaces from lines$body =~ s/[\ ]+$//gm;my $to = (!$build->jobset->emailoverride eq "") ? $build->jobset->emailoverride : $build->maintainers;my $email = Email::Simple->create(header => [To => $to,From => "Hydra Build Daemon <$sender>",Subject => "$status: Hydra job $jobName on " . $build->system . ", build " . $build->id,'X-Hydra-Instance' => $baseurl,'X-Hydra-Project' => $build->project->name,'X-Hydra-Jobset' => $build->jobset->name,'X-Hydra-Job' => $build->job->name,'X-Hydra-System' => $build->system],body => "",);$email->body_set($body);print $email->as_string if $ENV{'HYDRA_MAIL_TEST'};sendmail($email);} - replacement in src/script/hydra-build at line 291
sendEmailNotification $build;foreach my $plugin (Hydra::Plugin->plugins) {eval {$plugin->buildFinished($db, $config, $build, []);};if ($@) {print STDERR "$plugin->buildFinished: $@\n";}} - replacement in src/script/hydra-build at line 306
sendEmailNotification $db->resultset('Builds')->find($buildId);my $build = $db->resultset('Builds')->find($buildId);$_->buildFinished($db, $config, $build, []) foreach Hydra::Plugin->plugins;