[Bps-public-commit] smokingit branch, anna, created. cdda0a472f6d46af0c5c2e7bf05835af202699be
Alex Vandiver
alexmv at bestpractical.com
Tue May 28 12:25:27 EDT 2013
The branch, anna has been created
at cdda0a472f6d46af0c5c2e7bf05835af202699be (commit)
- Log -----------------------------------------------------------------
commit 81db510c53001345a8457bb0a2ba75f84dffed98
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Sun May 26 21:26:33 2013 -0400
Rename branchpoint to first_commit, as that is what it is
diff --git a/lib/Smokingit/Model/Branch.pm b/lib/Smokingit/Model/Branch.pm
index 29d9fba..4b67a76 100644
--- a/lib/Smokingit/Model/Branch.pm
+++ b/lib/Smokingit/Model/Branch.pm
@@ -81,7 +81,6 @@ sub create {
my $tip = $project->sha( delete $args{sha} );
$args{current_commit_id} = $tip->id;
$args{tested_commit_id} = $tip->id;
- $args{first_commit_id} = $tip->id;
$args{owner} = $tip->committer;
my ($ok, $msg) = $self->SUPER::create(%args);
@@ -215,12 +214,15 @@ sub commit_list {
my $self = shift;
local $ENV{GIT_DIR} = $self->project->repository_path;
- my $first = $self->first_commit->sha;
+ my $first = $self->first_commit ? "^".$self->first_commit->sha."~" : "";
my $last = $self->current_commit->sha;
- my @revs = map {chomp; $_} `git rev-list ^$first $last --topo-order --max-count=50`;
+ my @revs = map {chomp; $_} `git rev-list $first $last --topo-order --max-count=50`;
my $left = 50 - @revs; $left = 11 if $left > 11;
- push @revs, map {chomp; $_} `git rev-list $first --topo-order --max-count=$left`
- if $left > 0;
+ if ($self->first_commit) {
+ $first = $self->first_commit->sha . "~";
+ push @revs, map {chomp; $_} `git rev-list $first --topo-order --max-count=$left`
+ if $left > 0;
+ }
my $commits = Smokingit::Model::CommitCollection->new;
$commits->limit( column => "project_id", value => $self->project->id );
@@ -254,17 +256,19 @@ sub commit_list {
return map $commits{$_} || $self->project->sha($_), @revs;
}
-sub branchpoint {
+sub first_commit {
my $self = shift;
- my $max = shift || 100;
- return undef if $self->status eq "master";
- return undef unless $self->to_merge_into->id;
- my $trunk = $self->to_merge_into->current_commit->sha;
- my $tip = $self->current_commit->sha;
+ if ($self->status eq "master") {
+ # No first commit for master branches
+ return undef;
+ }
+
+ my @trunks = map {"^".$_->current_commit->sha} @{$self->project->trunks};
+ my $tip = $self->current_commit->sha;
local $ENV{GIT_DIR} = $self->project->repository_path;
- my @branch = map {chomp; $_} `git rev-list $tip ^$trunk --topo-order --max-count=$max`;
+ my @branch = map {chomp; $_} `git rev-list $tip @trunks --topo-order`;
return unless @branch;
my $commit = $self->project->sha( $branch[-1] );
diff --git a/lib/Smokingit/View/Branch.pm b/lib/Smokingit/View/Branch.pm
index 4191168..47f132b 100644
--- a/lib/Smokingit/View/Branch.pm
+++ b/lib/Smokingit/View/Branch.pm
@@ -48,7 +48,7 @@ template '/branch' => page {
my $project_id = $b->project->id;
my @commits = $b->commit_list;
- my $branchpoint = $b->branchpoint(@commits+1);
+ my $first = $b->first_commit;
div {
id is "branch-commits";
class is "commitlist biglist";
@@ -99,7 +99,7 @@ template '/branch' => page {
}
span {{class is "subject"} $commit->subject };
};
- if ($branchpoint and $branchpoint->id == $commit->id) {
+ if ($first and $first->id == $commit->id) {
div { { class is "branchpoint" } };
}
}
commit 3ebb0fa18b5ca053e8107b7ba11cfba12299e96d
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Sun May 26 21:28:42 2013 -0400
Cache the branchpoint in the helpfully already-existing first_commit_id column
diff --git a/lib/Smokingit/Model/Branch.pm b/lib/Smokingit/Model/Branch.pm
index 4b67a76..e8dabd0 100644
--- a/lib/Smokingit/Model/Branch.pm
+++ b/lib/Smokingit/Model/Branch.pm
@@ -81,6 +81,7 @@ sub create {
my $tip = $project->sha( delete $args{sha} );
$args{current_commit_id} = $tip->id;
$args{tested_commit_id} = $tip->id;
+ $args{first_commit_id} = undef; # Compute lazily
$args{owner} = $tip->committer;
my ($ok, $msg) = $self->SUPER::create(%args);
@@ -259,9 +260,17 @@ sub commit_list {
sub first_commit {
my $self = shift;
- if ($self->status eq "master") {
+ my $id = $self->first_commit_id;
+ if ($id) {
+ my $commit = Smokingit::Model::Commit->new;
+ $commit->load($id);
+ return $commit;
+ } elsif ($self->status eq "master") {
# No first commit for master branches
return undef;
+ } elsif (defined $id and $id == 0) {
+ # 0 means "Not found"
+ return undef;
}
my @trunks = map {"^".$_->current_commit->sha} @{$self->project->trunks};
@@ -272,7 +281,13 @@ sub first_commit {
return unless @branch;
my $commit = $self->project->sha( $branch[-1] );
- return $commit->id ? $commit : undef;
+ if ($commit->id) {
+ $self->set_first_commit_id( $commit->id );
+ return $commit;
+ } else {
+ $self->set_first_commit_id( 0 );
+ return undef;
+ }
}
sub test_status {
commit 7fec656bfbc31576b80c7195d3ee0e9db5daa2f4
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Sun May 26 21:29:19 2013 -0400
Update guess_merge_into to use first_commit, rather than reimplementing it
diff --git a/lib/Smokingit/Model/Branch.pm b/lib/Smokingit/Model/Branch.pm
index e8dabd0..bce24ee 100644
--- a/lib/Smokingit/Model/Branch.pm
+++ b/lib/Smokingit/Model/Branch.pm
@@ -104,30 +104,20 @@ sub create {
sub guess_merge_into {
my $self = shift;
- my @trunks;
- my $branches = $self->project->trunk_or_relengs;
- while (my $b = $branches->next) {
- push @trunks, [$b->id, $b->current_commit->sha, $b->name];
- }
+ my $first = $self->first_commit;
+ return unless $first;
- # Find the commit before the first non-trunk commit, which is the
- # commit this branch was branched off of
- local $ENV{GIT_DIR} = $self->project->repository_path;
- my $topic = $self->current_commit->sha;
- my @revlist = map {chomp; $_} `git rev-list $topic @{[map {"^".$_->[1]} @trunks]}`;
- my $branchpoint;
- if (@revlist) {
- $branchpoint = `git rev-parse $revlist[-1]~`;
- chomp $branchpoint;
- } else {
- $branchpoint = $topic;
- }
+ my ($branchpoint) = $first->parents;
+ return unless $branchpoint;
+ my $branchsha = $branchpoint->sha;
- for my $t (@trunks) {
+ my $trunks = $self->project->trunk_or_relengs;
+ while (my $t = $trunks->next) {
# Find the first trunk which contains all the branch point
- # (i.e. branchpoint - trunk is the empty set)
- next if `git rev-list --max-count=1 $branchpoint ^$t->[1]` =~ /\S/;
- return $t->[0];
+ # (i.e. branchsha - trunk is the empty set)
+ my $sha = $t->current_commit->sha;
+ next if `git rev-list --max-count=1 $branchsha ^$sha` =~ /\S/;
+ return $t->id;
}
return undef;
}
commit 3d652eb5c34f61a06a65d4cef42e471953da8df2
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Mon May 27 13:53:59 2013 -0400
Factor out and improve merge commit check
diff --git a/lib/Smokingit/Model/Commit.pm b/lib/Smokingit/Model/Commit.pm
index 1af2fbe..789a39f 100644
--- a/lib/Smokingit/Model/Commit.pm
+++ b/lib/Smokingit/Model/Commit.pm
@@ -75,6 +75,14 @@ sub short_sha {
return substr($self->sha,0,7);
}
+sub is_merge {
+ my $self = shift;
+ my @parents = $self->parents;
+ return unless @parents > 1;
+ return $1 if $self->subject =~ /^Merge branch '(.*?)'/;
+ return 'Unknown branch';
+}
+
sub run_smoke {
my $self = shift;
my $config = shift;
diff --git a/lib/Smokingit/View/Branch.pm b/lib/Smokingit/View/Branch.pm
index 47f132b..92a3769 100644
--- a/lib/Smokingit/View/Branch.pm
+++ b/lib/Smokingit/View/Branch.pm
@@ -54,8 +54,7 @@ template '/branch' => page {
class is "commitlist biglist";
for my $commit (@commits) {
$commit->hash_results;
- my $merge = $commit->subject =~ /^Merge branch /
- ? "merge" : "nonmerge";
+ my $merge = $commit->is_merge ? "merge" : "nonmerge";
div {
{class is $commit->sha." $merge commit ".$commit->status};
for my $config (@configs) {
commit 3fcb0ecfdbc12bf41c484e6188581cf2a50b639f
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Mon May 27 22:05:09 2013 -0400
Promote "branch contains commit" to a method
Make use of the git 1.8.0 feature of `git merge-base --is-ancestor commit branch`
diff --git a/lib/Smokingit/Model/Branch.pm b/lib/Smokingit/Model/Branch.pm
index bce24ee..cefdedf 100644
--- a/lib/Smokingit/Model/Branch.pm
+++ b/lib/Smokingit/Model/Branch.pm
@@ -3,6 +3,7 @@ use warnings;
package Smokingit::Model::Branch;
use Jifty::DBI::Schema;
+use Scalar::Util qw/blessed/;
use Smokingit::Record schema {
column project_id =>
@@ -109,15 +110,10 @@ sub guess_merge_into {
my ($branchpoint) = $first->parents;
return unless $branchpoint;
- my $branchsha = $branchpoint->sha;
my $trunks = $self->project->trunk_or_relengs;
while (my $t = $trunks->next) {
- # Find the first trunk which contains all the branch point
- # (i.e. branchsha - trunk is the empty set)
- my $sha = $t->current_commit->sha;
- next if `git rev-list --max-count=1 $branchsha ^$sha` =~ /\S/;
- return $t->id;
+ return $t->id if $t->contains($branchpoint);
}
return undef;
}
@@ -201,6 +197,18 @@ sub is_tested {
return $self->status ne "ignore";
}
+sub contains {
+ my $self = shift;
+ my $commit = shift;
+ $commit = $commit->sha if blessed($commit);
+
+ my $tip = $self->current_commit->sha;
+
+ local $ENV{GIT_DIR} = $self->project->repository_path;
+ `git merge-base --is-ancestor $commit $tip`;
+ return not $?;
+}
+
sub commit_list {
my $self = shift;
local $ENV{GIT_DIR} = $self->project->repository_path;
commit d680989832f11dd34e7c904ee4b5f07364eb66d7
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Mon May 27 22:06:03 2013 -0400
Add a method to retrieve completed smoke results for a commit
diff --git a/lib/Smokingit/Model/Commit.pm b/lib/Smokingit/Model/Commit.pm
index 789a39f..a657e62 100644
--- a/lib/Smokingit/Model/Commit.pm
+++ b/lib/Smokingit/Model/Commit.pm
@@ -106,6 +106,15 @@ sub run_smoke {
return $smoke->run_smoke;
}
+sub smoke_results {
+ my $self = shift;
+ my $results = Smokingit::Model::SmokeResultCollection->new;
+ $results->limit( column => "commit_id", value => $self->id );
+ $results->limit( column => "project_id", value => $self->project->id );
+ $results->limit( column => "queue_status", operator => "IS", value => "NULL" );
+ return $results;
+}
+
sub hash_results {
my $self = shift;
my $results = shift || $self->prefetched( "smoke_results" );
commit cdda0a472f6d46af0c5c2e7bf05835af202699be
Author: Alex Vandiver <alexmv at bestpractical.com>
Date: Tue May 28 01:38:54 2013 -0400
anna is a bot
diff --git a/bin/anna b/bin/anna
new file mode 100755
index 0000000..878bba1
--- /dev/null
+++ b/bin/anna
@@ -0,0 +1,17 @@
+#!/usr/bin/env perl
+use YAML;
+
+use strict;
+use warnings;
+
+use Jifty;
+BEGIN { Jifty->new( no_request => 1 ); };
+use Smokingit::IRC;
+
+my %config = %{ Jifty->config->app('irc') || {} };
+unless (%config) {
+ print "IRC is not configured; edit your site_config.yml\n";
+ exit;
+}
+
+Smokingit::IRC->new->run;
diff --git a/lib/Smokingit/IRC.pm b/lib/Smokingit/IRC.pm
new file mode 100644
index 0000000..8d74f2a
--- /dev/null
+++ b/lib/Smokingit/IRC.pm
@@ -0,0 +1,307 @@
+use strict;
+use warnings;
+
+package Smokingit::IRC;
+use String::IRC;
+
+use Moose;
+extends 'IM::Engine';
+
+has '+interface_args' => (
+ required => 0,
+ default => sub {
+ my %config = %{ Jifty->config->app('irc') || {} };
+ return {
+ protocol => 'IRC',
+ credentials => {
+ server => $config{host},
+ port => $config{port},
+ nick => $config{nick} || 'anna',
+ channels => [$config{channel}],
+ },
+ };
+ },
+);
+
+sub BUILD {
+ my $self = shift;
+
+ $self->interface->incoming_callback(
+ sub { $self->incoming(@_) },
+ );
+
+ $self->interface->irc->reg_cb(
+ registered => sub {
+ my $sub = Jifty->bus->new_listener;
+ $sub->subscribe(Jifty->bus->topic("test_result"));
+ $sub->poll( sub { $self->test_progress(@_) } );
+ },
+ );
+}
+
+sub error_reply {
+ my($incoming, $msg) = @_;
+ return $incoming->reply(
+ String::IRC->new( $msg )->maroon->stringify,
+ );
+}
+
+sub incoming {
+ my $self = shift;
+ my $incoming = shift;
+ Jifty::Record->flush_cache if Jifty::Record->can('flush_cache');
+
+ # Skip messages from the system
+ if ($incoming->sender->name =~ /\./) {
+ warn $incoming->sender->name . ": " .
+ $incoming->message;
+ return;
+ } elsif ($incoming->command eq "NOTICE") {
+ # NOTICE's are required to never trigger auto-replies
+ return;
+ }
+
+ my $msg = $incoming->message;
+ my $nick = $self->interface->irc->nick;
+ return if $incoming->isa("IM::Engine::Incoming::IRC::Channel")
+ and not $msg =~ s/^\s*$nick(?:\s*[:,])?\s*(?:please\s+)?//i;
+
+ if ($msg =~ /^retest\s+(.*)/) {
+ return $self->do_retest($incoming, $1);
+ } elsif ($msg =~ /^status\s+(?:of\s+)?(.*)/) {
+ return $self->do_status($incoming, $1);
+ } elsif ($msg =~ /^(?:re)?sync(?:\s+(.*))?/) {
+ return $self->do_sync($incoming, $1);
+ } else {
+ return $incoming->reply( "What?" );
+ }
+}
+
+sub do_retest {
+ my $self = shift;
+ my ($incoming, $what) = @_;
+ my $action = Smokingit::Action::Test->new(
+ current_user => Smokingit::CurrentUser->superuser,
+ arguments => { commit => $what },
+ );
+ $action->validate;
+ return error_reply(
+ $incoming => $action->result->field_error("commit"),
+ ) unless $action->result->success;
+
+ $action->run;
+ return error_reply(
+ $incoming => $action->result->error,
+ ) if $action->result->error;
+
+ return $incoming->reply( $action->result->message );
+}
+
+sub do_status {
+ my $self = shift;
+ my ($incoming, $what) = @_;
+ if ($what =~ /^\s*([a-fA-F0-9]{5,})\s*$/) {
+ my $commits = Smokingit::Model::CommitCollection->new;
+ $commits->limit( column => "sha", operator => "like", value => "$what%" );
+ my @matches = @{ $commits->items_array_ref };
+ if (not @matches) {
+ return error_reply(
+ $incoming => "No such SHA!"
+ );
+ } elsif (@matches > 0) {
+ return error_reply(
+ $incoming => "Found ".(@matches+0)." matching SHAs!",
+ );
+ }
+
+ $what = $matches[0];
+ } else {
+ my ($project, $branch) = $what =~ /^\s*(?:(\S+):)?(\S+)\s*$/;
+ my $branches = Smokingit::Model::BranchCollection->new;
+ $branches->limit( column => "name", value => "$branch" );
+
+ if ($project) {
+ my $project_obj = Smokingit::Model::Project->new;
+ $project_obj->load_by_cols( name => $project );
+ if (not $project_obj->id) {
+ return error_reply(
+ $incoming => "No such project '$project'!",
+ );
+ }
+ $branches->limit( column => "project_id", value => $project_obj->id );
+ }
+
+ my @matches = @{ $branches->items_array_ref };
+ if (not @matches) {
+ return error_reply(
+ $incoming => "No branch '$branch' found",
+ );
+ } elsif (@matches > 1) {
+ return error_reply(
+ $incoming => "Found '$branch' in ".
+ join(", ", map {$_->project->name} @matches).
+ ". Try, $matches[0]:$branch"
+ );
+ }
+
+ # Need to re-parse if this got any updates
+ return $self->do_status($incoming, $what)
+ if $matches[0]->project->as_superuser->sync;
+
+ $what = $matches[0]->current_commit;
+ }
+ return $incoming->reply( $what->short_sha . " is " . $what->status );
+}
+
+sub do_sync {
+ my $self = shift;
+ my ($incoming, $what) = @_;
+
+ if ($what) {
+ my $project = Smokingit::Model::Project->new;
+ $project->load_by_cols( name => $what );
+ if (not $project->id) {
+ return error_reply(
+ $incoming => "No such project '$what'!",
+ );
+ }
+ my @results = $project->sync;
+ return $incoming->reply("No changes") unless @results;
+ return $incoming->reply(join("; ", @results));
+ } else {
+ my $projects = Smokingit::Model::ProjectCollection->new;
+ $projects->unlimit;
+ while (my $p = $projects->next) {
+ $p->sync;
+ }
+ return $incoming->reply("Synchronized ".$projects->count." projects");
+ }
+}
+
+sub test_progress {
+ my $self = shift;
+ my $msg = shift;
+ Jifty::Record->flush_cache if Jifty::Record->can('flush_cache');
+
+ eval {
+ my %message = %{ $msg };
+
+ my $smoke = Smokingit::Model::SmokeResult->new;
+ $smoke->load( $message{smoke_id} );
+ return unless $smoke->id;
+
+ my $message = $self->do_analyze($smoke);
+ return unless $message;
+
+ my $out = IM::Engine::Outgoing::IRC::Channel->new(
+ channel => Jifty->config->app('irc')->{channel},
+ message => $message,
+ command => "NOTICE",
+ );
+ $self->interface->send_message($out);
+ };
+ warn "$@" if $@;
+}
+
+sub do_analyze {
+ my $self = shift;
+ my ($smoke) = @_;
+
+ my $commit = $smoke->commit;
+ my $project = $smoke->project;
+
+ warn "Got test result for ".$commit->short_sha;
+
+ # First off, have we tested all configurations?
+ return unless $commit->smoke_results->count == $project->configurations->count;
+
+ warn "Have tested all configs";
+
+ # See if we can find the branch for this commit
+ my $branch = Smokingit::Model::Branch->new;
+ $branch->load_by_cols(
+ project_id => $project->id,
+ name => $smoke->branch_name,
+ );
+ my $branchname = $branch->name;
+ return unless $branch->id;
+
+ # Make sure the branch actually still contains the commit
+ return unless $branch->contains($commit);
+
+ my $author = $commit->author;
+ $author = $1 if $author =~ /<(.*?)@/;
+
+ # If this is the first commit on the branch, _or_ we haven't tested
+ # some configuration of each parent of this commit, then this is
+ # first news we have of the branch.
+ my @tested_parents = grep {$_->smoke_results->count} $commit->parents;
+ if (($branch->first_commit and $commit->sha eq $branch->first_commit->sha)
+ or not @tested_parents) {
+ if ($commit->status eq "passing") {
+ return "New branch '$branchname' " .
+ String::IRC->new("passes tests")->green;
+ } else {
+ return "$author pushed a new branch '$branchname' which is " .
+ String::IRC->new("failing tests")->red;
+ }
+ } elsif ($commit->is_merge){
+ my $mergename = $commit->is_merge;
+ if ($commit->status eq "passing") {
+ return "Merged '$mergename' into '$branchname', " .
+ String::IRC->new("passes tests")->green;
+ }
+
+ # So the merge commit is fail: there are four possibilities,
+ # based on which of trunk/branch were passing previous to the
+ # commit. We assume here that there are no octopus commits.
+ my ($trunk_commit, $branch_commit) = $commit->parents;
+ my $trunk_good = $trunk_commit->status eq "passing";
+ my $branch_good = $branch_commit->status eq "passing";
+
+ if ($trunk_good and $branch_good) {
+ return "$author merged '$mergename' into '$branchname', which is " .
+ String::IRC->new("failing tests")->red .
+ ", although both parents were passing";
+ } elsif ($trunk_good and not $branch_good) {
+ return "$author merged '$mergename' (".
+ String::IRC->new("failing tests")->red.
+ ") into '$branchname', which is now ".
+ String::IRC->new("failing tests")->red;
+ } elsif (not $trunk_good and not $branch_good) {
+ return "$author merged '$mergename' (".
+ String::IRC->new("failing tests")->red.
+ ") into '$branchname', which is still ".
+ String::IRC->new("failing tests")->red;
+ } else {
+ return "$author merged '$mergename'".
+ " into '$branchname', which is still ".
+ String::IRC->new("failing tests")->red;
+ }
+ } elsif ($commit->status ne "passing") {
+ # A new commit on an existing branch, which fails tests. Let's
+ # check if this is better or worse than the previous commit.
+ if (@tested_parents == grep {$_->status eq "passing"} @tested_parents) {
+ return "'$branchname' by $author began ".
+ String::IRC->new("failing tests")->red .
+ " as of ".$commit->short_sha;
+ } else {
+ # Was failing, still failing? Let's not spam about it
+ return;
+ }
+ } elsif (grep {$_->status ne "passing"} @tested_parents) {
+ # A new commit on an existing branch, which passes tests but
+ # whose parents didn't!
+ return "'$branchname' by $author ".
+ String::IRC->new("passes tests")->green .
+ " as of ".$commit->short_sha;
+ } else {
+ # A commit which passes, and whose parents all passed. Go them?
+ return;
+ }
+}
+
+__PACKAGE__->meta->make_immutable;
+no Moose;
+
+1;
-----------------------------------------------------------------------
More information about the Bps-public-commit
mailing list