summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDylan William Hardison <dylan@hardison.net>2018-04-01 16:46:28 +0200
committerDylan William Hardison <dylan@hardison.net>2018-04-01 16:46:28 +0200
commit2f8b999750cc700faf03c6aee1c53d1fc4df767f (patch)
treeef00bf6fd9932fd4fa1fc69e3108a1c5603aa64a
parentc6d0d0e31a8b7b0e3c46ddf0a2be7741716d7977 (diff)
parent45485679f2e173170ed9c0e8a0eae979fe9de067 (diff)
downloadbugzilla-2f8b999750cc700faf03c6aee1c53d1fc4df767f.tar.gz
bugzilla-2f8b999750cc700faf03c6aee1c53d1fc4df767f.tar.xz
Merge branch 'master' of git://github.com/mozilla-bteam/bmo
-rw-r--r--.circleci/config.yml145
-rw-r--r--.htaccess8
-rw-r--r--.perlcriticrc3
-rw-r--r--Bugzilla.pm4
-rw-r--r--Bugzilla/CGI.pm18
-rw-r--r--Bugzilla/Constants.pm5
-rw-r--r--Bugzilla/DB.pm9
-rw-r--r--Bugzilla/Error.pm10
-rw-r--r--Bugzilla/Install/Localconfig.pm8
-rw-r--r--Bugzilla/JobQueue.pm4
-rw-r--r--Bugzilla/JobQueue/Runner.pm10
-rw-r--r--Bugzilla/Mailer.pm9
-rw-r--r--Bugzilla/Memcached.pm26
-rw-r--r--Bugzilla/ModPerl.pm8
-rw-r--r--Bugzilla/ModPerl/BasicAuth.pm13
-rw-r--r--Bugzilla/ModPerl/Hostage.pm71
-rw-r--r--Bugzilla/WebService/Server/REST.pm3
-rw-r--r--Dockerfile2
-rwxr-xr-xMakefile.PL2
-rw-r--r--README.rst49
-rw-r--r--conf/httpd.conf5
-rw-r--r--extensions/BMO/template/en/default/pages/user_activity.html.tmpl1
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/activity_stream.html.tmpl2
-rw-r--r--extensions/InlineHistory/template/en/default/hook/bug/comments-aftercomments.html.tmpl1
-rw-r--r--extensions/OpenGraph/template/en/default/hook/global/header-start.html.tmpl7
-rw-r--r--extensions/PhabBugz/Extension.pm1
-rw-r--r--extensions/PhabBugz/lib/Daemon.pm5
-rw-r--r--extensions/PhabBugz/lib/Feed.pm98
-rw-r--r--extensions/PhabBugz/lib/Logger.pm37
-rw-r--r--extensions/PhabBugz/lib/Revision.pm2
-rw-r--r--extensions/PhabBugz/lib/Util.pm12
-rw-r--r--extensions/Push/lib/Logger.pm52
-rw-r--r--extensions/Review/Extension.pm2
-rw-r--r--extensions/UserProfile/template/en/default/pages/user_profile.html.tmpl2
-rwxr-xr-xheartbeat.cgi1
-rw-r--r--helper.psgi35
-rwxr-xr-xscripts/build-bmo-push-data.pl203
-rwxr-xr-xscripts/entrypoint.pl3
-rwxr-xr-xses/index.cgi141
-rw-r--r--template/en/default/account/auth/login.html.tmpl2
-rw-r--r--template/en/default/bug/activity/table.html.tmpl1
-rw-r--r--template/en/default/email/new-api-key.txt.tmpl8
-rw-r--r--template/en/default/setup/strings.txt.pl6
-rw-r--r--vagrant_support/bmo-generate-data.j21
-rw-r--r--vagrant_support/checksetup_answers.j21
45 files changed, 755 insertions, 281 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 48899d254..f5c761af4 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -11,12 +11,10 @@ main_filters: &main_filters
- /^(?:release|test)-20\d\d\d\d\d\d\.\d+/
- /\//
- production
- tags:
- only: /^(?:release|test)-20\d\d\d\d\d\d\.\d+/
defaults:
bmo_slim_image: &bmo_slim_image
- image: mozillabteam/bmo-slim:20180314.1
+ image: mozillabteam/bmo-slim:20180330.1
user: app
mysql_image: &mysql_image
@@ -63,12 +61,61 @@ defaults:
run:
name: default qa setup
command: |
+ [[ -f build_info/only_version_changed.txt ]] && exit 0
mv /opt/bmo/local /app/local
perl -MSys::Hostname -i -pE 's/bmo.test/hostname() . ":$ENV{PORT}"/ges' $BZ_QA_CONF_FILE
perl checksetup.pl --no-database --default-localconfig
mkdir artifacts
jobs:
+ build_info:
+ parallelism: 1
+ working_directory: /app
+ docker:
+ - <<: *bmo_slim_image
+ environment:
+ <<: *bmo_env
+ steps:
+ - checkout
+ - run:
+ name: build push data
+ command: |
+ mv /opt/bmo/local /app/local
+ perl checksetup.pl --no-database --no-templates --no-permissions
+ perl scripts/build-bmo-push-data.pl
+ - run:
+ name: only publish if tag exists
+ command: |
+ tag="$(cat build_info/tag.txt)"
+ git fetch --tags
+ if git tag | fgrep -q "$tag"; then
+ echo "tag $tag exists!"
+ else
+ echo "tag $tag does not exist"
+ echo yes > build_info/publish.txt
+ fi
+ - run:
+ name: check if only version changed
+ command: |
+ if git diff 'HEAD~..HEAD' --name-only | grep -qv '^Bugzilla.pm'; then
+ echo "more files than just Bugzilla.pm changed."
+ exit 0
+ fi
+ if git diff 'HEAD~..HEAD' |grep '^[+-][^+-]' | grep -qv '^[+-]our $VERSION'; then
+ echo "Something other than the version number changed."
+ exit 0
+ fi
+ if [[ "$CIRCLE_BRANCH" == "master" ]]; then
+ echo "Can't cut corners on the master branch"
+ exit 0
+ fi
+ echo yes > build_info/only_version_changed.txt
+ - persist_to_workspace:
+ root: /app/build_info
+ paths: ["*.txt"]
+ - store_artifacts:
+ path: /app/build_info
+
build:
working_directory: /app
docker:
@@ -85,37 +132,52 @@ jobs:
--build-arg CIRCLE_SHA1="$CIRCLE_SHA1" \
--build-arg CIRCLE_BUILD_URL="$CIRCLE_BUILD_URL" \
-t bmo .
+ - attach_workspace:
+ at: /app/build_info
+ - run: "docker run --name bmo --entrypoint true bmo"
+ - run: "docker cp bmo:/app/version.json build_info/version.json"
+ - store_artifacts:
+ path: /app/build_info
- deploy:
command: |
- if [[ -n "$DOCKERHUB_REPO" && -n "$DOCKER_USER" && -n "$DOCKER_PASS" ]]; then
- TAG=""
- if [[ -n "$CIRCLE_TAG" ]]; then
- TAG="$CIRCLE_TAG"
- elif [[ "$CIRCLE_BRANCH" == "master" ]]; then
- TAG=latest
- fi
- if [[ -n "$TAG" ]]; then
- docker tag bmo "$DOCKERHUB_REPO:$TAG"
- docker login -u "$DOCKER_USER" -p "$DOCKER_PASS"
- docker push "$DOCKERHUB_REPO:$TAG"
- fi
+ TAG="$(cat /app/build_info/tag.txt)"
+ [[ "$CIRCLE_BRANCH" == "master" ]] || exit 0
+ [[ -n "$DOCKERHUB_REPO" && -n "$DOCKER_USER" && -n "$DOCKER_PASS" ]] || exit 0
+ [[ -n "$GITHUB_PERSONAL_TOKEN" ]] || exit 0
+ docker login -u "$DOCKER_USER" -p "$DOCKER_PASS"
+ if [[ -n "$TAG" && -f build_info/publish.txt ]]; then
+ git config credential.helper "cache --timeout 120"
+ git config user.email "$GITHUB_EMAIL"
+ git config user.name "$GITHUB_NAME"
+ git tag $TAG
+ git push https://${GITHUB_PERSONAL_TOKEN}:x-oauth-basic@github.com/$GITHUB_REPO.git $TAG
+ docker tag bmo "$DOCKERHUB_REPO:$TAG"
+ docker push "$DOCKERHUB_REPO:$TAG"
fi
+ docker tag bmo "$DOCKERHUB_REPO:latest"
+ docker push "$DOCKERHUB_REPO:latest"
test_sanity:
- parallelism: 4
+ parallelism: 2
working_directory: /app
docker:
- <<: *bmo_slim_image
environment: *bmo_env
steps:
- checkout
+ - attach_workspace:
+ at: /app/build_info
- run: |
+ [[ -f build_info/only_version_changed.txt ]] && exit 0
mv /opt/bmo/local /app/local
mkdir artifacts
- - run: perl Makefile.PL
+ - run: |
+ [[ -f build_info/only_version_changed.txt ]] && exit 0
+ perl Makefile.PL
- run:
name: run sanity tests
command: |
+ [[ -f build_info/only_version_changed.txt ]] && exit 0
/app/scripts/entrypoint.pl prove -qf $(circleci tests glob 't/*.t' | circleci tests split) | tee artifacts/$CIRCLE_JOB.txt
- store_artifacts:
path: /app/artifacts
@@ -126,12 +188,15 @@ jobs:
docker: *docker_oldtests
steps:
- checkout
+ - attach_workspace:
+ at: /app/build_info
- *default_qa_setup
- run: |
+ [[ -f build_info/only_version_changed.txt ]] && exit 0
/app/scripts/entrypoint.pl load_test_data
- - run:
- command: |
- /app/scripts/entrypoint.pl test_webservices | tee artifacts/$CIRCLE_JOB.txt
+ - run: |
+ [[ -f build_info/only_version_changed.txt ]] && exit 0
+ /app/scripts/entrypoint.pl test_webservices | tee artifacts/$CIRCLE_JOB.txt
- store_artifacts:
path: /app/artifacts
@@ -141,12 +206,15 @@ jobs:
docker: *docker_oldtests
steps:
- checkout
+ - attach_workspace:
+ at: /app/build_info
- *default_qa_setup
- run: |
+ [[ -f build_info/only_version_changed.txt ]] && exit 0
/app/scripts/entrypoint.pl load_test_data --legacy
- - run:
- command: |
- /app/scripts/entrypoint.pl test_selenium | tee artifacts/$CIRCLE_JOB.txt
+ - run: |
+ [[ -f build_info/only_version_changed.txt ]] && exit 0
+ /app/scripts/entrypoint.pl test_selenium | tee artifacts/$CIRCLE_JOB.txt
- store_artifacts:
path: /app/artifacts
@@ -167,34 +235,45 @@ jobs:
- image: selenium/standalone-firefox:2.53.1
steps:
- checkout
+ - attach_workspace:
+ at: /app/build_info
- run: |
+ [[ -f build_info/only_version_changed.txt ]] && exit 0
mv /opt/bmo/local /app/local
perl checksetup.pl --no-database
/app/scripts/entrypoint.pl load_test_data
mkdir artifacts
- - run: /app/scripts/entrypoint.pl test_bmo -q -f t/bmo/*.t
+ - run: |
+ [[ -f build_info/only_version_changed.txt ]] && exit 0
+ /app/scripts/entrypoint.pl test_bmo -q -f t/bmo/*.t
workflows:
version: 2
main:
jobs:
+ - build_info:
+ filters: *main_filters
+ - build:
+ filters: *main_filters
+ requires:
+ - build_info
+ - test_sanity
+ - test_bmo
+ - test_webservices
+ - test_selenium
- test_sanity:
filters: *main_filters
+ requires:
+ - build_info
- test_bmo:
filters: *main_filters
requires:
- - test_sanity
+ - build_info
- test_webservices:
filters: *main_filters
requires:
- - test_sanity
+ - build_info
- test_selenium:
filters: *main_filters
requires:
- - test_sanity
- - build:
- filters: *main_filters
- requires:
- - test_sanity
- - test_bmo
- - test_webservices
+ - build_info
diff --git a/.htaccess b/.htaccess
index 36195da50..fd14518bc 100644
--- a/.htaccess
+++ b/.htaccess
@@ -27,6 +27,8 @@ RewriteRule ^__version__$ version.json [L]
# heartbeat.cgi returns 200 if the DB and memcached are both working, and 500 otherwise.
RewriteRule ^__heartbeat__$ heartbeat.cgi [L]
+RewriteRule ^(\d+|quicksearch\.html|bugwritinghelp\.html)$ /helper/$1 [L]
+
RewriteRule ^static/v\d{4}\d{2}\d{2}\.\d+/(.+\.(?:js|css|woff2?|png|jpe?g|gif|ico|svg))$ $1 [NC,E=IMMUTABLE:1,L]
Header set Cache-Control "public, max-age=31536000" env=REDIRECT_IMMUTABLE
@@ -37,9 +39,9 @@ RewriteRule ^new[-_]bug$ new_bug.cgi [L,QSA]
RewriteRule ^template_cache/ - [F,L,NC]
RewriteRule ^template_cache.deleteme/ - [F,L,NC]
-RewriteRule ^review(.*) page.cgi?id=splinter.html$1 [QSA]
-RewriteRule ^user_?profile(.*) page.cgi?id=user_profile.html$1 [QSA]
-RewriteRule ^request_defer(.*) page.cgi?id=request_defer.html$1 [QSA]
+RewriteRule ^review$ page.cgi?id=splinter.html$1 [QSA]
+RewriteRule ^user_?profile$ page.cgi?id=user_profile.html$1 [QSA]
+RewriteRule ^request_defer$ page.cgi?id=request_defer.html$1 [QSA]
RewriteRule ^favicon\.ico$ extensions/BMO/web/images/favicon.ico
RewriteRule ^form[\.:]itrequest$ enter_bug.cgi?product=Infrastructure+\%26+Operations&format=itrequest [QSA]
RewriteRule ^form[\.:](mozlist|poweredby|presentation|trademark|recoverykey)$ enter_bug.cgi?product=mozilla.org&format=$1 [QSA]
diff --git a/.perlcriticrc b/.perlcriticrc
index 0c0d1c9be..44254f64e 100644
--- a/.perlcriticrc
+++ b/.perlcriticrc
@@ -32,12 +32,13 @@ severity = 2
[-Documentation::RequirePodLinksIncludeText]
[-Documentation::RequirePodSections]
[-ErrorHandling::RequireCarping]
+[-InputOutput::RequireBracedFileHandleWithPrint]
[-Modules::RequireVersionVar]
[-References::ProhibitDoubleSigils]
[-RegularExpressions::ProhibitComplexRegexes]
[-RegularExpressions::RequireDotMatchAnything]
-[-RegularExpressions::RequireLineBoundaryMatching]
[-RegularExpressions::RequireExtendedFormatting]
+[-RegularExpressions::RequireLineBoundaryMatching]
[-Subroutines::ProhibitExcessComplexity]
[-ValuesAndExpressions::ProhibitConstantPragma]
[-ValuesAndExpressions::ProhibitEmptyQuotes]
diff --git a/Bugzilla.pm b/Bugzilla.pm
index 5d394b908..1188102d3 100644
--- a/Bugzilla.pm
+++ b/Bugzilla.pm
@@ -22,7 +22,7 @@ BEGIN {
}
}
-our $VERSION = '20180316.1';
+our $VERSION = '20180330.1';
use Bugzilla::Auth;
use Bugzilla::Auth::Persist::Cookie;
@@ -631,7 +631,7 @@ sub switch_to_shadow_db {
my $class = shift;
if (!$class->request_cache->{dbh_shadow}) {
- if ($class->params->{'shadowdb'}) {
+ if ($class->get_param_with_override('shadowdb')) {
$class->request_cache->{dbh_shadow} = Bugzilla::DB::connect_shadow();
} else {
$class->request_cache->{dbh_shadow} = $class->dbh_main;
diff --git a/Bugzilla/CGI.pm b/Bugzilla/CGI.pm
index cd947841e..b0bc15e78 100644
--- a/Bugzilla/CGI.pm
+++ b/Bugzilla/CGI.pm
@@ -600,16 +600,18 @@ sub param {
# We don't let CGI.pm warn about list context, but we do it ourselves.
local $CGI::LIST_CONTEXT_WARN = 0;
- state $has_warned = {};
-
- ## no critic (Freenode::Wantarray)
- if ( wantarray && @_ ) {
- my ( $package, $filename, $line ) = caller;
- if ( $package ne 'CGI' && ! $has_warned->{"$filename:$line"}++) {
- WARN("Bugzilla::CGI::param called in list context from $package $filename:$line");
+ if (0) {
+ state $has_warned = {};
+
+ ## no critic (Freenode::Wantarray)
+ if ( wantarray && @_ ) {
+ my ( $package, $filename, $line ) = caller;
+ if ( $package ne 'CGI' && ! $has_warned->{"$filename:$line"}++) {
+ WARN("Bugzilla::CGI::param called in list context from $package $filename:$line");
+ }
}
+ ## use critic
}
- ## use critic
# When we are just requesting the value of a parameter...
if (scalar(@_) == 1) {
diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm
index 00f0f8104..6e3a12736 100644
--- a/Bugzilla/Constants.pm
+++ b/Bugzilla/Constants.pm
@@ -19,7 +19,6 @@ use Memoize;
@Bugzilla::Constants::EXPORT = qw(
BUGZILLA_VERSION
- REST_DOC
REMOTE_FILE
LOCAL_FILE
@@ -209,10 +208,6 @@ sub BUGZILLA_VERSION {
eval { Bugzilla->VERSION } || $bugzilla_version;
}
-# A base link to the current REST Documentation. We place it here
-# as it will need to be updated to whatever the current release is.
-use constant REST_DOC => "https://bugzilla.readthedocs.io/en/latest/api/";
-
# Location of the remote and local XML files to track new releases.
use constant REMOTE_FILE => 'http://updates.bugzilla.org/bugzilla-update.xml';
use constant LOCAL_FILE => 'bugzilla-update.xml'; # Relative to datadir.
diff --git a/Bugzilla/DB.pm b/Bugzilla/DB.pm
index ff75aa2cf..0dfa47c23 100644
--- a/Bugzilla/DB.pm
+++ b/Bugzilla/DB.pm
@@ -111,8 +111,13 @@ sub connect_shadow {
my $connect_params = dclone(Bugzilla->localconfig);
$connect_params->{db_host} = Bugzilla->get_param_with_override('shadowdbhost');
$connect_params->{db_name} = Bugzilla->get_param_with_override('shadowdb');
- $connect_params->{db_port} = Bugzilla->get_param_with_override('shadowport');
- $connect_params->{db_sock} = Bugzilla->get_param_with_override('shadowsock');
+ $connect_params->{db_port} = Bugzilla->get_param_with_override('shadowdbport');
+ $connect_params->{db_sock} = Bugzilla->get_param_with_override('shadowdbsock');
+
+ if ( Bugzilla->localconfig->{'shadowdb_user'} && Bugzilla->localconfig->{'shadowdb_pass'} ) {
+ $connect_params->{db_user} = Bugzilla->localconfig->{'shadowdb_user'};
+ $connect_params->{db_pass} = Bugzilla->localconfig->{'shadowdb_pass'};
+ }
return _connect($connect_params);
}
diff --git a/Bugzilla/Error.pm b/Bugzilla/Error.pm
index e7a99dba0..d67571848 100644
--- a/Bugzilla/Error.pm
+++ b/Bugzilla/Error.pm
@@ -38,15 +38,14 @@ sub _in_eval {
sub _throw_error {
my ($name, $error, $vars) = @_;
- my $dbh = Bugzilla->dbh;
$vars ||= {};
-
$vars->{error} = $error;
# Make sure any transaction is rolled back (if supported).
# If we are within an eval(), do not roll back transactions as we are
# eval'uating some test on purpose.
- $dbh->bz_rollback_transaction() if ($dbh->bz_in_transaction() && !_in_eval());
+ my $dbh = eval { Bugzilla->dbh };
+ $dbh->bz_rollback_transaction() if ($dbh && $dbh->bz_in_transaction() && !_in_eval());
my $datadir = bz_locations()->{'datadir'};
# If a writable $datadir/errorlog exists, log error details there.
@@ -191,10 +190,9 @@ sub ThrowCodeError {
sub ThrowTemplateError {
my ($template_err) = @_;
- my $dbh = Bugzilla->dbh;
-
+ my $dbh = eval { Bugzilla->dbh };
# Make sure the transaction is rolled back (if supported).
- $dbh->bz_rollback_transaction() if $dbh->bz_in_transaction();
+ $dbh->bz_rollback_transaction() if $dbh && $dbh->bz_in_transaction();
if (blessed($template_err) && $template_err->isa('Template::Exception')) {
my $type = $template_err->type;
diff --git a/Bugzilla/Install/Localconfig.pm b/Bugzilla/Install/Localconfig.pm
index de2219f16..7a913358c 100644
--- a/Bugzilla/Install/Localconfig.pm
+++ b/Bugzilla/Install/Localconfig.pm
@@ -175,6 +175,14 @@ use constant LOCALCONFIG_VARS => (
name => 'inbound_proxies',
default => _migrate_param( 'inbound_proxies', '' ),
},
+ {
+ name => 'shadowdb_user',
+ default => '',
+ },
+ {
+ name => 'shadowdb_pass',
+ default => '',
+ }
);
diff --git a/Bugzilla/JobQueue.pm b/Bugzilla/JobQueue.pm
index 8c8d73dcd..53b088c6e 100644
--- a/Bugzilla/JobQueue.pm
+++ b/Bugzilla/JobQueue.pm
@@ -101,12 +101,12 @@ sub debug {
my $caller_pkg = caller;
local $Log::Log4perl::caller_depth = $Log::Log4perl::caller_depth + 1;
my $logger = Log::Log4perl->get_logger($caller_pkg);
- $logger->debug(@args);
+ $logger->info(@args);
}
sub work {
my ($self, $delay) = @_;
- $delay ||= 5;
+ $delay ||= 1;
my $loop = IO::Async::Loop->new;
my $timer = IO::Async::Timer::Periodic->new(
first_interval => 0,
diff --git a/Bugzilla/JobQueue/Runner.pm b/Bugzilla/JobQueue/Runner.pm
index 1c74bc28f..0177de40a 100644
--- a/Bugzilla/JobQueue/Runner.pm
+++ b/Bugzilla/JobQueue/Runner.pm
@@ -26,7 +26,7 @@ use Cwd qw(abs_path);
use English qw(-no_match_vars $PROGRAM_NAME $EXECUTABLE_NAME);
use File::Basename;
use File::Copy;
-use File::Spec::Functions qw(catfile);
+use File::Spec::Functions qw(catfile tmpdir);
use Future;
use Future::Utils qw(fmap_void);
use IO::Async::Loop;
@@ -52,7 +52,7 @@ sub gd_preconfig {
my $pidfile = $self->{gd_args}{pidfile};
if ( !$pidfile ) {
- $pidfile = bz_locations()->{datadir} . '/' . $self->{gd_progname} . '.pid';
+ $pidfile = catfile(tmpdir(), $self->{gd_progname} . '.pid');
}
return ( pidfile => $pidfile );
}
@@ -211,7 +211,7 @@ sub gd_run {
my $self = shift;
my $jobs = $self->{gd_args}{jobs} // 1;
my $signal_f = $self->{_signal_future};
- my $workers_f = fmap_void { $self->run_worker("work") }
+ my $workers_f = fmap_void { $self->run_worker() }
concurrent => $jobs,
generate => sub { !$signal_f->is_ready };
@@ -225,10 +225,10 @@ sub gd_run {
# This executes the script "jobqueue-worker.pl"
# $EXECUTABLE_NAME is the name of the perl interpreter.
sub run_worker {
- my ( $self, $fn ) = @_;
+ my ( $self ) = @_;
my $script = catfile( bz_locations->{cgi_path}, 'jobqueue-worker.pl' );
- my @command = ( $EXECUTABLE_NAME, $script, '--function' => $fn );
+ my @command = ( $EXECUTABLE_NAME, $script);
if ( $self->{gd_args}{progname} ) {
push @command, '--name' => "$self->{gd_args}{progname} [worker]";
}
diff --git a/Bugzilla/Mailer.pm b/Bugzilla/Mailer.pm
index e245a05e0..1dec3d4ff 100644
--- a/Bugzilla/Mailer.pm
+++ b/Bugzilla/Mailer.pm
@@ -252,15 +252,14 @@ sub build_thread_marker {
$sitespec = "-$2$sitespec"; # Put the port number back in, before the '@'
}
- my $threadingmarker;
+ my $threadingmarker = "References: <bug-$bug_id-$user_id$sitespec>";
if ($is_new) {
- $threadingmarker = "Message-ID: <bug-$bug_id-$user_id$sitespec>";
+ $threadingmarker .= "\nMessage-ID: <bug-$bug_id-$user_id$sitespec>";
}
else {
my $rand_bits = generate_random_password(10);
- $threadingmarker = "Message-ID: <bug-$bug_id-$user_id-$rand_bits$sitespec>" .
- "\nIn-Reply-To: <bug-$bug_id-$user_id$sitespec>" .
- "\nReferences: <bug-$bug_id-$user_id$sitespec>";
+ $threadingmarker .= "\nMessage-ID: <bug-$bug_id-$user_id-$rand_bits$sitespec>" .
+ "\nIn-Reply-To: <bug-$bug_id-$user_id$sitespec>";
}
return $threadingmarker;
diff --git a/Bugzilla/Memcached.pm b/Bugzilla/Memcached.pm
index 85e3505e1..d34aaa595 100644
--- a/Bugzilla/Memcached.pm
+++ b/Bugzilla/Memcached.pm
@@ -36,11 +36,25 @@ sub _new {
if (Bugzilla->feature('memcached') && $servers) {
$self->{namespace} = Bugzilla->localconfig->{memcached_namespace};
TRACE("connecting servers: $servers, namespace: $self->{namespace}");
- $self->{memcached} = Cache::Memcached::Fast->new({
- servers => [ _parse_memcached_server_list($servers) ],
- namespace => $self->{namespace},
- max_size => 1024 * 1024 * 4,
- });
+ $self->{memcached} = Cache::Memcached::Fast->new(
+ {
+ servers => [ _parse_memcached_server_list($servers) ],
+ namespace => $self->{namespace},
+ max_size => 1024 * 1024 * 4,
+ max_failures => 1,
+ failure_timeout => 60,
+ io_timeout => 0.2,
+ connect_timeout => 0.2,
+ }
+ );
+ my $versions = $self->{memcached}->server_versions;
+ if (keys %$versions) {
+ # this is needed to ensure forked processes don't start out with a connected memcached socket.
+ $self->{memcached}->disconnect_all;
+ }
+ else {
+ WARN("No memcached servers");
+ }
}
else {
TRACE("memcached feature is not enabled");
@@ -324,7 +338,7 @@ sub _get {
my $enc_key = $self->_encode_key($key)
or return;
- my $val = $self->{memcached}->get($key);
+ my $val = $self->{memcached}->get($enc_key);
TRACE("get $enc_key: " . (defined $val ? "HIT" : "MISS"));
return $val;
}
diff --git a/Bugzilla/ModPerl.pm b/Bugzilla/ModPerl.pm
index a5c840897..120dd8210 100644
--- a/Bugzilla/ModPerl.pm
+++ b/Bugzilla/ModPerl.pm
@@ -20,6 +20,7 @@ use Carp ();
use Template ();
use Bugzilla::ModPerl::BlockIP;
+use Bugzilla::ModPerl::Hostage;
sub apache_config {
my ($class, $cgi_path) = @_;
@@ -74,6 +75,7 @@ __DATA__
# the built-in rand(), even though we never use it in Bugzilla itself,
# so we need to srand() both of them.)
PerlChildInitHandler "sub { Bugzilla::RNG::srand(); srand(); }"
+PerlInitHandler Bugzilla::ModPerl::Hostage
PerlAccessHandler Bugzilla::ModPerl::BlockIP
# It is important to specify ErrorDocuments outside of all directories.
@@ -84,6 +86,12 @@ ErrorDocument 403 /errors/403.html
ErrorDocument 404 /errors/404.html
ErrorDocument 500 /errors/500.html
+<Location /helper>
+ SetHandler perl-script
+ PerlResponseHandler Plack::Handler::Apache2
+ PerlSetVar psgi_app [% cgi_path %]/helper.psgi
+</Location>
+
<Directory "[% cgi_path %]">
AddHandler perl-script .cgi
# No need to PerlModule these because they're already defined in mod_perl.pl
diff --git a/Bugzilla/ModPerl/BasicAuth.pm b/Bugzilla/ModPerl/BasicAuth.pm
index e93680e9d..7248a19f3 100644
--- a/Bugzilla/ModPerl/BasicAuth.pm
+++ b/Bugzilla/ModPerl/BasicAuth.pm
@@ -25,18 +25,22 @@ use warnings;
# AUTH_VAR_NAME and AUTH_VAR_PASS are the names of variables defined in
# `localconfig` which hold the authentication credentials.
-use Apache2::Const -compile => qw(OK HTTP_UNAUTHORIZED);
+use Apache2::Const -compile => qw(OK HTTP_UNAUTHORIZED); ## no critic (Freenode::ModPerl)
+use Bugzilla::Logging;
use Bugzilla ();
sub handler {
my $r = shift;
my ($status, $password) = $r->get_basic_auth_pw;
- return $status if $status != Apache2::Const::OK;
+ if ($status != Apache2::Const::OK) {
+ WARN("Got non-OK status: $status when trying to get password");
+ return $status
+ }
my $auth_var_name = $ENV{AUTH_VAR_NAME};
my $auth_var_pass = $ENV{AUTH_VAR_PASS};
unless ($auth_var_name && $auth_var_pass) {
- warn "AUTH_VAR_NAME and AUTH_VAR_PASS environmental vars not set\n";
+ ERROR('AUTH_VAR_NAME and AUTH_VAR_PASS environmental vars not set');
$r->note_basic_auth_failure;
return Apache2::Const::HTTP_UNAUTHORIZED;
}
@@ -44,13 +48,14 @@ sub handler {
my $auth_user = Bugzilla->localconfig->{$auth_var_name};
my $auth_pass = Bugzilla->localconfig->{$auth_var_pass};
unless ($auth_user && $auth_pass) {
- warn "$auth_var_name and $auth_var_pass not configured\n";
+ ERROR("$auth_var_name and $auth_var_pass not configured");
$r->note_basic_auth_failure;
return Apache2::Const::HTTP_UNAUTHORIZED;
}
unless ($r->user eq $auth_user && $password eq $auth_pass) {
$r->note_basic_auth_failure;
+ WARN('username and password do not match');
return Apache2::Const::HTTP_UNAUTHORIZED;
}
diff --git a/Bugzilla/ModPerl/Hostage.pm b/Bugzilla/ModPerl/Hostage.pm
new file mode 100644
index 000000000..a3bdfac58
--- /dev/null
+++ b/Bugzilla/ModPerl/Hostage.pm
@@ -0,0 +1,71 @@
+package Bugzilla::ModPerl::Hostage;
+use 5.10.1;
+use strict;
+use warnings;
+
+use Apache2::Const qw(:common); ## no critic (Freenode::ModPerl)
+
+sub _attachment_root {
+ my ($base) = @_;
+ return undef unless $base;
+ return $base =~ m{^https?://(?:bug)?\%bugid\%\.([a-zA-Z\.-]+)}
+ ? $1
+ : undef;
+}
+
+sub _attachment_host_regex {
+ my ($base) = @_;
+ return undef unless $base;
+ my $val = $base;
+ $val =~ s{^https?://}{}s;
+ $val =~ s{/$}{}s;
+ my $regex = quotemeta $val;
+ $regex =~ s/\\\%bugid\\\%/\\d+/g;
+ return qr/^$regex$/s;
+}
+
+sub handler {
+ my $r = shift;
+ state $urlbase = Bugzilla->localconfig->{urlbase};
+ state $urlbase_uri = URI->new($urlbase);
+ state $urlbase_host = $urlbase_uri->host;
+ state $urlbase_host_regex = qr/^bug(\d+)\.\Q$urlbase_host\E$/;
+ state $attachment_base = Bugzilla->localconfig->{attachment_base};
+ state $attachment_root = _attachment_root($attachment_base);
+ state $attachment_host_regex = _attachment_host_regex($attachment_base);
+
+ my $hostname = $r->hostname;
+ return OK if $hostname eq $urlbase_host;
+
+ my $path = $r->uri;
+ return OK if $path eq '/__lbheartbeat__';
+
+ if ($attachment_base && $hostname eq $attachment_root) {
+ $r->headers_out->set(Location => $urlbase);
+ return REDIRECT;
+ }
+ elsif ($attachment_base && $hostname =~ $attachment_host_regex) {
+ if ($path =~ m{^/attachment\.cgi}s) {
+ return OK;
+ } else {
+ my $new_uri = URI->new($r->unparsed_uri);
+ $new_uri->scheme($urlbase_uri->scheme);
+ $new_uri->host($urlbase_host);
+ $r->headers_out->set(Location => $new_uri);
+ return REDIRECT;
+ }
+ }
+ elsif (my ($id) = $hostname =~ $urlbase_host_regex) {
+ my $new_uri = $urlbase_uri->clone;
+ $new_uri->path('/show_bug.cgi');
+ $new_uri->query_form(id => $id);
+ $r->headers_out->set(Location => $new_uri);
+ return REDIRECT;
+ }
+ else {
+ $r->headers_out->set(Location => $urlbase);
+ return REDIRECT;
+ }
+}
+
+1; \ No newline at end of file
diff --git a/Bugzilla/WebService/Server/REST.pm b/Bugzilla/WebService/Server/REST.pm
index 6fb86fdd4..b8884b753 100644
--- a/Bugzilla/WebService/Server/REST.pm
+++ b/Bugzilla/WebService/Server/REST.pm
@@ -132,7 +132,8 @@ sub response {
if (exists $json_data->{error}) {
$result = $json_data->{error};
$result->{error} = $self->type('boolean', 1);
- $result->{documentation} = REST_DOC;
+
+ $result->{documentation} = Bugzilla->params->{docs_urlbase} . "api/";
delete $result->{'name'}; # Remove JSONRPCError
}
elsif (exists $json_data->{result}) {
diff --git a/Dockerfile b/Dockerfile
index 056c8185a..6aa2f2646 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM mozillabteam/bmo-slim:20180314.1
+FROM mozillabteam/bmo-slim:20180330.1
ARG CI
ARG CIRCLE_SHA1
diff --git a/Makefile.PL b/Makefile.PL
index ceb0fc97c..9f56cd487 100755
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -50,6 +50,7 @@ my %requires = (
'File::Slurp' => '9999.13',
'Future' => '0.34',
'HTML::Escape' => '1.10',
+ 'IPC::System::Simple' => 0,
'IO::Async' => '0.71',
'JSON::MaybeXS' => '1.003008',
'JSON::XS' => '2.01',
@@ -271,6 +272,7 @@ my %optional_features = (
requires => {
'mod_perl2' => '1.999022',
'Apache2::SizeLimit' => '0.96',
+ 'Plack::Handler::Apache2' => 0,
}
}
}
diff --git a/README.rst b/README.rst
index 1576f07b7..64fae335b 100644
--- a/README.rst
+++ b/README.rst
@@ -19,6 +19,8 @@ BMO is Mozilla's highly customized version of Bugzilla.
3.1 Container Arguments
3.2 Environmental Variables
3.3 Persistent Data Volume
+ 4. Development Tips
+ 4.1 Testing Emails
If you want to contribute to BMO, you can fork this repo and get a local copy
of BMO running in a few minutes using Vagrant or Docker.
@@ -300,7 +302,7 @@ BMO_apache_size_limit
BMO_mail_delivery_method
Usually configured on the MTA section of admin interface, but may be set here for testing purposes.
Valid values are None, Test, Sendmail, or SMTP.
- If set to Test, email will be appended to the /app/data/mailer.test file.
+ If set to Test, email will be appended to the /app/data/mailer.testfile.
BMO_use_mailer_queue
Usually configured on the MTA section of the admin interface, you may change this here for testing purposes.
@@ -365,3 +367,48 @@ Persistent Data Volume
This container expects /app/data to be a persistent, shared, writable directory
owned by uid 10001. This must be a shared (NFS/EFS/etc) volume between all
nodes.
+
+Development Tips
+================
+
+Testing Emails
+--------------
+
+With vagrant have two options to test emails sent by a local bugzilla instance. You can configure
+which setting you want to use by going to http://bmo-web.vm/editparams.cgi?section=mta and
+changing the mail_delivery_method to either 'Test' or 'Sendmail'. Afterwards restart bmo with
+``vagrant reload``. With docker, only the default 'Test' option is supported.
+
+'Test' option (Default for Docker)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+With this option, all mail will be appended to a ``mailer.testfile``.
+
+- Using docker, run ``docker-compose run bmo-web.vm cat /app/data/mailer.testfile``.
+- Using vagrant, run ``vagrant ssh web`` and then naviage to ``/vagrant/data/mailer.testfile``.
+
+'Sendmail' option (Default for Vagrant)
+~~~~~~~~~~~~~~~~~
+
+This option is useful if you want to preview email using a real mail client.
+An imap server is running on bmo-web.vm on port 143 and you can connect to it with
+the following settings:
+
+- host: bmo-web.vm
+- port: 143
+- encryption: No SSL, Plaintext password
+- username: vagrant
+- password: anything
+
+All email that bmo sends will go to the vagrant user, so there is no need to login with
+multiple imap accounts.
+
+`Thunderbird's`_ wizard to add a new "Existing Mail Account" doesn't work with bmo-web. It
+fails because it wants to create a mail account with both incoming mail (IMAP) and outgoing
+mail (SMTP, which bmo-web.vm doesn't provide). To work around this, using a regular email
+account to first setup, then modify the settings of that account: Right Click the account in
+the left side bar > Settings > Server Settings. Update the server settings to match those
+listed above. Afterwards, you may update the account name to be vagrant@bmo-web.vm. Thunderbird
+will now pull email from bmo. You can try it out by commenting on a bug.
+
+.. _`Thunderbird's`: https://www.mozilla.org/en-US/thunderbird/
diff --git a/conf/httpd.conf b/conf/httpd.conf
index 20a21f5db..7fe859b59 100644
--- a/conf/httpd.conf
+++ b/conf/httpd.conf
@@ -4,7 +4,8 @@ ServerRoot "/etc/httpd"
ServerAdmin root@localhost
PidFile /tmp/httpd.pid
-Timeout 60
+Timeout 120
+LimitRequestLine 35000
KeepAlive Off
MaxKeepAliveRequests 100
KeepAliveTimeout 15
@@ -91,7 +92,7 @@ DocumentRoot "/app"
Alias "/bmo" "/app"
</IfDefine>
<IfDefine HTTPS>
- SetEnv HTTPS on
+ SetEnvIf X-Forwarded-Proto "https" HTTPS=on
</IfDefine>
<Directory "/app">
Options -Indexes -FollowSymLinks
diff --git a/extensions/BMO/template/en/default/pages/user_activity.html.tmpl b/extensions/BMO/template/en/default/pages/user_activity.html.tmpl
index 075c8edf9..5603b943f 100644
--- a/extensions/BMO/template/en/default/pages/user_activity.html.tmpl
+++ b/extensions/BMO/template/en/default/pages/user_activity.html.tmpl
@@ -199,6 +199,7 @@
change.fieldname == 'reporter' ||
change.fieldname == 'qa_contact' ||
change.fieldname == 'cc' ||
+ change.fieldname == 'bug_mentor' ||
change.fieldname == 'flagtypes.name' %]
[% display_value(change.fieldname, change_type) FILTER email FILTER html %]
[% ELSE %]
diff --git a/extensions/BugModal/template/en/default/bug_modal/activity_stream.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/activity_stream.html.tmpl
index a8d55c137..51919ab27 100644
--- a/extensions/BugModal/template/en/default/bug_modal/activity_stream.html.tmpl
+++ b/extensions/BugModal/template/en/default/bug_modal/activity_stream.html.tmpl
@@ -365,7 +365,7 @@
", " UNLESS loop.last;
END;
- CASE [ 'assigned_to', 'reporter', 'qa_contact', 'cc', 'flagtypes.name' ];
+ CASE [ 'assigned_to', 'reporter', 'qa_contact', 'cc', 'bug_mentor', 'flagtypes.name' ];
value FILTER email;
CASE 'reporter_accessible';
diff --git a/extensions/InlineHistory/template/en/default/hook/bug/comments-aftercomments.html.tmpl b/extensions/InlineHistory/template/en/default/hook/bug/comments-aftercomments.html.tmpl
index d0a3abb5b..32e6499cb 100644
--- a/extensions/InlineHistory/template/en/default/hook/bug/comments-aftercomments.html.tmpl
+++ b/extensions/InlineHistory/template/en/default/hook/bug/comments-aftercomments.html.tmpl
@@ -155,6 +155,7 @@
change.fieldname == 'reporter' ||
change.fieldname == 'qa_contact' ||
change.fieldname == 'cc' ||
+ change.fieldname == 'bug_mentor' ||
change.fieldname == 'flagtypes.name' %]
[% value FILTER email FILTER js %]
[% ELSIF change.fieldtype == constants.FIELD_TYPE_DATETIME %]
diff --git a/extensions/OpenGraph/template/en/default/hook/global/header-start.html.tmpl b/extensions/OpenGraph/template/en/default/hook/global/header-start.html.tmpl
index 9b8e0e0cc..2d7c3379f 100644
--- a/extensions/OpenGraph/template/en/default/hook/global/header-start.html.tmpl
+++ b/extensions/OpenGraph/template/en/default/hook/global/header-start.html.tmpl
@@ -8,6 +8,11 @@
[% USE Bugzilla %]
<meta property="og:type" content="website">
-<meta property="og:image" content="[% urlbase FILTER none %]extensions/OpenGraph/web/moz-social-bw-rgb-1200x1200.png">
<meta property="og:title" content="[% title FILTER none %]">
<meta property="og:url" content="[% Bugzilla.cgi.self_url FILTER html %]">
+[% IF bug %]
+<meta property="og:description"
+ content="[% bug.bug_status FILTER html %] ([% bug.assigned_to.login FILTER email FILTER html %]) in [% bug.product FILTER html %] - [% bug.component FILTER html %]. Last updated [% bug.delta_ts FILTER time('%Y-%m-%d') %].">
+[% ELSE %]
+<meta property="og:image" content="[% urlbase FILTER none %]extensions/OpenGraph/web/moz-social-bw-rgb-1200x1200.png">
+[% END %]
diff --git a/extensions/PhabBugz/Extension.pm b/extensions/PhabBugz/Extension.pm
index b3ad44819..ee96901a2 100644
--- a/extensions/PhabBugz/Extension.pm
+++ b/extensions/PhabBugz/Extension.pm
@@ -15,7 +15,6 @@ use parent qw(Bugzilla::Extension);
use Bugzilla::Constants;
use Bugzilla::Extension::PhabBugz::Feed;
-use Bugzilla::Extension::PhabBugz::Logger;
our $VERSION = '0.01';
diff --git a/extensions/PhabBugz/lib/Daemon.pm b/extensions/PhabBugz/lib/Daemon.pm
index c8b4f73af..ef4a00534 100644
--- a/extensions/PhabBugz/lib/Daemon.pm
+++ b/extensions/PhabBugz/lib/Daemon.pm
@@ -13,7 +13,6 @@ use warnings;
use Bugzilla::Constants;
use Bugzilla::Extension::PhabBugz::Feed;
-use Bugzilla::Extension::PhabBugz::Logger;
use Carp qw(confess);
use Daemon::Generic;
@@ -89,11 +88,9 @@ sub gd_setup_signals {
sub gd_run {
my $self = shift;
- $::SIG{__DIE__} = \&Carp::confess if $self->{debug};
+ $SIG{__DIE__} = \&Carp::confess if $self->{debug};
my $phabbugz = Bugzilla::Extension::PhabBugz::Feed->new();
$phabbugz->is_daemon(1);
- $phabbugz->logger(
- Bugzilla::Extension::PhabBugz::Logger->new(debugging => $self->{debug}));
$phabbugz->start();
}
diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm
index 9904d5090..074ecc0f9 100644
--- a/extensions/PhabBugz/lib/Feed.pm
+++ b/extensions/PhabBugz/lib/Feed.pm
@@ -13,6 +13,7 @@ use List::Util qw(first);
use List::MoreUtils qw(any);
use Moo;
+use Bugzilla::Logging;
use Bugzilla::Constants;
use Bugzilla::Search;
use Bugzilla::Util qw(diff_arrays with_writable_database with_readonly_database);
@@ -36,7 +37,6 @@ use Bugzilla::Extension::PhabBugz::Util qw(
);
has 'is_daemon' => ( is => 'rw', default => 0 );
-has 'logger' => ( is => 'rw' );
sub start {
my ($self) = @_;
@@ -48,7 +48,7 @@ sub start {
}
1;
};
- $self->logger->error( $@ // "unknown exception" ) unless $ok;
+ ERROR( $@ // "unknown exception" ) unless $ok;
sleep(PHAB_POLL_SECONDS);
}
}
@@ -59,19 +59,19 @@ sub feed_query {
# Ensure Phabricator syncing is enabled
if (!Bugzilla->params->{phabricator_enabled}) {
- $self->logger->info("PHABRICATOR SYNC DISABLED");
+ INFO("PHABRICATOR SYNC DISABLED");
return;
}
# PROCESS NEW FEED TRANSACTIONS
- $self->logger->info("FEED: Fetching new transactions");
+ INFO("FEED: Fetching new transactions");
my $story_last_id = $self->get_last_id('feed');
# Check for new transctions (stories)
my $new_stories = $self->new_stories($story_last_id);
- $self->logger->info("FEED: No new stories") unless @$new_stories;
+ INFO("FEED: No new stories") unless @$new_stories;
# Process each story
foreach my $story_data (@$new_stories) {
@@ -81,15 +81,15 @@ sub feed_query {
my $object_phid = $story_data->{objectPHID};
my $story_text = $story_data->{text};
- $self->logger->debug("STORY ID: $story_id");
- $self->logger->debug("STORY PHID: $story_phid");
- $self->logger->debug("AUTHOR PHID: $author_phid");
- $self->logger->debug("OBJECT PHID: $object_phid");
- $self->logger->info("STORY TEXT: $story_text");
+ DEBUG("STORY ID: $story_id");
+ DEBUG("STORY PHID: $story_phid");
+ DEBUG("AUTHOR PHID: $author_phid");
+ DEBUG("OBJECT PHID: $object_phid");
+ INFO("STORY TEXT: $story_text");
# Only interested in changes to revisions for now.
if ($object_phid !~ /^PHID-DREV/) {
- $self->logger->debug("SKIPPING: Not a revision change");
+ DEBUG("SKIPPING: Not a revision change");
$self->save_last_id($story_id, 'feed');
next;
}
@@ -99,7 +99,7 @@ sub feed_query {
if (@$phab_users) {
my $user = Bugzilla::User->new({ id => $phab_users->[0]->{id}, cache => 1 });
if ($user->login eq PHAB_AUTOMATION_USER) {
- $self->logger->debug("SKIPPING: Change made by phabricator user");
+ DEBUG("SKIPPING: Change made by phabricator user");
$self->save_last_id($story_id, 'feed');
next;
}
@@ -113,13 +113,13 @@ sub feed_query {
# PROCESS NEW USERS
- $self->logger->info("FEED: Fetching new users");
+ INFO("FEED: Fetching new users");
my $user_last_id = $self->get_last_id('user');
# Check for new users
my $new_users = $self->new_users($user_last_id);
- $self->logger->info("FEED: No new users") unless @$new_users;
+ INFO("FEED: No new users") unless @$new_users;
# Process each new user
foreach my $user_data (@$new_users) {
@@ -128,10 +128,10 @@ sub feed_query {
my $user_realname = $user_data->{fields}{realName};
my $object_phid = $user_data->{phid};
- $self->logger->debug("USER ID: $user_id");
- $self->logger->debug("USER LOGIN: $user_login");
- $self->logger->debug("USER REALNAME: $user_realname");
- $self->logger->debug("OBJECT PHID: $object_phid");
+ DEBUG("USER ID: $user_id");
+ DEBUG("USER LOGIN: $user_login");
+ DEBUG("USER REALNAME: $user_realname");
+ DEBUG("OBJECT PHID: $object_phid");
with_readonly_database {
$self->process_new_user($user_data);
@@ -151,15 +151,15 @@ sub process_revision_change {
if (!$revision->bug_id) {
if ($story_text =~ /\s+created\s+D\d+/) {
# If new revision and bug id was omitted, make revision public
- $self->logger->debug("No bug associated with new revision. Marking public.");
+ DEBUG("No bug associated with new revision. Marking public.");
$revision->set_policy('view', 'public');
$revision->set_policy('edit', 'users');
$revision->update();
- $self->logger->info("SUCCESS");
+ INFO("SUCCESS");
return;
}
else {
- $self->logger->debug("SKIPPING: No bug associated with revision change");
+ DEBUG("SKIPPING: No bug associated with revision change");
return;
}
}
@@ -170,7 +170,7 @@ sub process_revision_change {
$revision->title,
$revision->bug_id,
$story_text);
- $self->logger->info($log_message);
+ INFO($log_message);
# Pre setup before making changes
my $old_user = set_phab_user();
@@ -180,7 +180,7 @@ sub process_revision_change {
# If bug is public then remove privacy policy
if (!@{ $bug->groups_in }) {
- $self->logger->debug('Bug is public so setting view/edit public');
+ DEBUG('Bug is public so setting view/edit public');
$revision->set_policy('view', 'public');
$revision->set_policy('edit', 'users');
my $secure_project_phid = get_project_phid('secure-revision');
@@ -193,7 +193,7 @@ sub process_revision_change {
# If bug privacy groups do not have any matching synchronized groups,
# then leave revision private and it will have be dealt with manually.
if (!@set_groups) {
- $self->logger->debug('No matching groups. Adding comments to bug and revision');
+ DEBUG('No matching groups. Adding comments to bug and revision');
add_security_sync_comments([$revision], $bug);
}
# Otherwise, we create a new custom policy containing the project
@@ -205,23 +205,23 @@ sub process_revision_change {
# we leave the current policy alone.
my $current_policy;
if ($revision->view_policy =~ /^PHID-PLCY/) {
- $self->logger->debug("Loading current policy: " . $revision->view_policy);
+ DEBUG("Loading current policy: " . $revision->view_policy);
$current_policy
= Bugzilla::Extension::PhabBugz::Policy->new_from_query({ phids => [ $revision->view_policy ]});
my $current_projects = $current_policy->rule_projects;
- $self->logger->debug("Current policy projects: " . join(", ", @$current_projects));
+ DEBUG("Current policy projects: " . join(", ", @$current_projects));
my ($added, $removed) = diff_arrays($current_projects, \@set_projects);
if (@$added || @$removed) {
- $self->logger->debug('Project groups do not match. Need new custom policy');
+ DEBUG('Project groups do not match. Need new custom policy');
$current_policy= undef;
}
else {
- $self->logger->debug('Project groups match. Leaving current policy as-is');
+ DEBUG('Project groups match. Leaving current policy as-is');
}
}
if (!$current_policy) {
- $self->logger->debug("Creating new custom policy: " . join(", ", @set_projects));
+ DEBUG("Creating new custom policy: " . join(", ", @set_projects));
my $new_policy = Bugzilla::Extension::PhabBugz::Policy->create(\@set_projects);
$revision->set_policy('view', $new_policy->phid);
$revision->set_policy('edit', $new_policy->phid);
@@ -229,15 +229,17 @@ sub process_revision_change {
my $secure_project_phid = get_project_phid('secure-revision');
$revision->add_project($secure_project_phid);
-
- my $subscribers = get_bug_role_phids($bug);
- $revision->set_subscribers($subscribers);
}
+
+ # 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 ($timestamp) = Bugzilla->dbh->selectrow_array("SELECT NOW()");
- my $attachment = create_revision_attachment($bug, $revision->id, $revision->title, $timestamp);
+ my $attachment = create_revision_attachment($bug, $revision, $timestamp);
# ATTACHMENT OBSOLETES
@@ -250,11 +252,11 @@ sub process_revision_change {
next if $attach_revision_id != $revision->id;
my $make_obsolete = $revision->status eq 'abandoned' ? 1 : 0;
- $self->logger->debug('Updating obsolete status on attachmment ' . $attachment->id);
+ DEBUG('Updating obsolete status on attachmment ' . $attachment->id);
$attachment->set_is_obsolete($make_obsolete);
if ($revision->title ne $attachment->description) {
- $self->logger->debug('Updating description on attachment ' . $attachment->id);
+ DEBUG('Updating description on attachment ' . $attachment->id);
$attachment->set_description($revision->title);
}
@@ -270,7 +272,7 @@ sub process_revision_change {
});
foreach my $attachment (@$other_attachments) {
$other_bugs{$attachment->bug_id}++;
- $self->logger->debug('Updating obsolete status on attachment ' .
+ DEBUG('Updating obsolete status on attachment ' .
$attachment->id . " for bug " . $attachment->bug_id);
$attachment->set_is_obsolete(1);
$attachment->update($timestamp);
@@ -291,6 +293,8 @@ sub process_revision_change {
$phab_users = get_phab_bmo_ids({ phids => \@denied_phids });
@denied_user_ids = map { $_->{id} } @$phab_users;
+ my %reviewers_hash = map { $_->name => 1 } @{ $revision->reviewers };
+
foreach my $attachment (@attachments) {
my ($attach_revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN);
next if $revision->id != $attach_revision_id;
@@ -331,7 +335,11 @@ sub process_revision_change {
$comment .= $flag_data->{setter}->name . " has requested changes to the revision.\n";
}
foreach my $flag_data (@removed_flags) {
- $comment .= $flag_data->{setter}->name . " has been removed from the revision.\n";
+ if ( exists $reviewers_hash{$flag_data->{setter}->name} ) {
+ $comment .= "Flag set by " . $flag_data->{setter}->name . " is no longer active.\n";
+ } else {
+ $comment .= $flag_data->{setter}->name . " has been removed from the revision.\n";
+ }
}
if ($comment) {
@@ -362,7 +370,7 @@ sub process_revision_change {
Bugzilla->set_user($old_user);
- $self->logger->info('SUCCESS: Revision D' . $revision->id . ' processed');
+ INFO('SUCCESS: Revision D' . $revision->id . ' processed');
}
sub process_new_user {
@@ -372,7 +380,7 @@ sub process_new_user {
my $phab_user = Bugzilla::Extension::PhabBugz::User->new($user_data);
if (!$phab_user->bugzilla_id) {
- $self->logger->debug("SKIPPING: No bugzilla id associated with user");
+ DEBUG("SKIPPING: No bugzilla id associated with user");
return;
}
@@ -425,7 +433,7 @@ sub process_new_user {
my @bug_ids = map { shift @$_ } @$data;
foreach my $bug_id (@bug_ids) {
- $self->logger->debug("Processing bug $bug_id");
+ DEBUG("Processing bug $bug_id");
my $bug = Bugzilla::Bug->new({ id => $bug_id, cache => 1 });
@@ -434,7 +442,7 @@ sub process_new_user {
foreach my $attachment (@attachments) {
my ($revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN);
- $self->logger->debug("Processing revision D$revision_id");
+ DEBUG("Processing revision D$revision_id");
my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query(
{ ids => [ int($revision_id) ] });
@@ -442,13 +450,13 @@ sub process_new_user {
$revision->add_subscriber($phab_user->phid);
$revision->update();
- $self->logger->debug("Revision $revision_id updated");
+ DEBUG("Revision $revision_id updated");
}
}
Bugzilla->set_user($old_user);
- $self->logger->info('SUCCESS: User ' . $phab_user->id . ' processed');
+ INFO('SUCCESS: User ' . $phab_user->id . ' processed');
}
##################
@@ -496,7 +504,7 @@ sub get_last_id {
my $last_id = Bugzilla->dbh->selectrow_array( "
SELECT value FROM phabbugz WHERE name = ?", undef, $type_full );
$last_id ||= 0;
- $self->logger->debug( "QUERY " . uc($type_full) . ": $last_id" );
+ DEBUG( "QUERY " . uc($type_full) . ": $last_id" );
return $last_id;
}
@@ -505,7 +513,7 @@ sub save_last_id {
# Store the largest last key so we can start from there in the next session
my $type_full = $type . "_last_id";
- $self->logger->debug( "UPDATING " . uc($type_full) . ": $last_id" );
+ DEBUG( "UPDATING " . uc($type_full) . ": $last_id" );
Bugzilla->dbh->do( "REPLACE INTO phabbugz (name, value) VALUES (?, ?)",
undef, $type_full, $last_id );
}
diff --git a/extensions/PhabBugz/lib/Logger.pm b/extensions/PhabBugz/lib/Logger.pm
deleted file mode 100644
index 3127b66db..000000000
--- a/extensions/PhabBugz/lib/Logger.pm
+++ /dev/null
@@ -1,37 +0,0 @@
-# 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::PhabBugz::Logger;
-
-use 5.10.1;
-
-use Moo;
-
-use Bugzilla::Extension::PhabBugz::Constants;
-
-has 'debugging' => ( is => 'ro' );
-
-sub info { shift->_log_it('INFO', @_) }
-sub error { shift->_log_it('ERROR', @_) }
-sub debug { shift->_log_it('DEBUG', @_) }
-
-sub _log_it {
- my ($self, $method, $message) = @_;
-
- return if $method eq 'DEBUG' && !$self->debugging;
- chomp $message;
- if ($ENV{MOD_PERL}) {
- require Apache2::Log;
- Apache2::ServerRec::warn("FEED $method: $message");
- } elsif ($ENV{SCRIPT_FILENAME}) {
- print STDERR "FEED $method: $message\n";
- } else {
- print STDERR '[' . localtime(time) ."] $method: $message\n";
- }
-}
-
-1;
diff --git a/extensions/PhabBugz/lib/Revision.pm b/extensions/PhabBugz/lib/Revision.pm
index c114de78c..98c3196c2 100644
--- a/extensions/PhabBugz/lib/Revision.pm
+++ b/extensions/PhabBugz/lib/Revision.pm
@@ -29,6 +29,7 @@ use Bugzilla::Extension::PhabBugz::Util qw(
has id => ( is => 'ro', isa => Int );
has phid => ( is => 'ro', isa => Str );
has title => ( is => 'ro', isa => Str );
+has summary => ( is => 'ro', isa => Str );
has status => ( is => 'ro', isa => Str );
has creation_ts => ( is => 'ro', isa => Str );
has modification_ts => ( is => 'ro', isa => Str );
@@ -93,6 +94,7 @@ sub BUILDARGS {
my ( $class, $params ) = @_;
$params->{title} = $params->{fields}->{title};
+ $params->{summary} = $params->{fields}->{summary};
$params->{status} = $params->{fields}->{status}->{value};
$params->{creation_ts} = $params->{fields}->{dateCreated};
$params->{modification_ts} = $params->{fields}->{dateModified};
diff --git a/extensions/PhabBugz/lib/Util.pm b/extensions/PhabBugz/lib/Util.pm
index 6c51df98c..52ea9c0d5 100644
--- a/extensions/PhabBugz/lib/Util.pm
+++ b/extensions/PhabBugz/lib/Util.pm
@@ -77,12 +77,12 @@ sub _get_revisions {
}
sub create_revision_attachment {
- my ( $bug, $revision_id, $revision_title, $timestamp ) = @_;
+ my ( $bug, $revision, $timestamp ) = @_;
my $phab_base_uri = Bugzilla->params->{phabricator_base_uri};
ThrowUserError('invalid_phabricator_uri') unless $phab_base_uri;
- my $revision_uri = $phab_base_uri . "D" . $revision_id;
+ my $revision_uri = $phab_base_uri . "D" . $revision->id;
# Check for previous attachment with same revision id.
# If one matches then return it instead. This is fine as
@@ -102,8 +102,8 @@ sub create_revision_attachment {
bug => $bug,
creation_ts => $timestamp,
data => $revision_uri,
- description => $revision_title,
- filename => 'phabricator-D' . $revision_id . '-url.txt',
+ description => $revision->title,
+ filename => 'phabricator-D' . $revision->id . '-url.txt',
ispatch => 0,
isprivate => 0,
mimetype => PHAB_CONTENT_TYPE,
@@ -111,8 +111,8 @@ sub create_revision_attachment {
);
# Insert a comment about the new attachment into the database.
- $bug->add_comment('', { type => CMT_ATTACHMENT_CREATED,
- extra_data => $attachment->id });
+ $bug->add_comment($revision->summary, { type => CMT_ATTACHMENT_CREATED,
+ extra_data => $attachment->id });
return $attachment;
}
diff --git a/extensions/Push/lib/Logger.pm b/extensions/Push/lib/Logger.pm
index 833cb3b19..5d92010ee 100644
--- a/extensions/Push/lib/Logger.pm
+++ b/extensions/Push/lib/Logger.pm
@@ -8,53 +8,43 @@
package Bugzilla::Extension::Push::Logger;
use 5.10.1;
-use strict;
-use warnings;
+use Moo;
+use Bugzilla::Logging;
+use Log::Log4perl;
use Bugzilla::Extension::Push::Constants;
use Bugzilla::Extension::Push::LogEntry;
-sub new {
- my ($class) = @_;
- my $self = {};
- bless($self, $class);
- return $self;
-}
+# If Log4perl then finds that it's being called from a registered wrapper, it
+# will automatically step up to the next call frame.
+Log::Log4perl->wrapper_register(__PACKAGE__);
-sub info { shift->_log_it('INFO', @_) }
-sub error { shift->_log_it('ERROR', @_) }
-sub debug { shift->_log_it('DEBUG', @_) }
+sub info {
+ my ($this, $message) = @_;
+ INFO($message);
+}
-sub debugging {
- my ($self) = @_;
- return $self->{debug};
+sub error {
+ my ($this, $message) = @_;
+ ERROR($message);
}
-sub _log_it {
- require Apache2::Log;
- my ($self, $method, $message) = @_;
- return if $method eq 'DEBUG' && !$self->debugging;
- chomp $message;
- if ($ENV{MOD_PERL}) {
- Apache2::ServerRec::warn("Push $method: $message");
- } elsif ($ENV{SCRIPT_FILENAME}) {
- print STDERR "Push $method: $message\n";
- } else {
- print STDERR '[' . localtime(time) ."] $method: $message\n";
- }
+sub debug {
+ my ($this, $message) = @_;
+ DEBUG($message);
}
sub result {
my ($self, $connector, $message, $result, $data) = @_;
$data ||= '';
- $self->info(sprintf(
- "%s: Message #%s: %s %s",
+ my $log_msg = sprintf
+ '%s: Message #%s: %s %s',
$connector->name,
$message->message_id,
push_result_to_string($result),
- $data
- ));
+ $data;
+ $self->info($log_msg);
Bugzilla::Extension::Push::LogEntry->create({
message_id => $message->message_id,
@@ -68,4 +58,6 @@ sub result {
});
}
+sub _build_logger { Log::Log4perl->get_logger(__PACKAGE__); }
+
1;
diff --git a/extensions/Review/Extension.pm b/extensions/Review/Extension.pm
index db2b475a1..f05f2ba8b 100644
--- a/extensions/Review/Extension.pm
+++ b/extensions/Review/Extension.pm
@@ -93,6 +93,8 @@ sub _reviewers_objs {
sub _user_is_active {
my ($self) = @_;
+ # never consider .bugs or .tld addresses as inactive.
+ return 1 if $self->login =~ /bugs$/ || $self->login =~ /\.tld$/;
return 1 unless Bugzilla->params->{max_reviewer_last_seen};
return 0 if !defined($self->last_seen_date);
diff --git a/extensions/UserProfile/template/en/default/pages/user_profile.html.tmpl b/extensions/UserProfile/template/en/default/pages/user_profile.html.tmpl
index 27cb825ed..bea0d16d7 100644
--- a/extensions/UserProfile/template/en/default/pages/user_profile.html.tmpl
+++ b/extensions/UserProfile/template/en/default/pages/user_profile.html.tmpl
@@ -27,7 +27,7 @@
<td>&nbsp;</td>
<th>Search</th>
<td colspan="2">
- <form action="user_profile">
+ <form action="[% urlbase FILTER html %]user_profile">
[% INCLUDE global/userselect.html.tmpl
id => "login"
name => "login"
diff --git a/heartbeat.cgi b/heartbeat.cgi
index bb1c9dd46..0597f1e3a 100755
--- a/heartbeat.cgi
+++ b/heartbeat.cgi
@@ -30,7 +30,6 @@ my $ok = eval {
die "database not available" unless $database_ok;
die "memcached server(s) not available" unless $memcached_ok;
die "mod_perl not configured?" unless $ENV{MOD_PERL};
- die "missing bmo feature dependencies" unless Bugzilla->has_feature('bmo');
1;
};
FATAL("heartbeat error: $@") if !$ok && $@;
diff --git a/helper.psgi b/helper.psgi
new file mode 100644
index 000000000..cc8c648a8
--- /dev/null
+++ b/helper.psgi
@@ -0,0 +1,35 @@
+#!/usr/bin/perl
+# 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.
+
+use 5.10.1;
+use strict;
+use warnings;
+use Plack::Request;
+use Plack::Response;
+
+my $app = sub {
+ my $env = shift;
+ my $req = Plack::Request->new($env);
+ my $res = Plack::Response->new(404);
+ my $urlbase = Bugzilla->localconfig->{urlbase};
+ my $path = $req->path;
+
+ if ( $path eq '/quicksearch.html' ) {
+ $res->redirect( $urlbase . 'page.cgi?id=quicksearch.html', 301 );
+ }
+ elsif ( $path eq '/bugwritinghelp.html') {
+ $res->redirect( $urlbase . 'page.cgi?id=bug-writing.html', 301 );
+ }
+ elsif ( $path =~ m{^/(\d+)$}s ) {
+ $res->redirect( $urlbase . "show_bug.cgi?id=$1", 301 );
+ }
+ else {
+ $res->body('not found');
+ }
+ return $res->finalize;
+}; \ No newline at end of file
diff --git a/scripts/build-bmo-push-data.pl b/scripts/build-bmo-push-data.pl
new file mode 100755
index 000000000..e3cefe533
--- /dev/null
+++ b/scripts/build-bmo-push-data.pl
@@ -0,0 +1,203 @@
+#!/usr/bin/perl
+use 5.10.1;
+use strict;
+use warnings;
+
+use File::Basename qw(basename dirname);
+use File::Spec::Functions qw(catdir rel2abs);
+use Cwd qw(realpath);
+
+BEGIN {
+ require lib;
+ my $dir = realpath( catdir(dirname(__FILE__), '..') );
+ lib->import( $dir, catdir( $dir, 'lib' ), catdir( $dir, qw(local lib perl5) ) );
+ chdir $dir or die "chdir $dir failed: $!";
+}
+
+use autodie;
+use Bugzilla;
+use English qw(-no_match_vars $PROGRAM_NAME);
+use IPC::System::Simple qw(runx capture);
+use JSON::MaybeXS qw(decode_json);
+use LWP::Simple qw(get);
+use LWP::UserAgent;
+use MIME::Base64 qw(decode_base64);
+use URI::QueryParam;
+use URI;
+
+my $github_repo = "https://github.com/mozilla-bteam/bmo";
+my $version_info = decode_json(get('https://bugzilla.mozilla.org/__version__'));
+my $tag = 'release-' . Bugzilla->VERSION;
+my $prod_tag = "release-$version_info->{version}";
+my $tag_url = "$github_repo/tree/$tag";
+
+my @log = capture(qw(git log --oneline), "$prod_tag..HEAD");
+die "nothing to commit\n" unless @log;
+chomp @log;
+
+my @revisions;
+foreach my $line (@log) {
+ say $line;
+ my ($revision, $message);
+ unless ( ( $revision, $message ) = $line =~ /^(\S+) (.+)$/ ) {
+ warn "skipping $line\n";
+ next;
+ }
+
+ my @bug_ids;
+ if ($message =~ /\bBug (\d+)/i) {
+ push @bug_ids, $1;
+ }
+
+ if (!@bug_ids) {
+ warn "skipping $line (no bug)\n";
+ next;
+ }
+
+ foreach my $bug_id (@bug_ids) {
+ my $duplicate = 0;
+ foreach my $revisions (@revisions) {
+ if ($revisions->{bug_id} == $bug_id) {
+ $duplicate = 1;
+ last;
+ }
+ }
+ next if $duplicate;
+
+ my $bug = fetch_bug($bug_id);
+ if ($bug->{status} eq 'RESOLVED' && $bug->{resolution} ne 'FIXED') {
+ next;
+ }
+ if ($bug->{summary} =~ /\bbackport\s+(?:upstream\s+)?bug\s+(\d+)/i) {
+ my $upstream = $1;
+ $bug->{summary} = fetch_bug($upstream)->{summary};
+ }
+ push @revisions, {
+ hash => $revision,
+ bug_id => $bug_id,
+ summary => $bug->{summary},
+ };
+ }
+}
+if (!@revisions) {
+ die "no new revisions. make sure you run this script before production is updated.\n";
+}
+else {
+ @revisions = reverse @revisions;
+}
+
+my $first_revision = $revisions[0]->{hash};
+my $last_revision = $revisions[-1]->{hash};
+
+mkdir 'build_info' unless -d 'build_info';
+chdir 'build_info';
+
+say "write tag.txt";
+open my $tag_fh, '>', 'tag.txt';
+say $tag_fh $tag;
+close $tag_fh;
+
+say 'write bug.push.txt';
+
+open my $bug_fh, '>', 'bug.push.txt';
+say $bug_fh 'https://bugzilla.mozilla.org/enter_bug.cgi?product=bugzilla.mozilla.org&component=Infrastructure&short_desc=push+updated+bugzilla.mozilla.org+live';
+say $bug_fh "revisions: $first_revision - $last_revision";
+foreach my $revision (@revisions) {
+ say $bug_fh "bug $revision->{bug_id} : $revision->{summary}";
+}
+close $bug_fh;
+
+say 'write blog.push.txt';
+
+open my $blog_fh, '>', 'blog.push.txt';
+say $blog_fh "[release tag]($tag_url)\n";
+say $blog_fh "the following changes have been pushed to bugzilla.mozilla.org:\n<ul>";
+foreach my $revision (@revisions) {
+ printf $blog_fh '<li>[<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=%s" target="_blank">%s</a>] %s</li>%s',
+ $revision->{bug_id}, $revision->{bug_id}, html_escape($revision->{summary}), "\n";
+}
+say $blog_fh '</ul>';
+say $blog_fh q{discuss these changes on <a href="https://lists.mozilla.org/listinfo/tools-bmo" target="_blank">mozilla.tools.bmo</a>.};
+close $blog_fh;
+
+say 'write email.push.txt';
+
+open my $email_fh, '>', 'email.push.txt';
+say $email_fh "the following changes have been pushed to bugzilla.mozilla.org:\n";
+say $email_fh "(tag: $tag_url)\n";
+foreach my $revision (@revisions) {
+ printf $email_fh "https://bugzil.la/%s : %s\n", $revision->{bug_id}, $revision->{summary};
+}
+close $email_fh;
+
+say 'write wiki.push.txt';
+
+open my $wiki_fh, '>', 'wiki.push.txt';
+say $wiki_fh 'https://wiki.mozilla.org/BMO/Recent_Changes';
+say $wiki_fh '== ' . DateTime->now->set_time_zone('UTC')->ymd('-') . " ==\n";
+say $wiki_fh "[$tag_url $tag]";
+foreach my $revision (@revisions) {
+ printf $wiki_fh "* {{bug|%s}} %s\n", $revision->{bug_id}, $revision->{summary};
+}
+close $wiki_fh;
+
+sub html_escape {
+ my ($s) = @_;
+ $s =~ s/&/&amp;/g;
+ $s =~ s/</&lt;/g;
+ $s =~ s/>/&gt;/g;
+ return $s;
+}
+
+use constant BUG_FIELDS => [qw(
+ id
+ product
+ version
+ target_milestone
+ summary
+ status
+ resolution
+ assigned_to
+)];
+
+sub fetch_bug {
+ my ($bug_id) = @_;
+ die 'missing id' unless $bug_id;
+
+ my $response = _get( 'bug/' . $bug_id, { include_fields => BUG_FIELDS, } );
+ return $response->{bugs}->[0];
+}
+
+sub _get {
+ my ($endpoint, $args) = @_;
+ my $ua = LWP::UserAgent->new( agent => $PROGRAM_NAME );
+ $args //= {};
+
+ if (exists $args->{include_fields} && ref($args->{include_fields})) {
+ $args->{include_fields} = join ',', @{ $args->{include_fields} };
+ }
+
+ my $uri = URI->new('https://bugzilla.mozilla.org/rest/' . $endpoint);
+ foreach my $name (sort keys %$args) {
+ $uri->query_param($name => $args->{$name});
+ }
+
+ my $request = HTTP::Request->new('GET', $uri->as_string);
+ $request->header( Content_Type => 'application/json' );
+ $request->header( Accept => 'application/json' );
+ if ( $ENV{BMO_API_KEY} ) {
+ $request->header( X_Bugzilla_API_Key => $ENV{BMO_API_KEY} );
+ }
+
+ my $response = $ua->request($request);
+ if ($response->code !~ /^2/) {
+ my $error = $response->message;
+ my $ok = eval {
+ $error = decode_json($response->decoded_content)->{message};
+ 1;
+ };
+ $error = $@ unless $ok;
+ die $error . "\n";
+ }
+ return decode_json($response->decoded_content);
+}
diff --git a/scripts/entrypoint.pl b/scripts/entrypoint.pl
index 5c981c5c2..ce1cc795b 100755
--- a/scripts/entrypoint.pl
+++ b/scripts/entrypoint.pl
@@ -91,9 +91,10 @@ sub cmd_httpd {
}
sub cmd_jobqueue {
+ my (@args) = @_;
check_data_dir();
wait_for_db();
- exit run_cereal_and_jobqueue()->get;
+ exit run_cereal_and_jobqueue(@args)->get;
}
sub cmd_dev_httpd {
diff --git a/ses/index.cgi b/ses/index.cgi
index aa5b34704..9e1632586 100755
--- a/ses/index.cgi
+++ b/ses/index.cgi
@@ -13,49 +13,55 @@ use warnings;
use lib qw(.. ../lib ../local/lib/perl5);
use Bugzilla ();
+use Bugzilla::Logging;
use Bugzilla::Constants qw( ERROR_MODE_DIE );
use Bugzilla::Mailer qw( MessageToMTA );
use Bugzilla::User ();
use Bugzilla::Util qw( html_quote remote_ip );
-use JSON::XS qw( decode_json encode_json );
+use JSON::MaybeXS qw( decode_json encode_json );
use LWP::UserAgent ();
use Try::Tiny qw( try catch );
Bugzilla->error_mode(ERROR_MODE_DIE);
try {
main();
-} catch {
- warn "SES: Fatal error: $_\n";
- respond(500 => 'Internal Server Error');
+}
+catch {
+ FATAL("Fatal error: $_");
+ respond( 500 => 'Internal Server Error' );
};
sub main {
- my $message = decode_json_wrapper(Bugzilla->cgi->param('POSTDATA')) // return;
+ my $message = decode_json_wrapper( Bugzilla->cgi->param('POSTDATA') ) // return;
my $message_type = $ENV{HTTP_X_AMZ_SNS_MESSAGE_TYPE} // '(missing)';
- if ($message_type eq 'SubscriptionConfirmation') {
+ if ( $message_type eq 'SubscriptionConfirmation' ) {
confirm_subscription($message);
}
- elsif ($message_type eq 'Notification') {
- my $notification = decode_json_wrapper($message->{Message}) // return;
+ elsif ( $message_type eq 'Notification' ) {
+ my $notification = decode_json_wrapper( $message->{Message} ) // return;
my $notification_type = $notification->{notificationType} // '';
- if ($notification_type eq 'Bounce') {
+ if ( $notification_type eq '' ) {
+ my $keys = join ', ', keys %$notification;
+ WARN("No notificationType in notification (keys: $keys)");
+ }
+ if ( $notification_type eq 'Bounce' ) {
process_bounce($notification);
}
- elsif ($notification_type eq 'Complaint') {
+ elsif ( $notification_type eq 'Complaint' ) {
process_complaint($notification);
}
else {
- warn "SES: Unsupported notification-type: $notification_type\n";
- respond(200 => 'OK');
+ WARN("Unsupported notification-type: $notification_type");
+ respond( 200 => 'OK' );
}
}
else {
- warn "SES: Unsupported message-type: $message_type\n";
- respond(200 => 'OK');
+ WARN("Unsupported message-type: $message_type");
+ respond( 200 => 'OK' );
}
}
@@ -63,112 +69,117 @@ sub confirm_subscription {
my ($message) = @_;
my $subscribe_url = $message->{SubscribeURL};
- if (!$subscribe_url) {
- warn "SES: Bad SubscriptionConfirmation request: missing SubscribeURL\n";
- respond(400 => 'Bad Request');
+ if ( !$subscribe_url ) {
+ WARN('Bad SubscriptionConfirmation request: missing SubscribeURL');
+ respond( 400 => 'Bad Request' );
return;
}
- my $ua = ua();
- my $res = $ua->get($message->{SubscribeURL});
- if (!$res->is_success) {
- warn "SES: Bad response from SubscribeURL: " . $res->status_line . "\n";
- respond(400 => 'Bad Request');
+ my $ua = ua();
+ my $res = $ua->get( $message->{SubscribeURL} );
+ if ( !$res->is_success ) {
+ WARN( 'Bad response from SubscribeURL: ' . $res->status_line );
+ respond( 400 => 'Bad Request' );
return;
}
- respond(200 => 'OK');
+ respond( 200 => 'OK' );
}
sub process_bounce {
my ($notification) = @_;
my $type = $notification->{bounce}->{bounceType};
- # these should be infrequent and hopefully small
- warn("SES: notification: " . encode_json($notification));
+ if ( $type eq 'Transient' ) {
- if ($type eq 'Transient') {
# just log transient bounces
- foreach my $recipient (@{ $notification->{bounce}->{bouncedRecipients} }) {
+ foreach my $recipient ( @{ $notification->{bounce}->{bouncedRecipients} } ) {
my $address = $recipient->{emailAddress};
- Bugzilla->audit("SES: transient bounce for <$address>");
+ Bugzilla->audit("transient bounce for <$address>");
}
}
- elsif ($type eq 'Permanent') {
+ elsif ( $type eq 'Permanent' ) {
+
# disable each account that is permanently bouncing
- foreach my $recipient (@{ $notification->{bounce}->{bouncedRecipients} }) {
+ foreach my $recipient ( @{ $notification->{bounce}->{bouncedRecipients} } ) {
my $address = $recipient->{emailAddress};
- my $reason = sprintf('(%s) %s', $recipient->{action} // 'error',
- $recipient->{diagnosticCode} // 'unknown');
+ my $reason
+ = sprintf( '(%s) %s', $recipient->{action} // 'error', $recipient->{diagnosticCode} // 'unknown' );
- my $user = Bugzilla::User->new({ name => $address, cache => 1 });
+ my $user = Bugzilla::User->new( { name => $address, cache => 1 } );
if ($user) {
+
# never auto-disable admin accounts
- if ($user->in_group('admin')) {
- Bugzilla->audit("SES: ignoring permanent bounce for admin <$address>: $reason");
+ if ( $user->in_group('admin') ) {
+ Bugzilla->audit("ignoring permanent bounce for admin <$address>: $reason");
}
else {
my $template = Bugzilla->template_inner();
- my $vars = {
- mta => $notification->{bounce}->{reportingMTA} // 'unknown',
+ my $vars = {
+ mta => $notification->{bounce}->{reportingMTA} // 'unknown',
reason => $reason,
};
my $disable_text;
- $template->process('admin/users/bounce-disabled.txt.tmpl', $vars, \$disable_text)
+ $template->process( 'admin/users/bounce-disabled.txt.tmpl', $vars, \$disable_text )
|| die $template->error();
- $user->set_disabledtext($disable_text);
- $user->set_disable_mail(1);
- $user->update();
- Bugzilla->audit("SES: permanent bounce for <$address> disabled userid-" . $user->id . ": $reason");
+ $user->set_disabledtext($disable_text);
+ $user->set_disable_mail(1);
+ $user->update();
+ Bugzilla->audit(
+ "permanent bounce for <$address> disabled userid-" . $user->id . ": $reason" );
}
}
else {
- Bugzilla->audit("SES: permanent bounce for <$address> has no user: $reason");
+ Bugzilla->audit("permanent bounce for <$address> has no user: $reason");
}
}
}
else {
- warn "SES: Unsupported bounce type: $type\n";
+ WARN("Unsupported bounce type: $type\n");
}
- respond(200 => 'OK');
+ respond( 200 => 'OK' );
}
sub process_complaint {
+
# email notification to bugzilla admin
my ($notification) = @_;
- my $template = Bugzilla->template_inner();
- my $json = JSON::XS->new->pretty->utf8->canonical;
+ my $template = Bugzilla->template_inner();
+ my $json = JSON::MaybeXS->new(
+ pretty => 1,
+ utf8 => 1,
+ canonical => 1,
+ );
- foreach my $recipient (@{ $notification->{complaint}->{complainedRecipients} }) {
- my $reason = $notification->{complaint}->{complaintFeedbackType} // 'unknown';
+ foreach my $recipient ( @{ $notification->{complaint}->{complainedRecipients} } ) {
+ my $reason = $notification->{complaint}->{complaintFeedbackType} // 'unknown';
my $address = $recipient->{emailAddress};
- Bugzilla->audit("SES: complaint for <$address> for '$reason'");
+ Bugzilla->audit("complaint for <$address> for '$reason'");
my $vars = {
email => $address,
- user => Bugzilla::User->new({ name => $address, cache => 1 }),
+ user => Bugzilla::User->new( { name => $address, cache => 1 } ),
reason => $reason,
notification => $json->encode($notification),
};
my $message;
- $template->process('email/ses-complaint.txt.tmpl', $vars, \$message)
+ $template->process( 'email/ses-complaint.txt.tmpl', $vars, \$message )
|| die $template->error();
MessageToMTA($message);
}
- respond(200 => 'OK');
+ respond( 200 => 'OK' );
}
sub respond {
- my ($code, $message) = @_;
- print Bugzilla->cgi->header(
- -status => "$code $message",
- );
+ my ( $code, $message ) = @_;
+ print Bugzilla->cgi->header( -status => "$code $message" );
+
# apache will generate non-200 response pages for us
say html_quote($message) if $code == 200;
}
@@ -176,17 +187,17 @@ sub respond {
sub decode_json_wrapper {
my ($json) = @_;
my $result;
- if (!defined $json) {
- warn 'SES: Missing JSON from ' . remote_ip() . "\n";
- respond(400 => 'Bad Request');
+ if ( !defined $json ) {
+ WARN( 'Missing JSON from ' . remote_ip() );
+ respond( 400 => 'Bad Request' );
return undef;
}
my $ok = try {
$result = decode_json($json);
}
catch {
- warn 'SES: Malformed JSON from ' . remote_ip() . "\n";
- respond(400 => 'Bad Request');
+ WARN( 'Malformed JSON from ' . remote_ip() );
+ respond( 400 => 'Bad Request' );
return undef;
};
return $ok ? $result : undef;
@@ -195,9 +206,9 @@ sub decode_json_wrapper {
sub ua {
my $ua = LWP::UserAgent->new();
$ua->timeout(10);
- $ua->protocols_allowed(['http', 'https']);
- if (my $proxy_url = Bugzilla->params->{'proxy_url'}) {
- $ua->proxy(['http', 'https'], $proxy_url);
+ $ua->protocols_allowed( [ 'http', 'https' ] );
+ if ( my $proxy_url = Bugzilla->params->{'proxy_url'} ) {
+ $ua->proxy( [ 'http', 'https' ], $proxy_url );
}
else {
$ua->env_proxy;
diff --git a/template/en/default/account/auth/login.html.tmpl b/template/en/default/account/auth/login.html.tmpl
index 160fad43b..8cf5e85ef 100644
--- a/template/en/default/account/auth/login.html.tmpl
+++ b/template/en/default/account/auth/login.html.tmpl
@@ -42,7 +42,7 @@
</p>
<div id="login" class="login-form">
- <form name="login" action="[% target FILTER html %]" method="POST"
+ <form name="login" action="[% urlbase FILTER html %][% target FILTER uri FILTER html %]" method="POST"
[%- IF Bugzilla.cgi.param("data") %] enctype="multipart/form-data"[% END %]>
<div class="field-login">
<label for="Bugzilla_login">Email Address:</label>
diff --git a/template/en/default/bug/activity/table.html.tmpl b/template/en/default/bug/activity/table.html.tmpl
index 50193f894..101e43546 100644
--- a/template/en/default/bug/activity/table.html.tmpl
+++ b/template/en/default/bug/activity/table.html.tmpl
@@ -107,6 +107,7 @@
change.fieldname == 'reporter' ||
change.fieldname == 'qa_contact' ||
change.fieldname == 'cc' ||
+ change.fieldname == 'bug_mentor' ||
change.fieldname == 'flagtypes.name' %]
[% display_value(change.fieldname, change_type) FILTER email FILTER html %]
[% ELSE %]
diff --git a/template/en/default/email/new-api-key.txt.tmpl b/template/en/default/email/new-api-key.txt.tmpl
index 4a03fe800..aed904def 100644
--- a/template/en/default/email/new-api-key.txt.tmpl
+++ b/template/en/default/email/new-api-key.txt.tmpl
@@ -26,9 +26,17 @@ or update the key at the following URL:
[%+ urlbase %]userprefs.cgi?tab=apikey
+[% IF new_key.app_id == Param('mozreview_app_id') %]
+This API key was automatically created by MozReview. If you did not recently log in to
+MozReview, please disable the key at the above URL, and change your password immediately.
+[% ELSIF new_key.app_id == Param('phabricator_app_id') %]
+This API key was automatically created by Mozilla's Phabricator instance. If you did not recently
+log in to Phabricator, please disable the key at the above URL, and change your password immediately.
+[% ELSE %]
IMPORTANT: If you did not request a new key, your [% terms.Bugzilla %] account
may have been compromised. In this case, please disable the key at the above
URL, and change your password immediately.
+[% END %]
For security reasons, we have not included your new key in this e-mail.
diff --git a/template/en/default/setup/strings.txt.pl b/template/en/default/setup/strings.txt.pl
index 9de426972..5fc860519 100644
--- a/template/en/default/setup/strings.txt.pl
+++ b/template/en/default/setup/strings.txt.pl
@@ -269,6 +269,12 @@ END
This is the max amount of unshared memory the apache process is allowed to use
before Apache::SizeLimit kills it. This is only applicable when run under mod_perl.
EOT
+ localconfig_shadowdb_user => <<EOT,
+The username used to authenticate to the shadow db.
+EOT
+ localconfig_shadowdb_pass => <<EOT,
+The password used to authenticate to the shadow db.
+EOT
max_allowed_packet => <<EOT,
WARNING: You need to set the max_allowed_packet parameter in your MySQL
configuration to at least ##needed##. Currently it is set to ##current##.
diff --git a/vagrant_support/bmo-generate-data.j2 b/vagrant_support/bmo-generate-data.j2
index 79798ba4d..6ad9303d6 100644
--- a/vagrant_support/bmo-generate-data.j2
+++ b/vagrant_support/bmo-generate-data.j2
@@ -2,3 +2,4 @@
cd /vagrant
perl scripts/generate_bmo_data.pl 'vagrant@{{ WEB_HOSTNAME }}'
+perl scripts/update_params.pl 'mail_delivery_method' 'Sendmail'
diff --git a/vagrant_support/checksetup_answers.j2 b/vagrant_support/checksetup_answers.j2
index 683a28a6f..441a4d41d 100644
--- a/vagrant_support/checksetup_answers.j2
+++ b/vagrant_support/checksetup_answers.j2
@@ -23,6 +23,7 @@ $answer{'user_verify_class'} = 'GitHubAuth,DB';
$answer{'urlbase'} = "http://{{WEB_HOSTNAME}}/";
$answer{'memcached_namespace'} = 'bmo:';
$answer{'memcached_servers'} = '127.0.0.1:11211';
+$answer{'mail_delivery_method'} = 'Sendmail';
$answer{'use_mailer_queue'} = 1;
$answer{'useclassification'} = 1;
$answer{'usebugaliases'} = 1;