Commit 3238ed8f authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-03-19

# Conflicts:
#	Gemfile
#	Gemfile.lock
#	app/assets/javascripts/vue_merge_request_widget/dependencies.js
#	doc/user/project/integrations/prometheus.md
#	lib/api/search.rb

[ci skip]
parents 2d4296f9 09ae0071
...@@ -116,7 +116,11 @@ gem 'fog-rackspace', '~> 0.1.1' ...@@ -116,7 +116,11 @@ gem 'fog-rackspace', '~> 0.1.1'
gem 'fog-aliyun', '~> 0.2.0' gem 'fog-aliyun', '~> 0.2.0'
# for Google storage # for Google storage
<<<<<<< HEAD
gem 'google-api-client', '~> 0.19' gem 'google-api-client', '~> 0.19'
=======
gem 'google-api-client', '~> 0.19.8'
>>>>>>> upstream/master
# for aws storage # for aws storage
gem 'unf', '~> 0.1.4' gem 'unf', '~> 0.1.4'
......
...@@ -1103,7 +1103,11 @@ DEPENDENCIES ...@@ -1103,7 +1103,11 @@ DEPENDENCIES
gollum-lib (~> 4.2) gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.4) gollum-rugged_adapter (~> 0.4.4)
gon (~> 6.1.0) gon (~> 6.1.0)
<<<<<<< HEAD
google-api-client (~> 0.19) google-api-client (~> 0.19)
=======
google-api-client (~> 0.19.8)
>>>>>>> upstream/master
google-protobuf (= 3.5.1) google-protobuf (= 3.5.1)
gpgme gpgme
grape (~> 1.0) grape (~> 1.0)
......
...@@ -27,7 +27,11 @@ export { default as ConflictsState } from './components/states/mr_widget_conflic ...@@ -27,7 +27,11 @@ export { default as ConflictsState } from './components/states/mr_widget_conflic
export { default as NothingToMergeState } from './components/states/nothing_to_merge.vue'; export { default as NothingToMergeState } from './components/states/nothing_to_merge.vue';
export { default as MissingBranchState } from './components/states/mr_widget_missing_branch.vue'; export { default as MissingBranchState } from './components/states/mr_widget_missing_branch.vue';
export { default as NotAllowedState } from './components/states/mr_widget_not_allowed.vue'; export { default as NotAllowedState } from './components/states/mr_widget_not_allowed.vue';
<<<<<<< HEAD
export { default as ReadyToMergeState } from 'ee/vue_merge_request_widget/components/states/mr_widget_ready_to_merge'; export { default as ReadyToMergeState } from 'ee/vue_merge_request_widget/components/states/mr_widget_ready_to_merge';
=======
export { default as ReadyToMergeState } from './components/states/mr_widget_ready_to_merge';
>>>>>>> upstream/master
export { default as ShaMismatchState } from './components/states/sha_mismatch.vue'; export { default as ShaMismatchState } from './components/states/sha_mismatch.vue';
export { default as UnresolvedDiscussionsState } from './components/states/unresolved_discussions.vue'; export { default as UnresolvedDiscussionsState } from './components/states/unresolved_discussions.vue';
export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked.vue'; export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked.vue';
......
.navbar-gitlab { .navbar-gitlab {
&.navbar-gitlab { padding: 0 16px;
padding: 0 16px; z-index: 1000;
z-index: 1000; margin-bottom: 0;
margin-bottom: 0; min-height: $header-height;
min-height: $header-height; border: 0;
border: 0; border-bottom: 1px solid $border-color;
border-bottom: 1px solid $border-color; position: fixed;
position: fixed; top: 0;
top: 0; left: 0;
left: 0; right: 0;
right: 0; border-radius: 0;
border-radius: 0;
.logo-text {
.logo-text { line-height: initial;
line-height: initial;
svg {
svg { width: 55px;
width: 55px; height: 14px;
height: 14px; margin: 0;
margin: 0; fill: $white-light;
fill: $white-light;
}
}
.container-fluid {
padding: 0;
.user-counter {
svg {
margin-right: 3px;
}
}
.navbar-toggle {
right: -10px;
border-radius: 0;
min-width: 45px;
padding: 0;
margin-right: -7px;
font-size: 14px;
text-align: center;
color: currentColor;
&:hover,
&:focus,
&.active {
color: currentColor;
background-color: transparent;
}
.more-icon,
.close-icon {
fill: $white-light;
margin: auto;
}
}
} }
} }
...@@ -184,6 +148,38 @@ ...@@ -184,6 +148,38 @@
} }
.container-fluid { .container-fluid {
padding: 0;
.user-counter {
svg {
margin-right: 3px;
}
}
.navbar-toggle {
right: -10px;
border-radius: 0;
min-width: 45px;
padding: 0;
margin-right: -7px;
font-size: 14px;
text-align: center;
color: currentColor;
&:hover,
&:focus,
&.active {
color: currentColor;
background-color: transparent;
}
.more-icon,
.close-icon {
fill: $white-light;
margin: auto;
}
}
.navbar-nav { .navbar-nav {
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
display: -webkit-flex; display: -webkit-flex;
......
...@@ -140,12 +140,6 @@ ul.notes { ...@@ -140,12 +140,6 @@ ul.notes {
@include bulleted-list; @include bulleted-list;
word-wrap: break-word; word-wrap: break-word;
ul.task-list {
ul:not(.task-list) {
padding-left: 1.3em;
}
}
table { table {
@include markdown-table; @include markdown-table;
} }
......
...@@ -16,6 +16,7 @@ class Admin::ProjectsFinder ...@@ -16,6 +16,7 @@ class Admin::ProjectsFinder
items = by_archived(items) items = by_archived(items)
items = by_personal(items) items = by_personal(items)
items = by_name(items) items = by_name(items)
items = items.includes(namespace: [:owner])
sort(items).page(params[:page]) sort(items).page(params[:page])
end end
......
...@@ -35,7 +35,8 @@ class NotificationRecipient ...@@ -35,7 +35,8 @@ class NotificationRecipient
# check this last because it's expensive # check this last because it's expensive
# nobody should receive notifications if they've specifically unsubscribed # nobody should receive notifications if they've specifically unsubscribed
return false if unsubscribed? # except if they were mentioned.
return false if @type != :mention && unsubscribed?
true true
end end
......
---
title: Send @mention notifications even if a user has explicitly unsubscribed from
item
merge_request:
author:
type: added
---
title: Add documentation for displayed K8s Ingress IP address (#44330)
merge_request: 17836
author:
type: other
---
title: Clean up selectors in framework/header.scss
merge_request: 17822
author: Takuya Noguchi
type: other
---
title: Unify format for nested non-task lists
merge_request: 17823
author: Takuya Noguchi
type: fixed
---
title: Make /-/ delimiter optional for search endpoints
merge_request:
author:
type: changed
# rubocop:disable Lint/RescueException # rubocop:disable Lint/RescueException
# This patch fixes https://github.com/rails/rails/issues/26024 # Remove this entire initializer when we are at rails 5.0.
# TODO: Remove it when it's no longer necessary # This file fixes the bug (see below) which has been fixed in the upstream.
unless Gitlab.rails5?
module ActiveRecord # This patch fixes https://github.com/rails/rails/issues/26024
module Locking # TODO: Remove it when it's no longer necessary
module Optimistic
# We overwrite this method because we don't want to have default value module ActiveRecord
# for newly created records module Locking
def _create_record(attribute_names = self.attribute_names, *) # :nodoc: module Optimistic
super # We overwrite this method because we don't want to have default value
end # for newly created records
def _create_record(attribute_names = self.attribute_names, *) # :nodoc:
super
end
def _update_record(attribute_names = self.attribute_names) #:nodoc: def _update_record(attribute_names = self.attribute_names) #:nodoc:
return super unless locking_enabled? return super unless locking_enabled?
return 0 if attribute_names.empty? return 0 if attribute_names.empty?
lock_col = self.class.locking_column lock_col = self.class.locking_column
previous_lock_value = send(lock_col).to_i # rubocop:disable GitlabSecurity/PublicSend previous_lock_value = send(lock_col).to_i # rubocop:disable GitlabSecurity/PublicSend
# This line is added as a patch # This line is added as a patch
previous_lock_value = nil if previous_lock_value == '0' || previous_lock_value == 0 previous_lock_value = nil if previous_lock_value == '0' || previous_lock_value == 0
increment_lock increment_lock
attribute_names += [lock_col] attribute_names += [lock_col]
attribute_names.uniq! attribute_names.uniq!
begin begin
relation = self.class.unscoped relation = self.class.unscoped
affected_rows = relation.where( affected_rows = relation.where(
self.class.primary_key => id, self.class.primary_key => id,
lock_col => previous_lock_value lock_col => previous_lock_value
).update_all( ).update_all(
attributes_for_update(attribute_names).map do |name| attributes_for_update(attribute_names).map do |name|
[name, _read_attribute(name)] [name, _read_attribute(name)]
end.to_h end.to_h
) )
unless affected_rows == 1 unless affected_rows == 1
raise ActiveRecord::StaleObjectError.new(self, "update") raise ActiveRecord::StaleObjectError.new(self, "update")
end end
affected_rows affected_rows
# If something went wrong, revert the version. # If something went wrong, revert the version.
rescue Exception rescue Exception
send(lock_col + '=', previous_lock_value) # rubocop:disable GitlabSecurity/PublicSend send(lock_col + '=', previous_lock_value) # rubocop:disable GitlabSecurity/PublicSend
raise raise
end
end end
end
# This is patched because we need it to query `lock_version IS NULL` # This is patched because we need it to query `lock_version IS NULL`
# rather than `lock_version = 0` whenever lock_version is NULL. # rather than `lock_version = 0` whenever lock_version is NULL.
def relation_for_destroy def relation_for_destroy
return super unless locking_enabled? return super unless locking_enabled?
column_name = self.class.locking_column column_name = self.class.locking_column
super.where(self.class.arel_table[column_name].eq(self[column_name])) super.where(self.class.arel_table[column_name].eq(self[column_name]))
end
end end
end
# This is patched because we want `lock_version` default to `NULL` # This is patched because we want `lock_version` default to `NULL`
# rather than `0` # rather than `0`
class LockingType < SimpleDelegator class LockingType < SimpleDelegator
def type_cast_from_database(value) def type_cast_from_database(value)
super super
end
end end
end end
end end
......
...@@ -167,6 +167,17 @@ external IP address with the following procedure. It can be deployed using the ...@@ -167,6 +167,17 @@ external IP address with the following procedure. It can be deployed using the
In order to publish your web application, you first need to find the external IP In order to publish your web application, you first need to find the external IP
address associated to your load balancer. address associated to your load balancer.
### Let GitLab fetch the IP address
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17052) in GitLab 10.6.
If you installed the Ingress [via the **Applications**](#installing-applications),
you should see the Ingress IP address on this same page within a few minutes.
If you don't see this, GitLab might not be able to determine the IP address of
your ingress application in which case you should manually determine it.
### Manually determining the IP address
If the cluster is on GKE, click on the **Google Kubernetes Engine** link in the If the cluster is on GKE, click on the **Google Kubernetes Engine** link in the
**Advanced settings**, or go directly to the **Advanced settings**, or go directly to the
[Google Kubernetes Engine dashboard](https://console.cloud.google.com/kubernetes/) [Google Kubernetes Engine dashboard](https://console.cloud.google.com/kubernetes/)
...@@ -193,6 +204,24 @@ The output is the external IP address of your cluster. This information can then ...@@ -193,6 +204,24 @@ The output is the external IP address of your cluster. This information can then
be used to set up DNS entries and forwarding rules that allow external access to be used to set up DNS entries and forwarding rules that allow external access to
your deployed applications. your deployed applications.
### Using a static IP
By default, an ephemeral external IP address is associated to the cluster's load
balancer. If you associate the ephemeral IP with your DNS and the IP changes,
your apps will not be able to be reached, and you'd have to change the DNS
record again. In order to avoid that, you should change it into a static
reserved IP.
[Read how to promote an ephemeral external IP address in GKE.](https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address#promote_ephemeral_ip)
### Pointing your DNS at the cluster IP
Once you've set up the static IP, you should associate it to a [wildcard DNS
record](https://en.wikipedia.org/wiki/Wildcard_DNS_record), in order to be able
to reach your apps. This heavily depends on your domain provider, but in case
you aren't sure, just create an A record with a wildcard host like
`*.example.com.`.
## Setting the environment scope ## Setting the environment scope
NOTE: **Note:** NOTE: **Note:**
......
...@@ -12,7 +12,11 @@ There are two ways to setup Prometheus integration, depending on where your apps ...@@ -12,7 +12,11 @@ There are two ways to setup Prometheus integration, depending on where your apps
* For deployments on Kubernetes, GitLab can automatically [deploy and manage Prometheus](#managed-prometheus-on-kubernetes) * For deployments on Kubernetes, GitLab can automatically [deploy and manage Prometheus](#managed-prometheus-on-kubernetes)
* For other deployment targets, simply [specify the Prometheus server](#manual-configuration-of-prometheus). * For other deployment targets, simply [specify the Prometheus server](#manual-configuration-of-prometheus).
<<<<<<< HEAD
Once enabled, GitLab will automatically detect metrics from known services in the [metric library](#monitoring-ci-cd-environments). You are also able to [add your own metrics](#adding-additional-metrics) as well. Once enabled, GitLab will automatically detect metrics from known services in the [metric library](#monitoring-ci-cd-environments). You are also able to [add your own metrics](#adding-additional-metrics) as well.
=======
Once enabled, GitLab will automatically detect metrics from known services in the [metric library](#monitoring-ci-cd-environments).
>>>>>>> upstream/master
## Enabling Prometheus Integration ## Enabling Prometheus Integration
...@@ -88,6 +92,7 @@ Once configured, GitLab will attempt to retrieve performance metrics for any ...@@ -88,6 +92,7 @@ Once configured, GitLab will attempt to retrieve performance metrics for any
environment which has had a successful deployment. environment which has had a successful deployment.
GitLab will automatically scan the Prometheus server for metrics from known serves like Kubernetes and NGINX, and attempt to identify individual environment. The supported metrics and scan process is detailed in our [Prometheus Metric Library documentation](prometheus_library/metrics.html). GitLab will automatically scan the Prometheus server for metrics from known serves like Kubernetes and NGINX, and attempt to identify individual environment. The supported metrics and scan process is detailed in our [Prometheus Metric Library documentation](prometheus_library/metrics.html).
<<<<<<< HEAD
You can view the performance dashboard for an environment by [clicking on the monitoring button](../../../ci/environments.md#monitoring-environments). You can view the performance dashboard for an environment by [clicking on the monitoring button](../../../ci/environments.md#monitoring-environments).
...@@ -114,6 +119,10 @@ GitLab supports a limited set of [CI variables] in the Prometheus query. This is ...@@ -114,6 +119,10 @@ GitLab supports a limited set of [CI variables] in the Prometheus query. This is
* KUBE_NAMESPACE * KUBE_NAMESPACE
To specify a variable in a query, enclose it in curly braces with a leading percent. For example: `%{ci_environment_slug}`. To specify a variable in a query, enclose it in curly braces with a leading percent. For example: `%{ci_environment_slug}`.
=======
You can view the performance dashboard for an environment by [clicking on the monitoring button](../../../ci/environments.md#monitoring-environments).
>>>>>>> upstream/master
## Determining the performance impact of a merge ## Determining the performance impact of a merge
......
...@@ -108,8 +108,11 @@ module API ...@@ -108,8 +108,11 @@ module API
use :pagination use :pagination
end end
get ':id/(-/)search' do get ':id/(-/)search' do
<<<<<<< HEAD
check_elasticsearch_scope! check_elasticsearch_scope!
=======
>>>>>>> upstream/master
present search(group_id: user_group.id), with: entity present search(group_id: user_group.id), with: entity
end end
end end
......
...@@ -31,5 +31,15 @@ describe Admin::ProjectsController do ...@@ -31,5 +31,15 @@ describe Admin::ProjectsController do
expect(response.body).not_to match(pending_delete_project.name) expect(response.body).not_to match(pending_delete_project.name)
expect(response.body).to match(project.name) expect(response.body).to match(project.name)
end end
it 'does not have N+1 queries', :use_clean_rails_memory_store_caching, :request_store do
get :index
control_count = ActiveRecord::QueryRecorder.new { get :index }.count
create(:project)
expect { get :index }.not_to exceed_query_limit(control_count)
end
end end
end end
...@@ -34,6 +34,12 @@ describe NotificationService, :mailer do ...@@ -34,6 +34,12 @@ describe NotificationService, :mailer do
should_not_email_anyone should_not_email_anyone
end end
it 'emails new mentions despite being unsubscribed' do
send_notifications(@unsubscribed_mentioned)
should_only_email(@unsubscribed_mentioned)
end
it 'sends the proper notification reason header' do it 'sends the proper notification reason header' do
send_notifications(@u_watcher) send_notifications(@u_watcher)
should_only_email(@u_watcher) should_only_email(@u_watcher)
...@@ -122,7 +128,7 @@ describe NotificationService, :mailer do ...@@ -122,7 +128,7 @@ describe NotificationService, :mailer do
let(:project) { create(:project, :private) } let(:project) { create(:project, :private) }
let(:issue) { create(:issue, project: project, assignees: [assignee]) } let(:issue) { create(:issue, project: project, assignees: [assignee]) }
let(:mentioned_issue) { create(:issue, assignees: issue.assignees) } let(:mentioned_issue) { create(:issue, assignees: issue.assignees) }
let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@mention referenced, @outsider also') } let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@mention referenced, @unsubscribed_mentioned and @outsider also') }
before do before do
build_team(note.project) build_team(note.project)
...@@ -150,7 +156,7 @@ describe NotificationService, :mailer do ...@@ -150,7 +156,7 @@ describe NotificationService, :mailer do
add_users_with_subscription(note.project, issue) add_users_with_subscription(note.project, issue)
reset_delivered_emails! reset_delivered_emails!
expect(SentNotification).to receive(:record).with(issue, any_args).exactly(9).times expect(SentNotification).to receive(:record).with(issue, any_args).exactly(10).times
notification.new_note(note) notification.new_note(note)
...@@ -163,6 +169,7 @@ describe NotificationService, :mailer do ...@@ -163,6 +169,7 @@ describe NotificationService, :mailer do
should_email(@watcher_and_subscriber) should_email(@watcher_and_subscriber)
should_email(@subscribed_participant) should_email(@subscribed_participant)
should_email(@u_custom_off) should_email(@u_custom_off)
should_email(@unsubscribed_mentioned)
should_not_email(@u_guest_custom) should_not_email(@u_guest_custom)
should_not_email(@u_guest_watcher) should_not_email(@u_guest_watcher)
should_not_email(note.author) should_not_email(note.author)
...@@ -279,6 +286,7 @@ describe NotificationService, :mailer do ...@@ -279,6 +286,7 @@ describe NotificationService, :mailer do
before do before do
build_team(note.project) build_team(note.project)
note.project.add_master(note.author) note.project.add_master(note.author)
add_users_with_subscription(note.project, issue)
reset_delivered_emails! reset_delivered_emails!
end end
...@@ -286,6 +294,9 @@ describe NotificationService, :mailer do ...@@ -286,6 +294,9 @@ describe NotificationService, :mailer do
it 'notifies the team members' do it 'notifies the team members' do
notification.new_note(note) notification.new_note(note)
# Make sure @unsubscribed_mentioned is part of the team
expect(note.project.team.members).to include(@unsubscribed_mentioned)
# Notify all team members # Notify all team members
note.project.team.members.each do |member| note.project.team.members.each do |member|
# User with disabled notification should not be notified # User with disabled notification should not be notified
...@@ -486,7 +497,7 @@ describe NotificationService, :mailer do ...@@ -486,7 +497,7 @@ describe NotificationService, :mailer do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:project) { create(:project, :public, namespace: group) } let(:project) { create(:project, :public, namespace: group) }
let(:another_project) { create(:project, :public, namespace: group) } let(:another_project) { create(:project, :public, namespace: group) }
let(:issue) { create :issue, project: project, assignees: [assignee], description: 'cc @participant' } let(:issue) { create :issue, project: project, assignees: [assignee], description: 'cc @participant @unsubscribed_mentioned' }
before do before do
build_team(issue.project) build_team(issue.project)
...@@ -510,6 +521,7 @@ describe NotificationService, :mailer do ...@@ -510,6 +521,7 @@ describe NotificationService, :mailer do
should_email(@u_participant_mentioned) should_email(@u_participant_mentioned)
should_email(@g_global_watcher) should_email(@g_global_watcher)
should_email(@g_watcher) should_email(@g_watcher)
should_email(@unsubscribed_mentioned)
should_not_email(@u_mentioned) should_not_email(@u_mentioned)
should_not_email(@u_participating) should_not_email(@u_participating)
should_not_email(@u_disabled) should_not_email(@u_disabled)
...@@ -1865,6 +1877,7 @@ describe NotificationService, :mailer do ...@@ -1865,6 +1877,7 @@ describe NotificationService, :mailer do
def add_users_with_subscription(project, issuable) def add_users_with_subscription(project, issuable)
@subscriber = create :user @subscriber = create :user
@unsubscriber = create :user @unsubscriber = create :user
@unsubscribed_mentioned = create :user, username: 'unsubscribed_mentioned'
@subscribed_participant = create_global_setting_for(create(:user, username: 'subscribed_participant'), :participating) @subscribed_participant = create_global_setting_for(create(:user, username: 'subscribed_participant'), :participating)
@watcher_and_subscriber = create_global_setting_for(create(:user), :watch) @watcher_and_subscriber = create_global_setting_for(create(:user), :watch)
...@@ -1872,7 +1885,9 @@ describe NotificationService, :mailer do ...@@ -1872,7 +1885,9 @@ describe NotificationService, :mailer do
project.add_master(@subscriber) project.add_master(@subscriber)
project.add_master(@unsubscriber) project.add_master(@unsubscriber)
project.add_master(@watcher_and_subscriber) project.add_master(@watcher_and_subscriber)
project.add_master(@unsubscribed_mentioned)
issuable.subscriptions.create(user: @unsubscribed_mentioned, project: project, subscribed: false)
issuable.subscriptions.create(user: @subscriber, project: project, subscribed: true) issuable.subscriptions.create(user: @subscriber, project: project, subscribed: true)
issuable.subscriptions.create(user: @subscribed_participant, project: project, subscribed: true) issuable.subscriptions.create(user: @subscribed_participant, project: project, subscribed: true)
issuable.subscriptions.create(user: @unsubscriber, project: project, subscribed: false) issuable.subscriptions.create(user: @unsubscriber, project: project, subscribed: false)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment