diff options
-rw-r--r-- | .gitignore | 15 | ||||
-rw-r--r-- | Build.PL | 43 | ||||
-rw-r--r-- | MANIFEST | 42 | ||||
-rw-r--r-- | MANIFEST.SKIP | 20 | ||||
-rw-r--r-- | Makefile.PL | 31 | ||||
-rw-r--r-- | README | 43 | ||||
-rw-r--r-- | config.yml | 7 | ||||
-rw-r--r-- | lib/jitterbug/Builder.pm | 172 | ||||
-rw-r--r-- | lib/jitterbug/Emailer.pm | 78 | ||||
-rwxr-xr-x | scripts/build-failed.sh | 11 | ||||
-rw-r--r-- | scripts/builder.pl | 77 | ||||
-rwxr-xr-x | scripts/capsule.sh | 69 | ||||
-rw-r--r-- | t/001_base.t | 3 | ||||
-rw-r--r-- | t/002_index_route.t | 1 | ||||
-rw-r--r-- | t/005_builder.t | 76 | ||||
-rw-r--r-- | t/006_emailer.t | 153 | ||||
-rw-r--r-- | t/data/test.yml | 30 |
17 files changed, 740 insertions, 131 deletions
diff --git a/.gitignore b/.gitignore index 7eb4e9c..afad8f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,15 @@ logs/* -*.db \ No newline at end of file +*.db +*.sql +blib/* +pm_to_blib +Makefile +Makefile.old +_build +META.yml +MYMETA.yml +*.bak +*.sw? +Makefile.PL +Build +jitterbug.db diff --git a/Build.PL b/Build.PL new file mode 100644 index 0000000..063b3de --- /dev/null +++ b/Build.PL @@ -0,0 +1,43 @@ +use strict; +use warnings; +use Module::Build; + +my $builder = Module::Build->new( + module_name => 'jitterbug', + license => 'perl', + dist_author => 'franck cuny <franck@lumberjaph.net>', + dist_abstract => 'Perl smoker that uses Dancer and git', + dist_version_from => 'lib/jitterbug.pm', + include_dirs => '', + build_requires => { + 'Test::Most' => 0, + 'Test::MockObject'=> 0, + }, + requires => { + 'YAML' => 0, + 'Dancer' => 1.1810, + 'XML::Feed' => 0, + 'DateTime' => 0, + 'JSON' => 0, + 'Git::Repository' => 0, + 'YAML' => 0, + 'Dancer::Template::Xslate' => 0, + 'Dancer::Plugin::DBIC' => 0, + 'DBIx::Class' => 0, + 'SQL::Translator' => 0, + 'Digest::MD5' => 0, + 'App::perlbrew' => 0, + 'Dist::Zilla' => 0, + 'Email::Stuff' => 0, + }, + add_to_cleanup => [ 'jitterbug-' ], + create_makefile_pl => 'traditional', + meta_merge => { + resources => { + repository => 'http://github.com/franckcuny/jitterbug/tree/master' + }, + }, +); + +$builder->create_build_script(); +print "Have a great day!\n"; diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..268d204 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,42 @@ +Build.PL +config.yml +eg/post_hook.t +environments/development.yml +environments/production.yml +jitterbug.pl +lib/jitterbug.pm +lib/jitterbug/Hook.pm +lib/jitterbug/Plugin/Template.pm +lib/jitterbug/Project.pm +lib/jitterbug/Schema.pm +lib/jitterbug/Schema/Result/Commit.pm +lib/jitterbug/Schema/Result/Project.pm +lib/jitterbug/Schema/Result/Task.pm +lib/jitterbug/Task.pm +lib/jitterbug/WebService.pm +Makefile +MANIFEST This list of files +MANIFEST.SKIP +public/404.html +public/500.html +public/dispatch.cgi +public/dispatch.fcgi +public/favicon.ico +public/images/feed.png +public/images/perldancer-bg.jpg +public/images/perldancer.jpg +README +scripts/builder.pl +scripts/deploy_schema +scripts/migrate_from_redis.pl +t/001_base.t +t/002_index_route.t +t/003_hook_route.t +t/004_project.t +t/data/export_jitterbug.yml +t/data/test.yaml +TODO +views/index.tt +views/layouts/main.tt +views/project/index.tt +views/task/index.tt diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP new file mode 100644 index 0000000..7b521b7 --- /dev/null +++ b/MANIFEST.SKIP @@ -0,0 +1,20 @@ +\.git* +tmp.* +.*\.o +\..*sw? +\.bak +_build +^Build$ +blib/* +.*\.data +.*core +foo* +.*\.gz +^tags +jitterbug-\d+\..* +.prove +^MYMETA.yml$ +jitterbug.db +logs/ +META.yml +Makefile.PL diff --git a/Makefile.PL b/Makefile.PL deleted file mode 100644 index 880131b..0000000 --- a/Makefile.PL +++ /dev/null @@ -1,31 +0,0 @@ -use strict; -use warnings; -use ExtUtils::MakeMaker; - -WriteMakefile( - NAME => 'jitterbug', - AUTHOR => q{franck cuny <franck@lumberjaph.net>}, - VERSION_FROM => 'lib/jitterbug.pm', - ABSTRACT => 'perl/github smoker', - ( $ExtUtils::MakeMaker::VERSION >= 6.3002 - ? ( 'LICENSE' => 'perl' ) - : () ), - PL_FILES => {}, - PREREQ_PM => { - 'Test::More' => 0, - 'YAML' => 0, - 'Dancer' => 1.1810, - 'XML::Feed' => 0, - 'DateTime' => 0, - 'JSON' => 0, - 'Git::Repository' => 0, - 'YAML' => 0, - 'Dancer::Template::Xslate' => 0, - 'Dancer::Plugin::DBIC' => 0, - 'DBIx::Class' => 0, - 'SQL::Translator' => 0, - 'Digest::MD5' => 0, - }, - dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', }, - clean => { FILES => 'jitterbug-*' }, -); diff --git a/README b/README new file mode 100644 index 0000000..7139479 --- /dev/null +++ b/README @@ -0,0 +1,43 @@ +Jitterbug : A continuous integration system built on Dancer and git hooks + +Jitterbug is written in Perl 5 and depends on various CPAN modules, such +as Dancer, DBIx::Class and Git::Repository. + +Installing Jitterbug +=================== + +perl Build.PL + +# You can also use Makefile.PL, but you will then have to manually +# install dependencies +# perl Makefile.PL + +# install missing dependencies +./Build installdeps + +# start the jitterbug Dancer app, which by default binds to port 3000 +perl jitterbug.pl + +# If you need to start it on a different port use -p +# perl jitterbug.pl -p 3001 + +In another terminal, deploy a DBIx::Class schema ( which is SQLite by default, +change the values in config.yml to tweak) : + +perl scripts/deploy_schema config.yml + +Now add a post-receive hook to your github project that hits the /hook/ URL +on the server that the jitterbug Dancer app is running on, i.e. + + http://example.com:3001/hook/ + +Now you must start the builder, which actually clones a new git repo for +each task (this could be network-intensive) and actually runs the build +and test commands for each project. + + perl scripts/builder.pl -c config.yml + +Now, when you commit to a project that has a Jitterbug post-receive hook, +the builder check every 30 seconds for a new task and build and test your +projects! + diff --git a/config.yml b/config.yml index e4144c3..bf9bd23 100644 --- a/config.yml +++ b/config.yml @@ -18,6 +18,13 @@ jitterbug: build_process: builder: ./scripts/capsule.sh on_failure: ./scripts/build-failed.sh + on_failure_cc_email: alice@example.com + on_failure_from_email: donotreply@example.com + on_failure_subject_prefix: "[jitterbug] FAIL " + on_failure_header: + on_failure_footer: + options: + perlbrew: 1 plugins: DBIC: diff --git a/lib/jitterbug/Builder.pm b/lib/jitterbug/Builder.pm new file mode 100644 index 0000000..a836f70 --- /dev/null +++ b/lib/jitterbug/Builder.pm @@ -0,0 +1,172 @@ +package jitterbug::Builder; + +use strict; +use warnings; + +use YAML qw/LoadFile Dump/; +use JSON; +use File::Path qw/rmtree/; +use Path::Class; +use Getopt::Long qw/:config no_ignore_case/; +use File::Basename; +use Git::Repository; +use jitterbug::Schema; +#use Data::Dumper; + +local $| = 1; +use constant DEBUG => 1; + +sub new { + my $self = bless {} => shift; + + GetOptions( + 'C|cron' => \$self->{'cron'}, + 'c|configfile=s' => \$self->{'configfile'}, + 's|sleep=i' => \$self->{'sleep'}, + ) or die "Cannot get options\n"; + + $self->{'configfile'} + or die qq{missing config.yml, use "-c config.yml" to help us find it\n}; + + die "Does not exist!: " . $self->{'configfile'} unless -e $self->{'configfile'}; + + return $self; +} + +sub debug { + warn @_ if DEBUG; +} + +sub run { + my $self = shift || die "Must call run() from object\n"; + my $conf = $self->{'conf'} = LoadFile( $self->{'configfile'} ); + my $dbix_conf = $conf->{'plugins'}{'DBIC'}{'schema'}; + + debug("Loaded config file: " . $self->{'configfile'}); + debug("Connection Info: " . join ':', @{ $dbix_conf->{'connect_info'} }); + + $self->{'schema'} = jitterbug::Schema->connect( @{ $dbix_conf->{'connect_info'} } ); + $self->{'interval'} = $self->{'sleep'} || + $conf->{'jitterbug'}{'builder'}{'sleep'} || + 30; + + return $self->build; +} + +sub build { + my $self = shift; + + while (1) { + my @tasks = $self->{'schema'}->resultset('Task')->all(); + debug("Found " . scalar(@tasks) . " tasks"); + + foreach my $task (@tasks) { + $task ? $self->run_task($task) : $self->sleep; + } + + $self->{'cron'} and return 0; + + $self->sleep(5); + } + + return 1; +} + +sub sleep { + my ($self, $interval) = @_; + $interval ||= $self->{'interval'}; + debug("sleeping for $interval seconds\n"); + sleep $interval; +} + +sub run_task { + my $self = shift; + my ($task) = @_; + my $desc = JSON::decode_json( $task->commit->content ); + my $conf = $self->{'conf'}; + + $desc->{'build'}{'start_time'} = time(); + debug("Build Start"); + + my $report_path = dir( + $conf->{'jitterbug'}{'reports'}{'dir'}, + $task->project->name, + $task->commit->sha256, + ); + + my $build_dir = dir( + $conf->{'jitterbug'}{'build'}{'dir'}, + $task->project->name, + ); + + debug("Removing $build_dir"); + rmtree($build_dir, { error => \my $err } ); + warn @$err if @$err; + + $self->sleep(1); # avoid race conditions + + my $repo = $task->project->url . '.git'; + my $r = Git::Repository->create( clone => $repo => $build_dir ); + + debug("Checking out " . $task->commit->sha256 . " from $repo into $build_dir\n"); + $r->run( 'checkout', $task->commit->sha256 ); + + my $builder = $conf->{'jitterbug'}{'build_process'}{'builder'}; + + my $perlbrew = $conf->{'options'}{'perlbrew'} || 1; + + my $builder_command = "$builder $build_dir $report_path $perlbrew"; + + debug("Going to run builder : $builder_command"); + my $res = `$builder_command`; + debug($res); + + $desc->{'build'}{'end_time'} = time(); + + my @versions = glob( $report_path . '/*' ); + foreach my $version (@versions) { + open my $fh, '<', $version; + my ($result, $lines); + while (<$fh>){ + $lines .= $_; + } + ($result) = $lines =~ /Result:\s(\w+)/; + my ( $name, ) = basename($version); + $name =~ s/\.txt//; + if ( !$result || ($result && $result !~ /PASS/ )) { + # mail author of the commit + $result = "FAIL"; + my $message = $desc->{'message'}; + my $commiter = $desc->{'author'}{'email'}; + my $output = $lines; + my $sha = $desc->{'id'}; + my $on_failure = $conf->{'jitterbug'}{'build_process'}{'on_failure'}; + my $on_failure_cc_email = $conf->{'jitterbug'}{'build_process'}{'on_failure_email'}; + + $message =~ s/'/\\'/g; $commiter =~ s/'/\\'/g; $output =~ s/'/\\'/g; + my $failure_cmd = sprintf("%s '%s' %s '%s' '%s' %s %s", $on_failure, $commiter, $task->project->name, $message, $output, $sha, $on_failure_cc_email); + debug("Running failure command: $failure_cmd"); + + # does it look like a module name? + if ($on_failure =~ /::/) { + # we should do some error checking here + eval "require $on_failure"; + $on_failure->new($conf,$task,$output)->run; + } else { + system($failure_cmd); + } + } + $desc->{'build'}{'version'}{$name} = $result; + close $fh; + } + + $task->commit->update( { + content => JSON::encode_json($desc), + } ); + debug("Task completed for " . $task->commit->sha256 . "\n"); + + $task->delete(); + + debug("Task removed from " . $task->project->name . "\n"); +} + diff --git a/lib/jitterbug/Emailer.pm b/lib/jitterbug/Emailer.pm new file mode 100644 index 0000000..d21028f --- /dev/null +++ b/lib/jitterbug/Emailer.pm @@ -0,0 +1,78 @@ +package jitterbug::Emailer; + +use strict; +use warnings; +use Email::Stuff; +use JSON; + +sub new { + my $self = bless {} => shift; + my ($conf,$task,$tap_output) = @_; + # smelly + $self->{'conf'} = $conf; + $self->{'task'} = $task; + $self->{'tap_output'} = $tap_output; + + return $self; +} + +sub _make_body { + my ($header, $message, $tap, $footer) = @_; + + no warnings 'uninitialized'; + return <<BODY; +$header +Commit Message: +$message + +TAP Output: +$tap +$footer +BODY + +} +sub run { + my $self = shift; + my $task = $self->{'task'}; + my $buildconf = $self->{'conf'}->{'jitterbug'}{'build_process'}; + my $project = $task->project->name; + my $tap = $self->{'tap_output'}; + my $sha1 = $task->commit->sha256; + my $shortsha1 = substr($sha1, 0, 8); + my $desc = JSON::decode_json( $task->commit->content ); + my $email = $desc->{'author'}{'email'}; + my $message = $desc->{'message'}; + my $header = $buildconf->{'on_failure_header'}; + my $footer = $buildconf->{'on_failure_footer'}; + my $body = _make_body($header,$message, $tap, $footer); + my $summary = ''; + + if ( $tap =~ m/^(Test Summary Report.*)/ms ) { + $summary = $1; + } + + # Expand placeholders in our failure email + $body =~ s/%%PROJECT%%/$project/g; + $body =~ s/%%SHA1%%/$sha1/g; + $body =~ s/%%SUMMARY%%/$summary/g; + + + my $stuff = Email::Stuff->from($buildconf->{'on_failure_from_email'}) + # bug in Email::Stuff brakes chaining if $email is empty + ->to($email || " ") + ->cc($buildconf->{'on_failure_cc_email'}) + ->text_body($body) + ->subject( + $buildconf->{'on_failure_subject_prefix'} . "$project @ $shortsha1 $message" + ); + # Should we attach a build log for convenience? + # ->attach(io('dead_bunbun_faked.gif')->all, + # filename => 'dead_bunbun_proof.gif') + $self->{'last_email_sent'} = $stuff; + + $stuff->send; + + return $self; +} + +1; diff --git a/scripts/build-failed.sh b/scripts/build-failed.sh index bddf4d0..ebd098a 100755 --- a/scripts/build-failed.sh +++ b/scripts/build-failed.sh @@ -1,11 +1,14 @@ COMMITER=$1 -MESSAGE=$2 -OUTPUT=$3 -SHA=$4 +PROJECT=$2 +MESSAGE=$3 +OUTPUT=$4 +SHA=$5 +CC_EMAIL=$6 echo " Message: $MESSAGE +Test Output: $OUTPUT -" | mail -s "[jitterbug] BUILD FAILED $SHA" --to $COMMITER +" | mail -c "$CC_EMAIL" -s "[jitterbug] FAIL $PROJECT @ $SHA" $COMMITER diff --git a/scripts/builder.pl b/scripts/builder.pl index fba8147..8b7ee2c 100644 --- a/scripts/builder.pl +++ b/scripts/builder.pl @@ -1,80 +1,9 @@ -#!/usr/bin/env perl +#!/usr/bin/perl use strict; use warnings; -use JSON; -use YAML qw/LoadFile Dump/; -use File::Spec; -use File::Path qw/rmtree/; -use File::Basename; -use Git::Repository; -use jitterbug::Schema; +use jitterbug::Builder; -$|++; +exit jitterbug::Builder->new->run; -my $conf_file = shift || die "config.yml is missing"; -my $conf = LoadFile($conf_file); -my $dbix_conf = $conf->{plugins}->{DBIC}->{schema}; -my $schema = jitterbug::Schema->connect( @{ $dbix_conf->{connect_info} } ); -my $interval = $conf->{jitterbug}->{builder}->{sleep} || 30; - -while (1) { - my $task = $schema->resultset('Task')->search()->single(); - - unless ($task) { - sleep $interval; - next; - } - - my $desc = JSON::decode_json($task->commit->content); - $desc->{build}->{start_time} = time(); - - my $report_path = File::Spec->catdir( $conf->{jitterbug}->{reports}->{dir}, - $task->project->name, $task->commit->sha256 ); - my $build_dir = File::Spec->catdir( $conf->{jitterbug}->{build}->{dir}, - $task->project->name ); - - my $repo = $task->project->url . '.git'; - my $r = Git::Repository->create( clone => $repo => $build_dir ); - $r->run( 'checkout', $task->commit->sha256 ); - - my $builder = $conf->{jitterbug}->{build_process}->{builder}; - my $res = `$builder $build_dir $report_path`; - - rmtree($build_dir); - - $desc->{build}->{end_time} = time(); - - my @versions = glob( $report_path . '/*' ); - foreach my $version (@versions) { - open my $fh, '<', $version; - my ($result, $lines); - while (<$fh>){ - $lines .= $_; - } - ($result) = $lines =~ /Result:\s(\w+)/; - my ( $name, ) = basename($version); - $name =~ s/\.txt//; - if ( !$result || ($result && $result !~ /PASS/ )) { - # mail author of the commit - $result = "FAIL"; - my $message = $desc->{message}; - my $commiter = $desc->{author}->{email}; - my $output = "Build failed"; - my $sha = $desc->{id}; - my $on_failure = - $conf->{jitterbug}->{build_process}->{on_failure}; - `$on_failure $commiter $message $output $sha`; - } - $desc->{build}->{version}->{$name} = $result; - close $fh; - } - - $task->commit->update({ - content => JSON::encode_json($desc), - }); - $task->delete(); - warn "done\n"; - sleep 5; -} diff --git a/scripts/capsule.sh b/scripts/capsule.sh index 1977313..abc632d 100755 --- a/scripts/capsule.sh +++ b/scripts/capsule.sh @@ -3,31 +3,62 @@ # first arg: build_dir # second arg: report path +function jitterbug_build () { + if [ -f 'dist.ini' ]; then + echo "Found dist.ini, using Dist::Zilla" + dzil authordeps | cpanm + cpanm --installdeps . + HARNESS_VERBOSE=1 dzil test >> $logfile 2>&1 + elif [ -f 'Build.PL' ]; then + echo "Found Build.PL, using Build.PL" + perl Build.PL + # ./Build installdeps is not available in older Module::Build's + cpanm --installdeps . + HARNESS_VERBOSE=1 ./Build test --verbose >> $logfile 2>&1 + elif [ -f 'Makefile.PL' ]; then + echo "Found Makefile.PL" + perl Makefile.PL + cpanm --installdeps . + HARNESS_VERBOSE=1 make test >> $logfile 2>&1 + elif [ -f 'setup.pir' ]; then + echo "Found setup.pir" + HARNESS_VERBOSE=1 parrot setup.pir test >> $logfile 2>&1 + elif [ -f 'setup.nqp' ]; then + echo "Found setup.nqp" + HARNESS_VERBOSE=1 parrot-nqp setup.nqp test >> $logfile 2>&1 + elif [ -f 'Configure.pl' ]; then + echo "Found Configure.pl" + perl Configure.pl + cpanm --installdeps . + HARNESS_VERBOSE=1 make test >> $logfile 2>&1 + fi +} + +# this is getting smelly builddir=$1 report_path=$2 +perlbrew=$3 +echo "Creating report_path=$report_path" mkdir -p $report_path cd $builddir -source $HOME/perl5/perlbrew/etc/bashrc - -for perl in $HOME/perl5/perlbrew/perls/perl-5.* -do - theperl="$(basename $perl)" - perlbrew switch $theperl +if [ $use_perlbrew ]; then + source $HOME/perl5/perlbrew/etc/bashrc + for perl in $HOME/perl5/perlbrew/perls/perl-5.* + do + theperl=$(perl -e 'print $^V') + logfile="$report_path/perl-$theperl.txt" - perlversion=$(perl -v) - logfile="$report_path/$theperl.txt" + echo ">perlbrew switch $theperl" + perlbrew switch $theperl + # TODO: check error condition - if [ -f 'dist.ini' ]; then - dzil authordeps | cpanm - cpanm --installdeps . - HARNESS_VERBOSE=1 dzil test >> $logfile 2>&1 - else - perl Makefile.PL - cpanm --installdeps . - make - HARNESS_VERBOSE=1 make test >> $logfile 2>&1 - fi -done + jitterbug_build + done +else + theperl=$(perl -e 'print $^V') + logfile="$report_path/perl-$theperl.txt" + jitterbug_build +fi diff --git a/t/001_base.t b/t/001_base.t index 936ffff..43163e6 100644 --- a/t/001_base.t +++ b/t/001_base.t @@ -1,5 +1,6 @@ -use Test::More tests => 1; +use Test::More tests => 2; use strict; use warnings; use_ok 'jitterbug'; +use_ok 'jitterbug::Builder'; diff --git a/t/002_index_route.t b/t/002_index_route.t index 5c02d85..f6fe11b 100644 --- a/t/002_index_route.t +++ b/t/002_index_route.t @@ -3,7 +3,6 @@ use strict; use warnings; ok 1; -ok 2; # the order is important #use jitterbug; #use Dancer::Test; diff --git a/t/005_builder.t b/t/005_builder.t new file mode 100644 index 0000000..84e13ef --- /dev/null +++ b/t/005_builder.t @@ -0,0 +1,76 @@ + +use strict; +use warnings; +use Test::Most tests => 9; +use Data::Dumper; + +use jitterbug::Builder; + +{ + local @ARGV = qw(-c t/data/test.yml -C); + my $b = jitterbug::Builder->new(); + + isa_ok($b,'jitterbug::Builder'); + can_ok($b,qw/run build run_task sleep/); + + is($b->{'configfile'}, 't/data/test.yml'); + is($b->{'cron'}, 1 ); +} + +{ + local @ARGV = qw(-c blarg.yml -C); + + throws_ok (sub { + my $b = jitterbug::Builder->new(); + }, qr/Does not exist/i, 'nonexistent yaml file throws error'); +} + +{ + local @ARGV = qw(-c t/data/test.yml -C); + my $b = jitterbug::Builder->new(); + isa_ok($b, 'jitterbug::Builder'); + is($b->{'configfile'}, 't/data/test.yml'); + + is($b->run, 0, '->run returns 0 in cron mode'); + cmp_deeply($b->{'conf'}, { + 'engines' => { + 'xslate' => { + 'type' => 'text', + 'path' => '/', + 'cache' => '0' + } + }, + 'plugins' => { + 'DBIC' => { + 'schema' => { + 'connect_info' => [ + 'dbi:SQLite:dbname=jitterbug.db' + ], + 'pckg' => 'jitterbug::Schema', + 'skip_automake' => '1' + } + } + }, + 'jitterbug' => { + 'build_process' => { + 'on_failure' => './scripts/build-failed.sh', + 'builder' => './scripts/capsule.sh' + }, + 'builder' => {}, + 'reports' => { + 'dir' => '/tmp/jitterbug' + }, + 'build' => { + 'dir' => '/tmp/build' + } + }, + 'template' => 'xslate', + 'appname' => 'jitterbug', + 'layout' => 'main', + 'logger' => 'file', + 'builds_per_feed' => '5' + }); + + +} + diff --git a/t/006_emailer.t b/t/006_emailer.t new file mode 100644 index 0000000..44dd328 --- /dev/null +++ b/t/006_emailer.t @@ -0,0 +1,153 @@ +use strict; +use warnings; +use Test::Most tests => 9; +use Data::Dumper; +use Test::MockObject; + +use_ok "jitterbug::Emailer"; + +sub setup { + my $buildconf = { + on_failure_from_email => 'bob@example.com', + on_failure_cc_email => 'steve@example.com', + on_failure_subject_prefix => 'BLARG ', + on_failure_header => "Summary:\n%%SUMMARY%%", + on_failure_footer => "FOOT", + }; + + my $conf = { jitterbug => { build_process => $buildconf } }; + my $commit = Test::MockObject->new; + my $project = Test::MockObject->new; + my $task = Test::MockObject->new; + + $project->mock('name', sub { 'ponie' }); + + $commit->mock('sha256', sub { 'c0decafe' }); + $commit->mock('content', sub { '{ "message" : "blargly blarg" }' } ); + + $task->mock('commit', sub { $commit }); + $task->mock('project', sub { $project }); + return ($conf, $commit, $project, $task); +} + +{ + my ($conf, $commit, $project, $task) = setup(); + my $tap = "THIS IS TAP"; + my $e = jitterbug::Emailer->new($conf, $task, $tap); + + isa_ok($e,'jitterbug::Emailer'); + can_ok($e,qw/new run/); + + $e->run; + my $email = $e->{'last_email_sent'}{'email'}; + like($email->body, qr/THIS IS TAP/, 'email body looks right'); + + my $header = $email->{'header'}; + isa_ok($header, 'Email::MIME::Header'); + + is($header->header_raw('cc'), 'steve@example.com', 'cc header'); + like($header->header_raw('subject'), qr/BLARG ponie @ c0decafe blargly blarg/, 'subject header'); + is($header->header_raw('from'), 'bob@example.com', 'from header'); +} + +{ + my ($conf, $commit, $project, $task) = setup(); + my $tap = <<TAP; +Copying lib/Math/Primality/AKS.pm -> blib/lib/Math/Primality/AKS.pm +Copying lib/Math/Primality/BigPolynomial.pm -> blib/lib/Math/Primality/BigPolynomial.pm +Copying lib/Math/Primality.pm -> blib/lib/Math/Primality.pm +Copying bin/primes.pl -> blib/script/primes.pl +Copying bin/strong_psuedoprimes.pl -> blib/script/strong_psuedoprimes.pl +# Testing Math::Primality 0.0401, Perl 5.010001, /usr/bin/perl +t/00-load.t ...................... +1..1 +ok 1 - use Math::Primality; +ok +# Failed test '-1 is not prime' +# at t/is_prime.t line 16. +# Looks like you failed 1 test of 573. +t/is_prime.t ..................... +1..6 +ok 1 - is_prime should handle Math::GMPz objects, three is prime +ok 2 - 2 is prime +ok 3 - 1 is not prime +ok 4 - 0 is not prime +not ok 5 - -1 is not prime +ok 6 - blarg +t/boilerplate.t .................. +1..3 +ok 1 - README contains no boilerplate text +ok 2 - Changes contains no boilerplate text +ok 3 - lib/Math/Primality.pm contains no boilerplate text +ok +Test Summary Report +------------------- +t/is_prime.t (Wstat: 256 Tests: 573 Failed: 1) +Failed test: 5 +Non-zero exit status: 1 +Failed 1/11 test programs. 1/2498 subtests failed. +Files=11, Tests=2498, 3 wallclock secs ( 0.20 usr 0.04 sys + 2.99 cusr 0.18 csys = 3.41 CPU) +Result: FAIL +TAP + my $e = jitterbug::Emailer->new($conf, $task, $tap); + $e->run; + my $email = $e->{'last_email_sent'}{'email'}; + my $body = <<EMAIL; +Summary: +Test Summary Report +------------------- +t/is_prime.t (Wstat: 256 Tests: 573 Failed: 1) +Failed test: 5 +Non-zero exit status: 1 +Failed 1/11 test programs. 1/2498 subtests failed. +Files=11, Tests=2498, 3 wallclock secs ( 0.20 usr 0.04 sys + 2.99 cusr 0.18 csys = 3.41 CPU) +Result: FAIL + +Commit Message: +blargly blarg + +TAP Output: +Copying lib/Math/Primality/AKS.pm -> blib/lib/Math/Primality/AKS.pm +Copying lib/Math/Primality/BigPolynomial.pm -> blib/lib/Math/Primality/BigPolynomial.pm +Copying lib/Math/Primality.pm -> blib/lib/Math/Primality.pm +Copying bin/primes.pl -> blib/script/primes.pl +Copying bin/strong_psuedoprimes.pl -> blib/script/strong_psuedoprimes.pl +# Testing Math::Primality 0.0401, Perl 5.010001, /usr/bin/perl +t/00-load.t ...................... +1..1 +ok 1 - use Math::Primality; +ok +# Failed test '-1 is not prime' +# at t/is_prime.t line 16. +# Looks like you failed 1 test of 573. +t/is_prime.t ..................... +1..6 +ok 1 - is_prime should handle Math::GMPz objects, three is prime +ok 2 - 2 is prime +ok 3 - 1 is not prime +ok 4 - 0 is not prime +not ok 5 - -1 is not prime +ok 6 - blarg +t/boilerplate.t .................. +1..3 +ok 1 - README contains no boilerplate text +ok 2 - Changes contains no boilerplate text +ok 3 - lib/Math/Primality.pm contains no boilerplate text +ok +Test Summary Report +------------------- +t/is_prime.t (Wstat: 256 Tests: 573 Failed: 1) +Failed test: 5 +Non-zero exit status: 1 +Failed 1/11 test programs. 1/2498 subtests failed. +Files=11, Tests=2498, 3 wallclock secs ( 0.20 usr 0.04 sys + 2.99 cusr 0.18 csys = 3.41 CPU) +Result: FAIL + +FOOT +EMAIL + + my $ebody = $email->body; + $ebody =~ s/\r\n/\n/g; + eq_or_diff($ebody, $body, 'email body has failure summary'); + +} diff --git a/t/data/test.yml b/t/data/test.yml new file mode 100644 index 0000000..e4144c3 --- /dev/null +++ b/t/data/test.yml @@ -0,0 +1,30 @@ +layout: "main" +logger: "file" +appname: "jitterbug" + +builds_per_feed: 5 +template: "xslate" +engines: + xslate: + path: / + type: text + cache: 0 + +jitterbug: + reports: + dir: /tmp/jitterbug + build: + dir: /tmp/build + build_process: + builder: ./scripts/capsule.sh + on_failure: ./scripts/build-failed.sh + +plugins: + DBIC: + schema: + skip_automake: 1 + pckg: "jitterbug::Schema" + connect_info: + - dbi:SQLite:dbname=jitterbug.db + + |