summaryrefslogtreecommitdiffstats
path: root/extensions/PhabBugz/lib/Feed.pm
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/PhabBugz/lib/Feed.pm')
-rw-r--r--extensions/PhabBugz/lib/Feed.pm1206
1 files changed, 596 insertions, 610 deletions
diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm
index 71e6aa827..f199a96aa 100644
--- a/extensions/PhabBugz/lib/Feed.pm
+++ b/extensions/PhabBugz/lib/Feed.pm
@@ -26,7 +26,8 @@ use Bugzilla::Field;
use Bugzilla::Logging;
use Bugzilla::Mailer;
use Bugzilla::Search;
-use Bugzilla::Util qw(diff_arrays format_time with_writable_database with_readonly_database);
+use Bugzilla::Util
+ qw(diff_arrays format_time with_writable_database with_readonly_database);
use Bugzilla::Types qw(:types);
use Bugzilla::Extension::PhabBugz::Types qw(:types);
use Bugzilla::Extension::PhabBugz::Constants;
@@ -34,663 +35,661 @@ use Bugzilla::Extension::PhabBugz::Policy;
use Bugzilla::Extension::PhabBugz::Revision;
use Bugzilla::Extension::PhabBugz::User;
use Bugzilla::Extension::PhabBugz::Util qw(
- create_revision_attachment
- get_bug_role_phids
- is_attachment_phab_revision
- request
- set_phab_user
+ create_revision_attachment
+ get_bug_role_phids
+ is_attachment_phab_revision
+ request
+ set_phab_user
);
-has 'is_daemon' => ( is => 'rw', default => 0 );
+has 'is_daemon' => (is => 'rw', default => 0);
-my $Invocant = class_type { class => __PACKAGE__ };
+my $Invocant = class_type {class => __PACKAGE__};
sub start {
- my ($self) = @_;
-
- my $sig_alarm = IO::Async::Signal->new(
- name => 'ALRM',
- on_receipt => sub {
- FATAL("Timeout reached");
- exit;
- },
- );
- # Query for new revisions or changes
- my $feed_timer = IO::Async::Timer::Periodic->new(
- first_interval => 0,
- interval => PHAB_FEED_POLL_SECONDS,
- reschedule => 'drift',
- on_tick => sub {
- try {
- with_writable_database {
- alarm(PHAB_TIMEOUT);
- $self->feed_query();
- };
- }
- catch {
- FATAL($_);
- }
- finally {
- alarm(0);
- Bugzilla->_cleanup();
- };
- },
- );
-
- # Query for new users
- my $user_timer = IO::Async::Timer::Periodic->new(
- first_interval => 0,
- interval => PHAB_USER_POLL_SECONDS,
- reschedule => 'drift',
- on_tick => sub {
- try {
- with_writable_database {
- alarm(PHAB_TIMEOUT);
- $self->user_query();
- };
- }
- catch {
- FATAL($_);
- }
- finally {
- alarm(0);
- Bugzilla->_cleanup();
- };
- },
- );
-
- # Update project membership in Phabricator based on Bugzilla groups
- my $group_timer = IO::Async::Timer::Periodic->new(
- first_interval => 0,
- interval => PHAB_GROUP_POLL_SECONDS,
- reschedule => 'drift',
- on_tick => sub {
- try {
- with_writable_database {
- alarm(PHAB_TIMEOUT);
- $self->group_query();
- };
- }
- catch {
- FATAL($_);
- }
- finally {
- alarm(0);
- Bugzilla->_cleanup();
- };
-
- },
- );
-
- my $loop = IO::Async::Loop->new;
- $loop->add($feed_timer);
- $loop->add($user_timer);
- $loop->add($group_timer);
- $loop->add($sig_alarm);
- $feed_timer->start;
- $user_timer->start;
- $group_timer->start;
- $loop->run;
+ my ($self) = @_;
+
+ my $sig_alarm = IO::Async::Signal->new(
+ name => 'ALRM',
+ on_receipt => sub {
+ FATAL("Timeout reached");
+ exit;
+ },
+ );
+
+ # Query for new revisions or changes
+ my $feed_timer = IO::Async::Timer::Periodic->new(
+ first_interval => 0,
+ interval => PHAB_FEED_POLL_SECONDS,
+ reschedule => 'drift',
+ on_tick => sub {
+ try {
+ with_writable_database {
+ alarm(PHAB_TIMEOUT);
+ $self->feed_query();
+ };
+ }
+ catch {
+ FATAL($_);
+ }
+ finally {
+ alarm(0);
+ Bugzilla->_cleanup();
+ };
+ },
+ );
+
+ # Query for new users
+ my $user_timer = IO::Async::Timer::Periodic->new(
+ first_interval => 0,
+ interval => PHAB_USER_POLL_SECONDS,
+ reschedule => 'drift',
+ on_tick => sub {
+ try {
+ with_writable_database {
+ alarm(PHAB_TIMEOUT);
+ $self->user_query();
+ };
+ }
+ catch {
+ FATAL($_);
+ }
+ finally {
+ alarm(0);
+ Bugzilla->_cleanup();
+ };
+ },
+ );
+
+ # Update project membership in Phabricator based on Bugzilla groups
+ my $group_timer = IO::Async::Timer::Periodic->new(
+ first_interval => 0,
+ interval => PHAB_GROUP_POLL_SECONDS,
+ reschedule => 'drift',
+ on_tick => sub {
+ try {
+ with_writable_database {
+ alarm(PHAB_TIMEOUT);
+ $self->group_query();
+ };
+ }
+ catch {
+ FATAL($_);
+ }
+ finally {
+ alarm(0);
+ Bugzilla->_cleanup();
+ };
+
+ },
+ );
+
+ my $loop = IO::Async::Loop->new;
+ $loop->add($feed_timer);
+ $loop->add($user_timer);
+ $loop->add($group_timer);
+ $loop->add($sig_alarm);
+ $feed_timer->start;
+ $user_timer->start;
+ $group_timer->start;
+ $loop->run;
}
sub feed_query {
- my ($self) = @_;
+ my ($self) = @_;
+
+ local Bugzilla::Logging->fields->{type} = 'FEED';
+
+ # Ensure Phabricator syncing is enabled
+ if (!Bugzilla->params->{phabricator_enabled}) {
+ WARN("PHABRICATOR SYNC DISABLED");
+ return;
+ }
+
+ # PROCESS NEW FEED TRANSACTIONS
+
+ INFO("Fetching new stories");
+
+ my $story_last_id = $self->get_last_id('feed');
+
+ # Check for new transctions (stories)
+ my $new_stories = $self->new_stories($story_last_id);
+ INFO("No new stories") unless @$new_stories;
+
+ # Process each story
+ foreach my $story_data (@$new_stories) {
+ my $story_id = $story_data->{id};
+ my $story_phid = $story_data->{phid};
+ my $author_phid = $story_data->{authorPHID};
+ my $object_phid = $story_data->{objectPHID};
+ my $story_text = $story_data->{text};
- local Bugzilla::Logging->fields->{type} = 'FEED';
+ TRACE("STORY ID: $story_id");
+ TRACE("STORY PHID: $story_phid");
+ TRACE("AUTHOR PHID: $author_phid");
+ TRACE("OBJECT PHID: $object_phid");
+ INFO("STORY TEXT: $story_text");
- # Ensure Phabricator syncing is enabled
- if (!Bugzilla->params->{phabricator_enabled}) {
- WARN("PHABRICATOR SYNC DISABLED");
- return;
+ # Only interested in changes to revisions for now.
+ if ($object_phid !~ /^PHID-DREV/) {
+ INFO("SKIPPING: Not a revision change");
+ $self->save_last_id($story_id, 'feed');
+ next;
}
- # PROCESS NEW FEED TRANSACTIONS
-
- INFO("Fetching new stories");
-
- my $story_last_id = $self->get_last_id('feed');
-
- # Check for new transctions (stories)
- my $new_stories = $self->new_stories($story_last_id);
- INFO("No new stories") unless @$new_stories;
-
- # Process each story
- foreach my $story_data (@$new_stories) {
- my $story_id = $story_data->{id};
- my $story_phid = $story_data->{phid};
- my $author_phid = $story_data->{authorPHID};
- my $object_phid = $story_data->{objectPHID};
- my $story_text = $story_data->{text};
-
- TRACE("STORY ID: $story_id");
- TRACE("STORY PHID: $story_phid");
- TRACE("AUTHOR PHID: $author_phid");
- TRACE("OBJECT PHID: $object_phid");
- INFO("STORY TEXT: $story_text");
-
- # Only interested in changes to revisions for now.
- if ($object_phid !~ /^PHID-DREV/) {
- INFO("SKIPPING: Not a revision change");
- $self->save_last_id($story_id, 'feed');
- next;
- }
-
- # Skip changes done by phab-bot user
- # If changer does not exist in bugzilla database
- # we use the phab-bot account as the changer
- my $author = Bugzilla::Extension::PhabBugz::User->new_from_query(
- { phids => [ $author_phid ] }
- );
-
- if ($author && $author->bugzilla_id) {
- if ($author->bugzilla_user->login eq PHAB_AUTOMATION_USER) {
- INFO("SKIPPING: Change made by phabricator user");
- $self->save_last_id($story_id, 'feed');
- next;
- }
- }
- else {
- my $phab_user = Bugzilla::User->new( { name => PHAB_AUTOMATION_USER } );
- $author = Bugzilla::Extension::PhabBugz::User->new_from_query(
- {
- ids => [ $phab_user->id ]
- }
- );
- }
- # Load the revision from Phabricator
- my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query({ phids => [ $object_phid ] });
- $self->process_revision_change($revision, $author, $story_text);
+ # Skip changes done by phab-bot user
+ # If changer does not exist in bugzilla database
+ # we use the phab-bot account as the changer
+ my $author = Bugzilla::Extension::PhabBugz::User->new_from_query(
+ {phids => [$author_phid]});
+
+ if ($author && $author->bugzilla_id) {
+ if ($author->bugzilla_user->login eq PHAB_AUTOMATION_USER) {
+ INFO("SKIPPING: Change made by phabricator user");
$self->save_last_id($story_id, 'feed');
+ next;
+ }
+ }
+ else {
+ my $phab_user = Bugzilla::User->new({name => PHAB_AUTOMATION_USER});
+ $author
+ = Bugzilla::Extension::PhabBugz::User->new_from_query({ids => [$phab_user->id]
+ });
}
- # Process any build targets as well.
- my $dbh = Bugzilla->dbh;
+ # Load the revision from Phabricator
+ my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query(
+ {phids => [$object_phid]});
+ $self->process_revision_change($revision, $author, $story_text);
+ $self->save_last_id($story_id, 'feed');
+ }
- INFO("Checking for revisions in draft mode");
- my $build_targets = $dbh->selectall_arrayref(
- "SELECT name, value FROM phabbugz WHERE name LIKE 'build_target_%'",
- { Slice => {} }
- );
+ # Process any build targets as well.
+ my $dbh = Bugzilla->dbh;
- my $delete_build_target = $dbh->prepare(
- "DELETE FROM phabbugz WHERE name = ? AND VALUE = ?"
- );
+ INFO("Checking for revisions in draft mode");
+ my $build_targets
+ = $dbh->selectall_arrayref(
+ "SELECT name, value FROM phabbugz WHERE name LIKE 'build_target_%'",
+ {Slice => {}});
- foreach my $target (@$build_targets) {
- my ($revision_id) = ($target->{name} =~ /^build_target_(\d+)$/);
- my $build_target = $target->{value};
+ my $delete_build_target
+ = $dbh->prepare("DELETE FROM phabbugz WHERE name = ? AND VALUE = ?");
- next unless $revision_id && $build_target;
+ foreach my $target (@$build_targets) {
+ my ($revision_id) = ($target->{name} =~ /^build_target_(\d+)$/);
+ my $build_target = $target->{value};
- INFO("Processing revision $revision_id with build target $build_target");
+ next unless $revision_id && $build_target;
- my $revision =
- Bugzilla::Extension::PhabBugz::Revision->new_from_query(
- {
- ids => [ int($revision_id) ]
- }
- );
+ INFO("Processing revision $revision_id with build target $build_target");
- $self->process_revision_change( $revision, $revision->author, " created D" . $revision->id );
+ my $revision
+ = Bugzilla::Extension::PhabBugz::Revision->new_from_query({
+ ids => [int($revision_id)]
+ });
- # Set the build target to a passing status to
- # allow the revision to exit draft state
- request( 'harbormaster.sendmessage', {
- buildTargetPHID => $build_target,
- type => 'pass'
- } );
+ $self->process_revision_change($revision, $revision->author,
+ " created D" . $revision->id);
- $delete_build_target->execute($target->{name}, $target->{value});
- }
+ # Set the build target to a passing status to
+ # allow the revision to exit draft state
+ request('harbormaster.sendmessage',
+ {buildTargetPHID => $build_target, type => 'pass'});
- if (Bugzilla->datadog) {
- my $dd = Bugzilla->datadog();
- $dd->increment('bugzilla.phabbugz.feed_query_count');
- }
+ $delete_build_target->execute($target->{name}, $target->{value});
+ }
+
+ if (Bugzilla->datadog) {
+ my $dd = Bugzilla->datadog();
+ $dd->increment('bugzilla.phabbugz.feed_query_count');
+ }
}
sub user_query {
- my ( $self ) = @_;
+ my ($self) = @_;
- local Bugzilla::Logging->fields->{type} = 'USERS';
+ local Bugzilla::Logging->fields->{type} = 'USERS';
- # Ensure Phabricator syncing is enabled
- if (!Bugzilla->params->{phabricator_enabled}) {
- WARN("PHABRICATOR SYNC DISABLED");
- return;
- }
+ # Ensure Phabricator syncing is enabled
+ if (!Bugzilla->params->{phabricator_enabled}) {
+ WARN("PHABRICATOR SYNC DISABLED");
+ return;
+ }
- # PROCESS NEW USERS
+ # PROCESS NEW USERS
- INFO("Fetching new users");
+ INFO("Fetching new users");
- my $user_last_id = $self->get_last_id('user');
+ my $user_last_id = $self->get_last_id('user');
- # Check for new users
- my $new_users = $self->new_users($user_last_id);
- INFO("No new users") unless @$new_users;
+ # Check for new users
+ my $new_users = $self->new_users($user_last_id);
+ INFO("No new users") unless @$new_users;
- # Process each new user
- foreach my $user_data (@$new_users) {
- my $user_id = $user_data->{id};
- my $user_login = $user_data->{fields}{username};
- my $user_realname = $user_data->{fields}{realName};
- my $object_phid = $user_data->{phid};
+ # Process each new user
+ foreach my $user_data (@$new_users) {
+ my $user_id = $user_data->{id};
+ my $user_login = $user_data->{fields}{username};
+ my $user_realname = $user_data->{fields}{realName};
+ my $object_phid = $user_data->{phid};
- TRACE("ID: $user_id");
- TRACE("LOGIN: $user_login");
- TRACE("REALNAME: $user_realname");
- TRACE("OBJECT PHID: $object_phid");
+ TRACE("ID: $user_id");
+ TRACE("LOGIN: $user_login");
+ TRACE("REALNAME: $user_realname");
+ TRACE("OBJECT PHID: $object_phid");
- with_readonly_database {
- $self->process_new_user($user_data);
- };
- $self->save_last_id($user_id, 'user');
- }
+ with_readonly_database {
+ $self->process_new_user($user_data);
+ };
+ $self->save_last_id($user_id, 'user');
+ }
- if (Bugzilla->datadog) {
- my $dd = Bugzilla->datadog();
- $dd->increment('bugzilla.phabbugz.user_query_count');
- }
+ if (Bugzilla->datadog) {
+ my $dd = Bugzilla->datadog();
+ $dd->increment('bugzilla.phabbugz.user_query_count');
+ }
}
sub group_query {
- my ($self) = @_;
+ my ($self) = @_;
- local Bugzilla::Logging->fields->{type} = 'GROUPS';
+ local Bugzilla::Logging->fields->{type} = 'GROUPS';
- # Ensure Phabricator syncing is enabled
- if ( !Bugzilla->params->{phabricator_enabled} ) {
- WARN("PHABRICATOR SYNC DISABLED");
- return;
- }
+ # Ensure Phabricator syncing is enabled
+ if (!Bugzilla->params->{phabricator_enabled}) {
+ WARN("PHABRICATOR SYNC DISABLED");
+ return;
+ }
- # PROCESS SECURITY GROUPS
-
- INFO("Updating group memberships");
-
- # Loop through each group and perform the following:
- #
- # 1. Load flattened list of group members
- # 2. Check to see if Phab project exists for 'bmo-<group_name>'
- # 3. Create if does not exist with locked down policy.
- # 4. Set project members to exact list including phab-bot user
- # 5. Profit
-
- my $sync_groups = Bugzilla::Group->match( { isactive => 1, isbuggroup => 1 } );
-
- # Load phab-bot Phabricator user to add as a member of each project group later
- my $phab_bmo_user = Bugzilla::User->new( { name => PHAB_AUTOMATION_USER, cache => 1 } );
- my $phab_user =
- Bugzilla::Extension::PhabBugz::User->new_from_query(
- {
- ids => [ $phab_bmo_user->id ]
- }
- );
-
- # secure-revision project that will be used for bmo group projects
- my $secure_revision =
- Bugzilla::Extension::PhabBugz::Project->new_from_query(
- {
- name => 'secure-revision'
- }
- );
-
- foreach my $group (@$sync_groups) {
- # Create group project if one does not yet exist
- my $phab_project_name = 'bmo-' . $group->name;
- my $project =
- Bugzilla::Extension::PhabBugz::Project->new_from_query(
- {
- name => $phab_project_name
- }
- );
-
- if ( !$project ) {
- INFO("Project $phab_project_name not found. Creating.");
- $project = Bugzilla::Extension::PhabBugz::Project->create(
- {
- name => $phab_project_name,
- description => 'BMO Security Group for ' . $group->name,
- view_policy => $secure_revision->phid,
- edit_policy => $secure_revision->phid,
- join_policy => $secure_revision->phid
- }
- );
- }
- else {
- # Make sure that the group project permissions are set properly
- INFO("Updating permissions on $phab_project_name");
- $project->set_policy( 'view', $secure_revision->phid );
- $project->set_policy( 'edit', $secure_revision->phid );
- $project->set_policy( 'join', $secure_revision->phid );
- }
-
- # Make sure phab-bot also a member of the new project group so that it can
- # make policy changes to the private revisions
- INFO( "Checking project members for " . $project->name );
- my $set_members = $self->get_group_members($group);
- my @set_member_phids = uniq map { $_->phid } ( @$set_members, $phab_user );
- my @current_member_phids = uniq map { $_->phid } @{ $project->members };
- my ( $removed, $added ) = diff_arrays( \@current_member_phids, \@set_member_phids );
-
- if (@$added) {
- INFO( 'Adding project members: ' . join( ',', @$added ) );
- $project->add_member($_) foreach @$added;
- }
-
- if (@$removed) {
- INFO( 'Removing project members: ' . join( ',', @$removed ) );
- $project->remove_member($_) foreach @$removed;
- }
-
- if (@$added || @$removed) {
- my $result = $project->update();
- local Bugzilla::Logging->fields->{api_result} = $result;
- INFO( "Project " . $project->name . " updated" );
- }
- }
+ # PROCESS SECURITY GROUPS
+
+ INFO("Updating group memberships");
+
+ # Loop through each group and perform the following:
+ #
+ # 1. Load flattened list of group members
+ # 2. Check to see if Phab project exists for 'bmo-<group_name>'
+ # 3. Create if does not exist with locked down policy.
+ # 4. Set project members to exact list including phab-bot user
+ # 5. Profit
+
+ my $sync_groups = Bugzilla::Group->match({isactive => 1, isbuggroup => 1});
- if (Bugzilla->datadog) {
- my $dd = Bugzilla->datadog();
- $dd->increment('bugzilla.phabbugz.group_query_count');
+ # Load phab-bot Phabricator user to add as a member of each project group later
+ my $phab_bmo_user
+ = Bugzilla::User->new({name => PHAB_AUTOMATION_USER, cache => 1});
+ my $phab_user
+ = Bugzilla::Extension::PhabBugz::User->new_from_query({
+ ids => [$phab_bmo_user->id]
+ });
+
+ # secure-revision project that will be used for bmo group projects
+ my $secure_revision
+ = Bugzilla::Extension::PhabBugz::Project->new_from_query({
+ name => 'secure-revision'
+ });
+
+ foreach my $group (@$sync_groups) {
+
+ # Create group project if one does not yet exist
+ my $phab_project_name = 'bmo-' . $group->name;
+ my $project
+ = Bugzilla::Extension::PhabBugz::Project->new_from_query({
+ name => $phab_project_name
+ });
+
+ if (!$project) {
+ INFO("Project $phab_project_name not found. Creating.");
+ $project = Bugzilla::Extension::PhabBugz::Project->create({
+ name => $phab_project_name,
+ description => 'BMO Security Group for ' . $group->name,
+ view_policy => $secure_revision->phid,
+ edit_policy => $secure_revision->phid,
+ join_policy => $secure_revision->phid
+ });
+ }
+ else {
+ # Make sure that the group project permissions are set properly
+ INFO("Updating permissions on $phab_project_name");
+ $project->set_policy('view', $secure_revision->phid);
+ $project->set_policy('edit', $secure_revision->phid);
+ $project->set_policy('join', $secure_revision->phid);
}
-}
-sub process_revision_change {
- state $check = compile($Invocant, Revision, LinkedPhabUser, Str);
- my ($self, $revision, $changer, $story_text) = $check->(@_);
-
- # NO BUG ID
- if (!$revision->bug_id) {
- if ($story_text =~ /\s+created\s+D\d+/) {
- # If new revision and bug id was omitted, make revision public
- INFO("No bug associated with new revision. Marking public.");
- $revision->make_public();
- $revision->update();
- INFO("SUCCESS");
- return;
- }
- else {
- INFO("SKIPPING: No bug associated with revision change");
- return;
- }
+ # Make sure phab-bot also a member of the new project group so that it can
+ # make policy changes to the private revisions
+ INFO("Checking project members for " . $project->name);
+ my $set_members = $self->get_group_members($group);
+ my @set_member_phids = uniq map { $_->phid } (@$set_members, $phab_user);
+ my @current_member_phids = uniq map { $_->phid } @{$project->members};
+ my ($removed, $added) = diff_arrays(\@current_member_phids, \@set_member_phids);
+
+ if (@$added) {
+ INFO('Adding project members: ' . join(',', @$added));
+ $project->add_member($_) foreach @$added;
}
+ if (@$removed) {
+ INFO('Removing project members: ' . join(',', @$removed));
+ $project->remove_member($_) foreach @$removed;
+ }
- my $log_message = sprintf(
- "REVISION CHANGE FOUND: D%d: %s | bug: %d | %s | %s",
- $revision->id,
- $revision->title,
- $revision->bug_id,
- $changer->name,
- $story_text);
- INFO($log_message);
-
- # change to the phabricator user, which returns a guard that restores the previous user.
- my $restore_prev_user = set_phab_user();
- my $bug = $revision->bug;
-
- # Check to make sure bug id is valid and author can see it
- if ($bug->{error}
- ||!$revision->author->bugzilla_user->can_see_bug($revision->bug_id))
- {
- if ($story_text =~ /\s+created\s+D\d+/) {
- INFO('Invalid bug ID or author does not have access to the bug. ' .
- 'Waiting til next revision update to notify author.');
- return;
- }
-
- INFO('Invalid bug ID or author does not have access to the bug');
- my $phab_error_message = "";
- Bugzilla->template->process('revision/comments.html.tmpl',
- { message => 'invalid_bug_id' },
- \$phab_error_message);
- $revision->add_comment($phab_error_message);
- $revision->update();
- return;
+ if (@$added || @$removed) {
+ my $result = $project->update();
+ local Bugzilla::Logging->fields->{api_result} = $result;
+ INFO("Project " . $project->name . " updated");
}
+ }
- # REVISION SECURITY POLICY
+ if (Bugzilla->datadog) {
+ my $dd = Bugzilla->datadog();
+ $dd->increment('bugzilla.phabbugz.group_query_count');
+ }
+}
- # If bug is public then remove privacy policy
- if (!@{ $bug->groups_in }) {
- INFO('Bug is public so setting view/edit public');
- $revision->make_public();
+sub process_revision_change {
+ state $check = compile($Invocant, Revision, LinkedPhabUser, Str);
+ my ($self, $revision, $changer, $story_text) = $check->(@_);
+
+ # NO BUG ID
+ if (!$revision->bug_id) {
+ if ($story_text =~ /\s+created\s+D\d+/) {
+
+ # If new revision and bug id was omitted, make revision public
+ INFO("No bug associated with new revision. Marking public.");
+ $revision->make_public();
+ $revision->update();
+ INFO("SUCCESS");
+ return;
}
- # else bug is private.
else {
- # Here we create a new custom policy containing the project
- # groups that are mapped to bugzilla groups.
- my $set_project_names = [ map { "bmo-" . $_->name } @{ $bug->groups_in } ];
-
- # If current policy projects matches what we want to set, then
- # we leave the current policy alone.
- my $current_policy;
- if ($revision->view_policy =~ /^PHID-PLCY/) {
- INFO("Loading current policy: " . $revision->view_policy);
- $current_policy
- = Bugzilla::Extension::PhabBugz::Policy->new_from_query({ phids => [ $revision->view_policy ]});
- my $current_project_names = [ map { $_->name } @{ $current_policy->rule_projects } ];
- INFO("Current policy projects: " . join(", ", @$current_project_names));
- my ($added, $removed) = diff_arrays($current_project_names, $set_project_names);
- if (@$added || @$removed) {
- INFO('Project groups do not match. Need new custom policy');
- $current_policy = undef;
- }
- else {
- INFO('Project groups match. Leaving current policy as-is');
- }
- }
-
- if (!$current_policy) {
- INFO("Creating new custom policy: " . join(", ", @$set_project_names));
- $revision->make_private($set_project_names);
- }
-
- # Subscriber list of the private revision should always match
- # the bug roles such as assignee, qa contact, and cc members.
- my $subscribers = get_bug_role_phids($bug);
- $revision->set_subscribers($subscribers);
+ INFO("SKIPPING: No bug associated with revision change");
+ return;
+ }
+ }
+
+
+ my $log_message = sprintf(
+ "REVISION CHANGE FOUND: D%d: %s | bug: %d | %s | %s",
+ $revision->id, $revision->title, $revision->bug_id,
+ $changer->name, $story_text
+ );
+ INFO($log_message);
+
+# change to the phabricator user, which returns a guard that restores the previous user.
+ my $restore_prev_user = set_phab_user();
+ my $bug = $revision->bug;
+
+ # Check to make sure bug id is valid and author can see it
+ if ($bug->{error}
+ || !$revision->author->bugzilla_user->can_see_bug($revision->bug_id))
+ {
+ if ($story_text =~ /\s+created\s+D\d+/) {
+ INFO( 'Invalid bug ID or author does not have access to the bug. '
+ . 'Waiting til next revision update to notify author.');
+ return;
}
- my ($timestamp) = Bugzilla->dbh->selectrow_array("SELECT NOW()");
-
- INFO('Checking for revision attachment');
- my $rev_attachment = create_revision_attachment($bug, $revision, $timestamp, $revision->author->bugzilla_user);
- INFO('Attachment ' . $rev_attachment->id . ' created or already exists.');
-
- # ATTACHMENT OBSOLETES
+ INFO('Invalid bug ID or author does not have access to the bug');
+ my $phab_error_message = "";
+ Bugzilla->template->process('revision/comments.html.tmpl',
+ {message => 'invalid_bug_id'},
+ \$phab_error_message);
+ $revision->add_comment($phab_error_message);
+ $revision->update();
+ return;
+ }
+
+ # REVISION SECURITY POLICY
+
+ # If bug is public then remove privacy policy
+ if (!@{$bug->groups_in}) {
+ INFO('Bug is public so setting view/edit public');
+ $revision->make_public();
+ }
+
+ # else bug is private.
+ else {
+ # Here we create a new custom policy containing the project
+ # groups that are mapped to bugzilla groups.
+ my $set_project_names = [map { "bmo-" . $_->name } @{$bug->groups_in}];
+
+ # If current policy projects matches what we want to set, then
+ # we leave the current policy alone.
+ my $current_policy;
+ if ($revision->view_policy =~ /^PHID-PLCY/) {
+ INFO("Loading current policy: " . $revision->view_policy);
+ $current_policy = Bugzilla::Extension::PhabBugz::Policy->new_from_query(
+ {phids => [$revision->view_policy]});
+ my $current_project_names
+ = [map { $_->name } @{$current_policy->rule_projects}];
+ INFO("Current policy projects: " . join(", ", @$current_project_names));
+ my ($added, $removed) = diff_arrays($current_project_names, $set_project_names);
+ if (@$added || @$removed) {
+ INFO('Project groups do not match. Need new custom policy');
+ $current_policy = undef;
+ }
+ else {
+ INFO('Project groups match. Leaving current policy as-is');
+ }
+ }
- # fixup attachments on current bug
- my @attachments =
- grep { is_attachment_phab_revision($_) } @{ $bug->attachments() };
+ if (!$current_policy) {
+ INFO("Creating new custom policy: " . join(", ", @$set_project_names));
+ $revision->make_private($set_project_names);
+ }
- foreach my $attachment (@attachments) {
- my ($attach_revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN);
- next if $attach_revision_id != $revision->id;
+ # Subscriber list of the private revision should always match
+ # the bug roles such as assignee, qa contact, and cc members.
+ my $subscribers = get_bug_role_phids($bug);
+ $revision->set_subscribers($subscribers);
+ }
- my $make_obsolete = $revision->status eq 'abandoned' ? 1 : 0;
- INFO('Updating obsolete status on attachmment ' . $attachment->id);
- $attachment->set_is_obsolete($make_obsolete);
+ my ($timestamp) = Bugzilla->dbh->selectrow_array("SELECT NOW()");
- if ($revision->title ne $attachment->description) {
- INFO('Updating description on attachment ' . $attachment->id);
- $attachment->set_description($revision->title);
- }
+ INFO('Checking for revision attachment');
+ my $rev_attachment = create_revision_attachment($bug, $revision, $timestamp,
+ $revision->author->bugzilla_user);
+ INFO('Attachment ' . $rev_attachment->id . ' created or already exists.');
- $attachment->update($timestamp);
- }
+ # ATTACHMENT OBSOLETES
- # fixup attachments with same revision id but on different bugs
- my %other_bugs;
- my $other_attachments = Bugzilla::Attachment->match({
- mimetype => PHAB_CONTENT_TYPE,
- filename => 'phabricator-D' . $revision->id . '-url.txt',
- WHERE => { 'bug_id != ? AND NOT isobsolete' => $bug->id }
- });
- foreach my $attachment (@$other_attachments) {
- $other_bugs{$attachment->bug_id}++;
- INFO('Updating obsolete status on attachment ' .
- $attachment->id . " for bug " . $attachment->bug_id);
- $attachment->set_is_obsolete(1);
- $attachment->update($timestamp);
- }
+ # fixup attachments on current bug
+ my @attachments
+ = grep { is_attachment_phab_revision($_) } @{$bug->attachments()};
- # FINISH UP
+ foreach my $attachment (@attachments) {
+ my ($attach_revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN);
+ next if $attach_revision_id != $revision->id;
- $bug->update($timestamp);
- $revision->update();
+ my $make_obsolete = $revision->status eq 'abandoned' ? 1 : 0;
+ INFO('Updating obsolete status on attachmment ' . $attachment->id);
+ $attachment->set_is_obsolete($make_obsolete);
- # Email changes for this revisions bug and also for any other
- # bugs that previously had these revision attachments
- foreach my $bug_id ($revision->bug_id, keys %other_bugs) {
- Bugzilla::BugMail::Send($bug_id, { changer => $changer->bugzilla_user });
+ if ($revision->title ne $attachment->description) {
+ INFO('Updating description on attachment ' . $attachment->id);
+ $attachment->set_description($revision->title);
}
- INFO('SUCCESS: Revision D' . $revision->id . ' processed');
+ $attachment->update($timestamp);
+ }
+
+ # fixup attachments with same revision id but on different bugs
+ my %other_bugs;
+ my $other_attachments = Bugzilla::Attachment->match({
+ mimetype => PHAB_CONTENT_TYPE,
+ filename => 'phabricator-D' . $revision->id . '-url.txt',
+ WHERE => {'bug_id != ? AND NOT isobsolete' => $bug->id}
+ });
+ foreach my $attachment (@$other_attachments) {
+ $other_bugs{$attachment->bug_id}++;
+ INFO( 'Updating obsolete status on attachment '
+ . $attachment->id
+ . " for bug "
+ . $attachment->bug_id);
+ $attachment->set_is_obsolete(1);
+ $attachment->update($timestamp);
+ }
+
+ # FINISH UP
+
+ $bug->update($timestamp);
+ $revision->update();
+
+ # Email changes for this revisions bug and also for any other
+ # bugs that previously had these revision attachments
+ foreach my $bug_id ($revision->bug_id, keys %other_bugs) {
+ Bugzilla::BugMail::Send($bug_id, {changer => $changer->bugzilla_user});
+ }
+
+ INFO('SUCCESS: Revision D' . $revision->id . ' processed');
}
sub process_new_user {
- state $check = compile($Invocant, HashRef);
- my ( $self, $user_data ) = $check->(@_);
+ state $check = compile($Invocant, HashRef);
+ my ($self, $user_data) = $check->(@_);
- # Load the user data into a proper object
- my $phab_user = Bugzilla::Extension::PhabBugz::User->new($user_data);
+ # Load the user data into a proper object
+ my $phab_user = Bugzilla::Extension::PhabBugz::User->new($user_data);
- if (!$phab_user->bugzilla_id) {
- WARN("SKIPPING: No bugzilla id associated with user");
- return;
- }
+ if (!$phab_user->bugzilla_id) {
+ WARN("SKIPPING: No bugzilla id associated with user");
+ return;
+ }
- my $bug_user = $phab_user->bugzilla_user;
+ my $bug_user = $phab_user->bugzilla_user;
- # Pre setup before querying DB
- my $restore_prev_user = set_phab_user();
+ # Pre setup before querying DB
+ my $restore_prev_user = set_phab_user();
- # CHECK AND WARN FOR POSSIBLE USERNAME SQUATTING
- INFO("Checking for username squatters");
- my $dbh = Bugzilla->dbh;
- my $regexp = $dbh->quote( ":?:" . quotemeta($phab_user->name) . "[[:>:]]" );
- my $results = $dbh->selectall_arrayref( "
+ # CHECK AND WARN FOR POSSIBLE USERNAME SQUATTING
+ INFO("Checking for username squatters");
+ my $dbh = Bugzilla->dbh;
+ my $regexp = $dbh->quote(":?:" . quotemeta($phab_user->name) . "[[:>:]]");
+ my $results = $dbh->selectall_arrayref("
SELECT userid, login_name, realname
FROM profiles
- WHERE userid != ? AND " . $dbh->sql_regexp( 'realname', $regexp ),
- { Slice => {} },
- $bug_user->id );
- if (@$results) {
- # The email client will display the Date: header in the desired timezone,
- # so we can always use UTC here.
- my $timestamp = Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
- $timestamp = format_time($timestamp, '%a, %d %b %Y %T %z', 'UTC');
-
- foreach my $row (@$results) {
- WARN(
- 'Possible username squatter: ',
- 'phab user login: ' . $phab_user->name,
- ' phab user realname: ' . $phab_user->realname,
- ' bugzilla user id: ' . $row->{userid},
- ' bugzilla login: ' . $row->{login_name},
- ' bugzilla realname: ' . $row->{realname}
- );
-
- my $vars = {
- date => $timestamp,
- phab_user_login => $phab_user->name,
- phab_user_realname => $phab_user->realname,
- bugzilla_userid => $bug_user->id,
- bugzilla_login => $bug_user->login,
- bugzilla_realname => $bug_user->name,
- squat_userid => $row->{userid},
- squat_login => $row->{login_name},
- squat_realname => $row->{realname}
- };
-
- my $message;
- my $template = Bugzilla->template;
- $template->process("admin/email/squatter-alert.txt.tmpl", $vars, \$message)
- || ThrowTemplateError($template->error());
-
- MessageToMTA($message);
- }
+ WHERE userid != ? AND " . $dbh->sql_regexp('realname', $regexp),
+ {Slice => {}}, $bug_user->id);
+ if (@$results) {
+
+ # The email client will display the Date: header in the desired timezone,
+ # so we can always use UTC here.
+ my $timestamp = Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
+ $timestamp = format_time($timestamp, '%a, %d %b %Y %T %z', 'UTC');
+
+ foreach my $row (@$results) {
+ WARN(
+ 'Possible username squatter: ',
+ 'phab user login: ' . $phab_user->name,
+ ' phab user realname: ' . $phab_user->realname,
+ ' bugzilla user id: ' . $row->{userid},
+ ' bugzilla login: ' . $row->{login_name},
+ ' bugzilla realname: ' . $row->{realname}
+ );
+
+ my $vars = {
+ date => $timestamp,
+ phab_user_login => $phab_user->name,
+ phab_user_realname => $phab_user->realname,
+ bugzilla_userid => $bug_user->id,
+ bugzilla_login => $bug_user->login,
+ bugzilla_realname => $bug_user->name,
+ squat_userid => $row->{userid},
+ squat_login => $row->{login_name},
+ squat_realname => $row->{realname}
+ };
+
+ my $message;
+ my $template = Bugzilla->template;
+ $template->process("admin/email/squatter-alert.txt.tmpl", $vars, \$message)
+ || ThrowTemplateError($template->error());
+
+ MessageToMTA($message);
}
+ }
- # ADD SUBSCRIBERS TO REVSISIONS FOR CURRENT PRIVATE BUGS
+ # ADD SUBSCRIBERS TO REVSISIONS FOR CURRENT PRIVATE BUGS
- my $params = {
- f3 => 'OP',
- j3 => 'OR',
+ my $params = {
+ f3 => 'OP',
+ j3 => 'OR',
- # User must be either reporter, assignee, qa_contact
- # or on the cc list of the bug
- f4 => 'cc',
- o4 => 'equals',
- v4 => $bug_user->login,
+ # User must be either reporter, assignee, qa_contact
+ # or on the cc list of the bug
+ f4 => 'cc',
+ o4 => 'equals',
+ v4 => $bug_user->login,
- f5 => 'assigned_to',
- o5 => 'equals',
- v5 => $bug_user->login,
+ f5 => 'assigned_to',
+ o5 => 'equals',
+ v5 => $bug_user->login,
- f6 => 'qa_contact',
- o6 => 'equals',
- v6 => $bug_user->login,
+ f6 => 'qa_contact',
+ o6 => 'equals',
+ v6 => $bug_user->login,
- f7 => 'reporter',
- o7 => 'equals',
- v7 => $bug_user->login,
+ f7 => 'reporter',
+ o7 => 'equals',
+ v7 => $bug_user->login,
- f9 => 'CP',
+ f9 => 'CP',
- # The bug needs to be private
- f10 => 'bug_group',
- o10 => 'isnotempty',
+ # The bug needs to be private
+ f10 => 'bug_group',
+ o10 => 'isnotempty',
- # And the bug must have one or more attachments
- # that are connected to revisions
- f11 => 'attachments.filename',
- o11 => 'regexp',
- v11 => '^phabricator-D[[:digit:]]+-url.txt$',
- };
+ # And the bug must have one or more attachments
+ # that are connected to revisions
+ f11 => 'attachments.filename',
+ o11 => 'regexp',
+ v11 => '^phabricator-D[[:digit:]]+-url.txt$',
+ };
- my $search = Bugzilla::Search->new( fields => [ 'bug_id' ],
- params => $params,
- order => [ 'bug_id' ] );
- my $data = $search->data;
+ my $search = Bugzilla::Search->new(
+ fields => ['bug_id'],
+ params => $params,
+ order => ['bug_id']
+ );
+ my $data = $search->data;
- # the first value of each row should be the bug id
- my @bug_ids = map { shift @$_ } @$data;
+ # the first value of each row should be the bug id
+ my @bug_ids = map { shift @$_ } @$data;
- INFO("Updating subscriber values for old private bugs");
+ INFO("Updating subscriber values for old private bugs");
- foreach my $bug_id (@bug_ids) {
- INFO("Processing bug $bug_id");
+ foreach my $bug_id (@bug_ids) {
+ INFO("Processing bug $bug_id");
- my $bug = Bugzilla::Bug->new({ id => $bug_id, cache => 1 });
+ my $bug = Bugzilla::Bug->new({id => $bug_id, cache => 1});
- my @attachments =
- grep { is_attachment_phab_revision($_) } @{ $bug->attachments() };
+ my @attachments
+ = grep { is_attachment_phab_revision($_) } @{$bug->attachments()};
- foreach my $attachment (@attachments) {
- my ($revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN);
+ foreach my $attachment (@attachments) {
+ my ($revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN);
- if (!$revision_id) {
- WARN("Skipping " . $attachment->filename . " on bug $bug_id. Filename should be fixed.");
- next;
- }
+ if (!$revision_id) {
+ WARN( "Skipping "
+ . $attachment->filename
+ . " on bug $bug_id. Filename should be fixed.");
+ next;
+ }
- INFO("Processing revision D$revision_id");
+ INFO("Processing revision D$revision_id");
- my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query(
- { ids => [ int($revision_id) ] });
+ my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query(
+ {ids => [int($revision_id)]});
- $revision->add_subscriber($phab_user->phid);
- $revision->update();
+ $revision->add_subscriber($phab_user->phid);
+ $revision->update();
- INFO("Revision $revision_id updated");
- }
+ INFO("Revision $revision_id updated");
}
+ }
- INFO('SUCCESS: User ' . $phab_user->id . ' processed');
+ INFO('SUCCESS: User ' . $phab_user->id . ' processed');
}
##################
@@ -698,87 +697,74 @@ sub process_new_user {
##################
sub new_stories {
- my ( $self, $after ) = @_;
- my $data = { view => 'text' };
- $data->{after} = $after if $after;
+ my ($self, $after) = @_;
+ my $data = {view => 'text'};
+ $data->{after} = $after if $after;
- my $result = request( 'feed.query_id', $data );
+ my $result = request('feed.query_id', $data);
- unless ( ref $result->{result}{data} eq 'ARRAY'
- && @{ $result->{result}{data} } )
- {
- return [];
- }
+ unless (ref $result->{result}{data} eq 'ARRAY' && @{$result->{result}{data}}) {
+ return [];
+ }
- # Guarantee that the data is in ascending ID order
- return [ sort { $a->{id} <=> $b->{id} } @{ $result->{result}{data} } ];
+ # Guarantee that the data is in ascending ID order
+ return [sort { $a->{id} <=> $b->{id} } @{$result->{result}{data}}];
}
sub new_users {
- my ( $self, $after ) = @_;
- my $data = {
- order => [ "id" ],
- attachments => {
- 'external-accounts' => 1
- }
- };
- $data->{before} = $after if $after;
+ my ($self, $after) = @_;
+ my $data = {order => ["id"], attachments => {'external-accounts' => 1}};
+ $data->{before} = $after if $after;
- my $result = request( 'user.search', $data );
+ my $result = request('user.search', $data);
- unless ( ref $result->{result}{data} eq 'ARRAY'
- && @{ $result->{result}{data} } )
- {
- return [];
- }
+ unless (ref $result->{result}{data} eq 'ARRAY' && @{$result->{result}{data}}) {
+ return [];
+ }
- # Guarantee that the data is in ascending ID order
- return [ sort { $a->{id} <=> $b->{id} } @{ $result->{result}{data} } ];
+ # Guarantee that the data is in ascending ID order
+ return [sort { $a->{id} <=> $b->{id} } @{$result->{result}{data}}];
}
sub get_last_id {
- my ( $self, $type ) = @_;
- my $type_full = $type . "_last_id";
- my $last_id = Bugzilla->dbh->selectrow_array( "
- SELECT value FROM phabbugz WHERE name = ?", undef, $type_full );
- $last_id ||= 0;
- TRACE(uc($type_full) . ": $last_id" );
- return $last_id;
+ my ($self, $type) = @_;
+ my $type_full = $type . "_last_id";
+ my $last_id = Bugzilla->dbh->selectrow_array("
+ SELECT value FROM phabbugz WHERE name = ?", undef, $type_full);
+ $last_id ||= 0;
+ TRACE(uc($type_full) . ": $last_id");
+ return $last_id;
}
sub save_last_id {
- my ( $self, $last_id, $type ) = @_;
+ my ($self, $last_id, $type) = @_;
- # Store the largest last key so we can start from there in the next session
- my $type_full = $type . "_last_id";
- TRACE("UPDATING " . uc($type_full) . ": $last_id" );
- Bugzilla->dbh->do( "REPLACE INTO phabbugz (name, value) VALUES (?, ?)",
- undef, $type_full, $last_id );
+ # Store the largest last key so we can start from there in the next session
+ my $type_full = $type . "_last_id";
+ TRACE("UPDATING " . uc($type_full) . ": $last_id");
+ Bugzilla->dbh->do("REPLACE INTO phabbugz (name, value) VALUES (?, ?)",
+ undef, $type_full, $last_id);
}
sub get_group_members {
- state $check = compile( $Invocant, Group | Str );
- my ( $self, $group ) = $check->(@_);
- my $group_obj =
- ref $group ? $group : Bugzilla::Group->check( { name => $group, cache => 1 } );
+ state $check = compile($Invocant, Group | Str);
+ my ($self, $group) = $check->(@_);
+ my $group_obj
+ = ref $group ? $group : Bugzilla::Group->check({name => $group, cache => 1});
- my $flat_list = join(',',
- @{ Bugzilla::Group->flatten_group_membership( $group_obj->id ) } );
+ my $flat_list
+ = join(',', @{Bugzilla::Group->flatten_group_membership($group_obj->id)});
- my $user_query = "
+ my $user_query = "
SELECT DISTINCT profiles.userid
FROM profiles, user_group_map AS ugm
WHERE ugm.user_id = profiles.userid
AND ugm.isbless = 0
AND ugm.group_id IN($flat_list)";
- my $user_ids = Bugzilla->dbh->selectcol_arrayref($user_query);
+ my $user_ids = Bugzilla->dbh->selectcol_arrayref($user_query);
- # Return matching users in Phabricator
- return Bugzilla::Extension::PhabBugz::User->match(
- {
- ids => $user_ids
- }
- );
+ # Return matching users in Phabricator
+ return Bugzilla::Extension::PhabBugz::User->match({ids => $user_ids});
}
1;