#! /usr/bin/env perl use strict; use utf8; use Hydra::Plugin; use Hydra::PostgresListener; use Hydra::Helper::Nix; use Hydra::Helper::AddBuilds; use Getopt::Long; STDERR->autoflush(1); STDOUT->autoflush(1); binmode STDERR, ":encoding(utf8)"; my $queued_only; GetOptions( "queued-only" => \$queued_only ) or exit 1; my $config = getHydraConfig(); my $db = Hydra::Model::DB->new(); my @plugins = Hydra::Plugin->instantiate(db => $db, config => $config); my $dbh = $db->storage->dbh; my $listener = Hydra::PostgresListener->new($dbh); $listener->subscribe("build_started"); $listener->subscribe("build_finished"); $listener->subscribe("step_finished"); sub buildStarted { my ($buildId) = @_; my $build = $db->resultset('Builds')->find($buildId) or die "build $buildId does not exist\n"; foreach my $plugin (@plugins) { eval { $plugin->buildStarted($build); 1; } or do { print STDERR "error with $plugin->buildStarted: $@\n"; } } } sub buildFinished { my ($buildId, @deps) = @_; my $build = $db->resultset('Builds')->find($buildId) or die "build $buildId does not exist\n"; my @dependents; foreach my $id (@deps) { my $dep = $db->resultset('Builds')->find($id) or die "build $id does not exist\n"; push @dependents, $dep; } foreach my $plugin (@plugins) { eval { $plugin->buildFinished($build, [@dependents]); 1; } or do { print STDERR "error with $plugin->buildFinished: $@\n"; } } # We have to iterate through all dependents as well, and if they are finished # to mark their notificationpendingsince. # Otherwise, the dependent builds will remain with notificationpendingsince set # until hydra-notify is started, as buildFinished is never emitted for them. foreach my $b ($build, @dependents) { $b->update({ notificationpendingsince => undef }) if $b->finished; } } sub stepFinished { my ($buildId, $stepNr, $logPath) = @_; my $build = $db->resultset('Builds')->find($buildId) or die "build $buildId does not exist\n"; my $step = $build->buildsteps->find({stepnr => $stepNr}) or die "step $stepNr does not exist\n"; $logPath = undef if $logPath eq "-"; foreach my $plugin (@plugins) { eval { $plugin->stepFinished($step, $logPath); 1; } or do { print STDERR "error with $plugin->stepFinished: $@\n"; } } } # Process builds that finished while hydra-notify wasn't running. for my $build ($db->resultset('Builds')->search( { notificationpendingsince => { '!=', undef } })) { my $buildId = $build->id; print STDERR "sending notifications for build ${\$buildId}...\n"; buildFinished($build->id); } # Process incoming notifications. while (!$queued_only) { my $messages = $listener->block_for_messages(); while (my $message = $messages->()) { my $channelName = $message->{"channel"}; my $pid = $message->{"pid"}; my $payload = $message->{"payload"}; #print STDERR "got '$channelName' from $pid: $payload\n"; my @payload = split /\t/, $payload; eval { if ($channelName eq "build_started") { buildStarted(int($payload[0])); } elsif ($channelName eq "build_finished") { my $buildId = int($payload[0]); buildFinished($buildId, @payload[1..$#payload]); } elsif ($channelName eq "step_finished") { stepFinished(int($payload[0]), int($payload[1])); } 1; } or do { print STDERR "error processing message '$payload' on channel '$channelName': $@\n"; } } }