about summary refs log tree commit diff
diff options
context:
space:
mode:
authorfranck cuny <franck@lumberjaph.net>2011-02-13 16:20:46 +0100
committerfranck cuny <franck@lumberjaph.net>2011-02-13 16:20:46 +0100
commit7ba102a0b05decfeb046e541d0f76902a071e9d7 (patch)
tree898fd14726300d335cdaa11d38b2de868d3eb29d
parentresolve merge conflict (diff)
parentadd more tests (diff)
downloadjitterbug-7ba102a0b05decfeb046e541d0f76902a071e9d7.tar.gz
Merge branch 'feature/stack_builds' into devel devel
* feature/stack_builds:
  add more tests
  inside the hook, we check if we can add more than one task for this project
  add configuration option to skip some branches; add tests for the Hook
  that's why we want datetime
  use DateTime
-rw-r--r--lib/jitterbug/Builder.pm5
-rw-r--r--lib/jitterbug/Hook.pm125
-rw-r--r--lib/jitterbug/Schema.pm2
-rw-r--r--lib/jitterbug/Schema/Result/Task.pm1
-rw-r--r--t/003_hook_route.t213
5 files changed, 271 insertions, 75 deletions
diff --git a/lib/jitterbug/Builder.pm b/lib/jitterbug/Builder.pm
index a836f70..1f201e2 100644
--- a/lib/jitterbug/Builder.pm
+++ b/lib/jitterbug/Builder.pm
@@ -3,6 +3,7 @@ package jitterbug::Builder;
 use strict;
 use warnings;
 
+use DateTime;
 use YAML qw/LoadFile Dump/;
 use JSON;
 use File::Path qw/rmtree/;
