summaryrefslogtreecommitdiffstats
path: root/extensions/TryAutoLand/Extension.pm
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/TryAutoLand/Extension.pm')
-rw-r--r--extensions/TryAutoLand/Extension.pm323
1 files changed, 323 insertions, 0 deletions
diff --git a/extensions/TryAutoLand/Extension.pm b/extensions/TryAutoLand/Extension.pm
new file mode 100644
index 000000000..40dbb70d9
--- /dev/null
+++ b/extensions/TryAutoLand/Extension.pm
@@ -0,0 +1,323 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::TryAutoLand;
+
+use strict;
+
+use base qw(Bugzilla::Extension);
+
+use Bugzilla::Bug;
+use Bugzilla::Attachment;
+use Bugzilla::User;
+use Bugzilla::Util qw(trick_taint diff_arrays);
+use Bugzilla::Error;
+
+use Bugzilla::Extension::TryAutoLand::Constants;
+
+our $VERSION = '0.01';
+
+BEGIN {
+ *Bugzilla::Bug::autoland_branches = \&_autoland_branches;
+ *Bugzilla::Bug::autoland_try_syntax = \&_autoland_try_syntax;
+ *Bugzilla::Attachment::autoland_checked = \&_autoland_attachment_checked;
+ *Bugzilla::Attachment::autoland_who = \&_autoland_attachment_who;
+ *Bugzilla::Attachment::autoland_status = \&_autoland_attachment_status;
+ *Bugzilla::Attachment::autoland_status_when = \&_autoland_attachment_status_when;
+ *Bugzilla::Attachment::autoland_update_status = \&_autoland_attachment_update_status;
+ *Bugzilla::Attachment::autoland_remove = \&_autoland_attachment_remove;
+}
+
+sub db_schema_abstract_schema {
+ my ($self, $args) = @_;
+ $args->{'schema'}->{'autoland_branches'} = {
+ FIELDS => [
+ bug_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ PRIMARYKEY => 1,
+ REFERENCES => {
+ TABLE => 'bugs',
+ COLUMN => 'bug_id',
+ DELETE => 'CASCADE'
+ }
+ },
+ branches => {
+ TYPE => 'VARCHAR(255)',
+ NOTNULL => 1
+ },
+ try_syntax => {
+ TYPE => 'VARCHAR(255)',
+ NOTNULL => 1,
+ DEFAULT => "''",
+ }
+ ],
+ };
+
+ $args->{'schema'}->{'autoland_attachments'} = {
+ FIELDS => [
+ attach_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ PRIMARYKEY => 1,
+ REFERENCES => {
+ TABLE => 'attachments',
+ COLUMN => 'attach_id',
+ DELETE => 'CASCADE'
+ },
+ },
+ who => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {
+ TABLE => 'profiles',
+ COLUMN => 'userid',
+ },
+ },
+ status => {
+ TYPE => 'varchar(64)',
+ NOTNULL => 1
+ },
+ status_when => {
+ TYPE => 'DATETIME',
+ NOTNULL => 1,
+ },
+ ],
+ };
+}
+
+sub install_update_db {
+ my ($self) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ if (!$dbh->bz_column_info('autoland_branches', 'try_syntax')) {
+ $dbh->bz_add_column('autoland_branches', 'try_syntax', {
+ TYPE => 'VARCHAR(255)',
+ NOTNULL => 1,
+ DEFAULT => "''",
+ });
+ }
+}
+
+sub _autoland_branches {
+ my $self = shift;
+ return $self->{'autoland_branches'} if exists $self->{'autoland_branches'};
+ _preload_bug_data($self);
+ return $self->{'autoland_branches'};
+}
+
+sub _autoland_try_syntax {
+ my $self = shift;
+ return $self->{'autoland_try_syntax'} if exists $self->{'autoland_try_syntax'};
+ _preload_bug_data($self);
+ return $self->{'autoland_try_syntax'};
+}
+
+sub _preload_bug_data {
+ my ($self) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $result = $dbh->selectrow_hashref("SELECT branches, try_syntax FROM autoland_branches
+ WHERE bug_id = ?", { Slice => {} }, $self->id);
+ if ($result) {
+ $self->{'autoland_branches'} = $result->{'branches'};
+ $self->{'autoland_try_syntax'} = $result->{'try_syntax'};
+ }
+ else {
+ $self->{'autoland_branches'} = undef;
+ $self->{'autoland_try_syntax'} = undef;
+ }
+}
+
+sub _autoland_attachment_checked {
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
+ return $self->{'autoland_checked'} if exists $self->{'autoland_checked'};
+ my $result = $dbh->selectrow_hashref("SELECT who, status, status_when
+ FROM autoland_attachments
+ WHERE attach_id = ?", { Slice => {} }, $self->id);
+ if ($result) {
+ $self->{'autoland_checked'} = 1;
+ $self->{'autoland_who'} = Bugzilla::User->new($result->{'who'});
+ $self->{'autoland_status'} = $result->{'status'};
+ $self->{'autoland_status_when'} = $result->{'status_when'};
+ }
+ else {
+ $self->{'autoland_checked'} = 0;
+ $self->{'autoland_who'} = undef;
+ $self->{'autoland_status'} = undef;
+ $self->{'autoland_status_when'} = undef;
+ }
+ return $self->{'autoland_checked'};
+}
+
+sub _autoland_attachment_who {
+ my $self = shift;
+ return undef if !$self->autoland_checked;
+ return $self->{'autoland_who'};
+}
+
+sub _autoland_attachment_status {
+ my $self = shift;
+ return undef if !$self->autoland_checked;
+ return $self->{'autoland_status'};
+}
+
+sub _autoland_attachment_status_when {
+ my $self = shift;
+ return undef if !$self->autoland_checked;
+ return $self->{'autoland_status_when'};
+}
+
+sub _autoland_attachment_update_status {
+ my ($self, $status) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ return undef if !$self->autoland_checked;
+
+ grep($_ eq $status, VALID_STATUSES)
+ || ThrowUserError('autoland_invalid_status',
+ { status => $status,
+ valid => [ VALID_STATUSES ] });
+
+ if ($self->autoland_status ne $status) {
+ my $timestamp = $dbh->selectrow_array("SELECT LOCALTIMESTAMP(0)");
+ trick_taint($status);
+ $dbh->do("UPDATE autoland_attachments SET status = ?, status_when = ?
+ WHERE attach_id = ?", undef, $status, $timestamp, $self->id);
+ $self->{'autoland_status'} = $status;
+ $self->{'autoland_status_when'} = $timestamp;
+ }
+
+ return 1;
+}
+
+sub _autoland_attachment_remove {
+ my ($self) = @_;
+ my $dbh = Bugzilla->dbh;
+ return undef if !$self->autoland_checked;
+ $dbh->do("DELETE FROM autoland_attachments WHERE attach_id = ?", undef, $self->id);
+ delete $self->{'autoland_checked'};
+ delete $self->{'autoland_who'};
+ delete $self->{'autoland_status'};
+ delete $self->{'autoland_status_when'};
+}
+
+sub object_end_of_update {
+ my ($self, $args) = @_;
+ my $object = $args->{'object'};
+ my $user = Bugzilla->user;
+ my $dbh = Bugzilla->dbh;
+ my $cgi = Bugzilla->cgi;
+ my $params = Bugzilla->input_params;
+
+ return if !$user->in_group('autoland');
+
+ if ($object->isa('Bugzilla::Bug')) {
+ # First make any needed changes to the branches and try_syntax fields
+ my $bug_id = $object->bug_id;
+ my $bug_result = $dbh->selectrow_hashref("SELECT branches, try_syntax
+ FROM autoland_branches
+ WHERE bug_id = ?",
+ { Slice => {} }, $bug_id);
+
+ my $old_branches = '';
+ my $old_try_syntax = '';
+ if ($bug_result) {
+ $old_branches = $bug_result->{'branches'};
+ $old_try_syntax = $bug_result->{'try_syntax'};
+ }
+
+ my $new_branches = $params->{'autoland_branches'} || '';
+ my $new_try_syntax = $params->{'autoland_try_syntax'} || '';
+
+ my $set_attachments = [];
+ if (ref $params->{'autoland_attachments'}) {
+ $set_attachments = $params->{'autoland_attachments'};
+ } elsif ($params->{'autoland_attachments'}) {
+ $set_attachments = [ $params->{'autoland_attachments'} ];
+ }
+
+ # Check for required values
+ (!$new_branches && @{$set_attachments})
+ && ThrowUserError('autoland_empty_branches');
+ ($new_branches && !$new_try_syntax)
+ && ThrowUserError('autoland_empty_try_syntax');
+
+ trick_taint($new_branches);
+ if (!$new_branches && $old_branches) {
+ $dbh->do("DELETE FROM autoland_branches WHERE bug_id = ?",
+ undef, $bug_id);
+ }
+ elsif ($new_branches && !$old_branches) {
+ $dbh->do("INSERT INTO autoland_branches (bug_id, branches)
+ VALUES (?, ?)", undef, $bug_id, $new_branches);
+ }
+ elsif ($old_branches ne $new_branches) {
+ $dbh->do("UPDATE autoland_branches SET branches = ? WHERE bug_id = ?",
+ undef, $new_branches, $bug_id);
+ }
+
+ trick_taint($new_try_syntax);
+ if (($old_try_syntax ne $new_try_syntax) && $new_branches) {
+ $dbh->do("UPDATE autoland_branches SET try_syntax = ? WHERE bug_id = ?",
+ undef, $new_try_syntax, $bug_id);
+ }
+
+ # Next make any changes needed to each of the attachments.
+ # 1. If an attachment is checked it has a row in the table, if
+ # there is no row in the table it is not checked.
+ # 2. Do not allow changes to checked state if status == 'running' or status == 'waiting'
+ my $check_attachments = ref $params->{'defined_autoland_attachments'}
+ ? $params->{'defined_autoland_attachments'}
+ : [ $params->{'defined_autoland_attachments'} ];
+ my ($removed_attachments) = diff_arrays($check_attachments, $set_attachments);
+ foreach my $attachment (@{$object->attachments}) {
+ next if !$attachment->ispatch;
+ my $attach_id = $attachment->id;
+
+ my $checked = (grep $_ == $attach_id, @$set_attachments) ? 1 : 0;
+ my $unchecked = (grep $_ == $attach_id, @$removed_attachments) ? 1 : 0;
+ my $old_checked = $dbh->selectrow_array("SELECT 1 FROM autoland_attachments
+ WHERE attach_id = ?", undef, $attach_id) || 0;
+
+ next if $checked && $old_checked;
+
+ if ($unchecked && $old_checked && $attachment->autoland_status =~ /^(failed|success)$/) {
+ $dbh->do("DELETE FROM autoland_attachments WHERE attach_id = ?", undef, $attach_id);
+ }
+ elsif ($checked && !$old_checked) {
+ $dbh->do("INSERT INTO autoland_attachments (attach_id, who, status, status_when)
+ VALUES (?, ?, 'waiting', now())", undef, $attach_id, $user->id);
+ }
+ }
+
+ }
+}
+
+sub template_before_process {
+ my ($self, $args) = @_;
+ my $file = $args->{'file'};
+ my $vars = $args->{'vars'};
+
+ # in the header we just need to set the var to ensure the css gets included
+ if ($file eq 'bug/show-header.html.tmpl' && Bugzilla->user->in_group('autoland') ) {
+ $vars->{'autoland'} = 1;
+ }
+
+ if ($file eq 'bug/edit.html.tmpl') {
+ $vars->{'autoland_default_try_syntax'} = DEFAULT_TRY_SYNTAX;
+ }
+}
+
+sub webservice {
+ my ($self, $args) = @_;
+
+ my $dispatch = $args->{dispatch};
+ $dispatch->{TryAutoLand} = "Bugzilla::Extension::TryAutoLand::WebService";
+}
+
+__PACKAGE__->NAME;