@@ -85,7 +86,9 @@ sub run_task {
     my $desc   = JSON::decode_json( $task->commit->content );
     my $conf   = $self->{'conf'};
 
-    $desc->{'build'}{'start_time'} = time();
+    my $dt = DateTime->now();
+    $task->update({started_when => $dt});
+    $desc->{'build'}{'start_time'} = $dt->epoch;
     debug("Build Start");
 
     my $report_path = dir(
diff --git a/lib/jitterbug/Hook.pm b/lib/jitterbug/Hook.pm
index 87cfba2..fe27921 100644
--- a/lib/jitterbug/Hook.pm
+++ b/lib/jitterbug/Hook.pm
@@ -19,29 +19,21 @@ post '/' => sub {
 
     $payload = from_json($payload);
     my $repo = $payload->{repository}->{name};
+    my $ref  = $payload->{ref};
+
+    if ( !_authorized_branch( $repo, $ref ) ) {
+        debug("this branch is not authorized");
+        status 200;
+        return;
+    }
 
     my $project = schema->resultset('Project')->find( { name => $repo } );
+    $project = _create_new_project( $repo, $payload ) if !$project;
 
-    if ( !$project ) {
-        debug("need to create a new project");
-        try {
-            schema->txn_do(
-                sub {
-                    $project = schema->resultset('Project')->create(
-                        {
-                            name => $repo,
-                            url  => $payload->{repository}->{url},
-                            description =>
-                              $payload->{repository}->{description},
-                            owner => to_json($payload->{repository}->{owner}),
-                        }
-                    );
-                }
-            );
-        }
-        catch {
-            error($_);
-        };
+    if ( !_slot_available_for_task( $project->id ) ) {
+        debug("task already present for this project");
+        status 200;
+        return;
     }
 
     my $last_commit = pop @{ $payload->{commits} };
@@ -49,15 +41,85 @@ post '/' => sub {
     $last_commit->{pusher}  = $payload->{pushed};
     $last_commit->{ref}     = $payload->{ref};
 
+    _insert_commit($last_commit, $project);
+    _insert_new_task( $last_commit, $project );
+
+    debug("hook accepted");
+
+    { updated => $repo };
+};
+
+sub _authorized_branch {
+    my ($repo, $ref) = @_;
+    my $jtbg_conf     = setting 'jitterbug';
+    my $branches_conf = $jtbg_conf->{branches};
+
+    foreach my $name ($repo, 'jt_global') {
+        if ( defined $branches_conf->{$name} ) {
+            return 0 if _should_skip( $ref, $branches_conf->{$name} );
+        }
+    }
+    return 1;
+}
+
+sub _should_skip {
+    my ( $ref, $conf ) = @_;
+    foreach my $br_name (@$conf) {
+        return 1 if $ref =~ m!^refs/heads/$br_name!;
+    }
+    return 0;
+}
+
+sub _create_new_project {
+    my ($repo, $payload) = @_;
+
+    debug("need to create a new project");
+
+    my $project;
+    try {
+        schema->txn_do(
+            sub {
+                $project = schema->resultset('Project')->create(
+                    {
+                        name        => $repo,
+                        url         => $payload->{repository}->{url},
+                        description => $payload->{repository}->{description},
+                        owner => to_json( $payload->{repository}->{owner} ),
+                    }
+                );
+            }
+        );
+    }
+    catch {
+        error($_);
+    };
+    return $project;
+}
+
+sub _slot_available_for_task {
+    my $project_id = shift;
+
+    # is there already a task for this project, and could we stack ?
+    my $jtbg_settings = setting('jitterbug') || {};
+    my $stack_option = $jtbg_settings->{options}->{stack_tasks};
+    my $total_task =
+      schema->resultset('Task')->search( { projectid => $project_id } )->count;
+
+    ( $total_task && !$stack_option) ? return 0 : return 1;
+}
+
+sub _insert_commit {
+    my ($commit, $project) = @_;
+
     try {
         schema->txn_do(
             sub {
                 schema->resultset('Commit')->create(
                     {
-                        sha256    => $last_commit->{id},
-                        content   => to_json($last_commit),
+                        sha256    => $commit->{id},
+                        content   => to_json($commit),
                         projectid => $project->projectid,
-                        timestamp => $last_commit->{timestamp},
+                        timestamp => $commit->{timestamp},
                     }
                 );
             }
@@ -66,22 +128,25 @@ post '/' => sub {
     catch {
         error($_);
     };
+}
 
+sub _insert_new_task {
+    my ( $commit, $project ) = @_;
     try {
         schema->txn_do(
             sub {
                 schema->resultset('Task')->create(
-                    {sha256 => $last_commit->{id}, projectid => $project->projectid}
+                    {
+                        sha256    => $commit->{id},
+                        projectid => $project->projectid
+                    }
                 );
             }
         );
-    }catch{
+    }
+    catch {
         error($_);
     };
-
-    debug("hook accepted");
-
-    { updated => $repo };
-};
+}
 
 1;
diff --git a/lib/jitterbug/Schema.pm b/lib/jitterbug/Schema.pm
index f47d7e9..c671d78 100644
--- a/lib/jitterbug/Schema.pm
+++ b/lib/jitterbug/Schema.pm
@@ -1,7 +1,7 @@
 package jitterbug::Schema;
 use base qw/DBIx::Class::Schema/;
 
-our $VERSION = '3';
+our $VERSION = '4';
 
 __PACKAGE__->load_namespaces();
 
diff --git a/lib/jitterbug/Schema/Result/Task.pm b/lib/jitterbug/Schema/Result/Task.pm
index 2eaed62..ad880d8 100644
--- a/lib/jitterbug/Schema/Result/Task.pm
+++ b/lib/jitterbug/Schema/Result/Task.pm
@@ -25,7 +25,6 @@ __PACKAGE__->add_columns(
 );
 
 __PACKAGE__->set_primary_key('taskid');
-__PACKAGE__->add_unique_constraint( [qw/projectid/] );
 __PACKAGE__->add_unique_constraint( [qw/sha256/] );
 __PACKAGE__->belongs_to(
     project => 'jitterbug::Schema::Result::Project',
diff --git a/t/003_hook_route.t b/t/003_hook_route.t
index 72a841f..11c7af4 100644
--- a/t/003_hook_route.t
+++ b/t/003_hook_route.t
@@ -1,44 +1,173 @@
-use Test::More tests => 1;
+use Test::More tests => 13;
 use strict;
 use warnings;
-ok 1;
-#use jitterbug;
-#use JSON;
-#use YAML qw/LoadFile/;
-#use Dancer::Test;
-#use Dancer::Config qw/setting/;
-
-#my $content = LoadFile('t/data/test.yaml');
-
-#setting jitterbug => { namespace => 'jitterbug_test' };
-
-#route_exists [ POST => '/hook/' ], 'a route handle is defined for /';
-
-#my $response;
-
-#{
-    #$response = dancer_response( POST => '/hook', );
-    #is $response->{status}, 200, '200 with empty post';
-#}
-
-#{
-    #my $payload = "payload=" . JSON::encode_json($content);
-    ##open my $in, '<', \$payload;
-
-    ##$ENV{'CONTENT_LENGTH'} = length($payload);
-    ##$ENV{'CONTENT_TYPE'}   = 'application/x-www-form-urlencoded';
-    ##$ENV{'psgi.input'}     = $in;
-
-    #$response = dancer_response(
-        #POST => '/hook/',
-        #{
-            #headers =>
-              #[ 'Content-Type' => 'application/x-www-form-urlencoded' ],
-            #body => $payload
-        #}
-    #);
-##use YAML::Syck; warn Dump $response;
-    #is $response->{status}, 200;
-    #is_deeply JSON::decode_json( $response->{content} ),
-      #{ updated => 'Dancer' };
-#}
+
+use jitterbug;
+use jitterbug::Schema;
+
+use JSON;
+use YAML qw/LoadFile Dump/;
+
+use File::Spec;
+use File::Temp qw/tempdir/;
+
+use Dancer::Test;
+use Dancer::Config qw/setting/;
+
+my $content = LoadFile('t/data/test.yaml');
+
+my $db_dir = tempdir( CLEANUP => 1 );
+my $db_file = File::Spec->catfile( $db_dir, 'jitterbug.db' );
+my $dsn     = 'dbi:SQLite:dbname=' . $db_file;
+my $schema  = jitterbug::Schema->connect($dsn);
+$schema->deploy;
+
+setting plugins => {
+    DBIC => {
+        schema => {
+            skip_automake => 1,
+            pckg          => "jitterbug::Schema",
+            connect_info  => [$dsn]
+        }
+    }
+};
+
+route_exists [ POST => '/hook/' ], 'a route handle is defined for /';
+
+my $response;
+
+{
+    $response = dancer_response( POST => '/hook', );
+    is $response->{status}, 200, '200 with empty post';
+}
+
+{
+    my $rs = $schema->resultset('Project')->find( { name => 'Dancer' } );
+    ok !defined $rs, 'no project dancer yet';
+
+    $response = dancer_response(
+        POST => '/hook/',
+        {
+            headers =>
+              [ 'Content-Type' => 'application/x-www-form-urlencoded' ],
+            body => _generate_post_request($content),
+        }
+    );
+
+    is $response->{status}, 200, 'status OK with payload';
+    is_deeply JSON::decode_json( $response->{content} ),
+      { updated => 'Dancer' }, 'response OK with payload';
+
+    $rs = $schema->resultset('Project')->find( { name => 'Dancer' } );
+    ok $rs, 'project exists in DB';
+    is $rs->name, 'Dancer', 'project\'s name is good';
+
+    is $schema->resultset('Task')->search()->count(), 1, 'one task created';
+}
+
+{
+    $schema->resultset('Project')->search()->delete();
+    $schema->resultset('Task')->search()->delete();
+
+    # testing with invalid global branch
+    setting jitterbug => { branches => { jt_global => ['foo'], }, };
+    $content->{ref} = 'refs/heads/foo';
+    $response = dancer_response(
+        POST => '/hook/',
+        {
+            headers =>
+              [ 'Content-Type' => 'application/x-www-form-urlencoded' ],
+            body => _generate_post_request($content),
+        }
+    );
+    is $schema->resultset('Task')->search()->count(), 0, 'no task created since this branch is forbiden';
+}
+
+{
+    $schema->resultset('Project')->search()->delete();
+    $schema->resultset('Task')->search()->delete();
+
+    # testing with invalid global branch
+    setting jitterbug => { branches => { Dancer => ['foo'], }, };
+    $content->{ref} = 'refs/heads/foo';
+    $response = dancer_response(
+        POST => '/hook/',
+        {
+            headers =>
+              [ 'Content-Type' => 'application/x-www-form-urlencoded' ],
+            body => _generate_post_request($content),
+        }
+    );
+    is $schema->resultset('Task')->search()->count(), 0, 'no task created since this branch is forbiden';
+}
+
+{
+    $schema->resultset('Project')->search()->delete();
+    $schema->resultset('Task')->search()->delete();
+
+    # this branch is forbiden for another project
+    setting jitterbug => { branches => { jitterbug => ['foo'], }, };
+    $content->{ref} = 'refs/heads/foo';
+    $response = dancer_response(
+        POST => '/hook/',
+        {
+            headers =>
+              [ 'Content-Type' => 'application/x-www-form-urlencoded' ],
+            body => _generate_post_request($content),
+        }
+    );
+    is $schema->resultset('Task')->search()->count(), 1, 'one task created since this branch is authorized for this project';
+}
+
+{
+    $schema->resultset('Project')->search()->delete();
+    $schema->resultset('Task')->search()->delete();
+
+    # this branch is forbiden for another project
+    setting jitterbug => { options => { stack_tasks => 0 } };
+    for ( 1 .. 2 ) {
+        $content->{commits}->[0]->{id} = $_;
+        $response = dancer_response(
+            POST => '/hook/',
+            {
+                headers =>
+                  [ 'Content-Type' => 'application/x-www-form-urlencoded' ],
+                body => _generate_post_request($content),
+            }
+        );
+    }
+    is $schema->resultset('Task')->search()->count(), 1,
+      'can\'t stack tasks for this project';
+}
+
+{
+    $schema->resultset('Project')->search()->delete();
+    $schema->resultset('Task')->search()->delete();
+
+    # this branch is forbiden for another project
+    setting jitterbug => { options => { stack_tasks => 1 } };
+    for ( 1 .. 2 ) {
+        $content->{commits}->[0]->{id} = $_;
+        $response = dancer_response(
+            POST => '/hook/',
+            {
+                headers =>
+                  [ 'Content-Type' => 'application/x-www-form-urlencoded' ],
+                body => _generate_post_request($content),
+            }
+        );
+    }
+    is $schema->resultset('Task')->search()->count(), 2,
+      'can stack tasks for this project';
+}
+
+sub _generate_post_request {
+    my $content = shift;
+    my $payload = "payload=" . JSON::encode_json($content);
+    open my $in, '<', \$payload;
+
+    $ENV{'CONTENT_LENGTH'} = length($payload);
+    $ENV{'CONTENT_TYPE'}   = 'application/x-www-form-urlencoded';
+    $ENV{'psgi.input'}     = $in;
+    return $payload;
+}