Commit f6de5937 authored by Kamil Trzcinski's avatar Kamil Trzcinski

Merge remote-tracking branch 'origin/master' into artifacts-expire-date

parents c534d2e8 57ebd399
...@@ -7,7 +7,8 @@ services: ...@@ -7,7 +7,8 @@ services:
cache: cache:
key: "ruby21" key: "ruby21"
paths: paths:
- vendor - vendor/apt
- vendor/ruby
variables: variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "1" MYSQL_ALLOW_EMPTY_PASSWORD: "1"
......
...@@ -10,6 +10,7 @@ v 8.9.0 (unreleased) ...@@ -10,6 +10,7 @@ v 8.9.0 (unreleased)
- Fix issue with arrow keys not working in search autocomplete dropdown - Fix issue with arrow keys not working in search autocomplete dropdown
- Make EmailsOnPushWorker use Sidekiq mailers queue - Make EmailsOnPushWorker use Sidekiq mailers queue
- Fix wiki page events' webhook to point to the wiki repository - Fix wiki page events' webhook to point to the wiki repository
- Don't show tags for revert and cherry-pick operations
- Fix issue todo not remove when leave project !4150 (Long Nguyen) - Fix issue todo not remove when leave project !4150 (Long Nguyen)
- Allow customisable text on the 'nearly there' page after a user signs up - Allow customisable text on the 'nearly there' page after a user signs up
- Bump recaptcha gem to 3.0.0 to remove deprecated stoken support - Bump recaptcha gem to 3.0.0 to remove deprecated stoken support
...@@ -58,6 +59,7 @@ v 8.9.0 (unreleased) ...@@ -58,6 +59,7 @@ v 8.9.0 (unreleased)
- Improve error handling importing projects - Improve error handling importing projects
- Remove duplicated notification settings - Remove duplicated notification settings
- Put project Files and Commits tabs under Code tab - Put project Files and Commits tabs under Code tab
- Decouple global notification level from user model
- Replace Colorize with Rainbow for coloring console output in Rake tasks. - Replace Colorize with Rainbow for coloring console output in Rake tasks.
- Add workhorse controller and API helpers - Add workhorse controller and API helpers
- An indicator is now displayed at the top of the comment field for confidential issues. - An indicator is now displayed at the top of the comment field for confidential issues.
...@@ -69,6 +71,7 @@ v 8.9.0 (unreleased) ...@@ -69,6 +71,7 @@ v 8.9.0 (unreleased)
- Improved UX of date pickers on issue & milestone forms - Improved UX of date pickers on issue & milestone forms
- Cache on the database if a project has an active external issue tracker. - Cache on the database if a project has an active external issue tracker.
- Put project Labels and Milestones pages links under Issues and Merge Requests tabs as subnav - Put project Labels and Milestones pages links under Issues and Merge Requests tabs as subnav
- All classes in the Banzai::ReferenceParser namespace are now instrumented
v 8.8.5 (unreleased) v 8.8.5 (unreleased)
- Ensure branch cleanup regardless of whether the GitHub import process succeeds - Ensure branch cleanup regardless of whether the GitHub import process succeeds
......
...@@ -3,10 +3,11 @@ class Profiles::NotificationsController < Profiles::ApplicationController ...@@ -3,10 +3,11 @@ class Profiles::NotificationsController < Profiles::ApplicationController
@user = current_user @user = current_user
@group_notifications = current_user.notification_settings.for_groups @group_notifications = current_user.notification_settings.for_groups
@project_notifications = current_user.notification_settings.for_projects @project_notifications = current_user.notification_settings.for_projects
@global_notification_setting = current_user.global_notification_setting
end end
def update def update
if current_user.update_attributes(user_params) if current_user.update_attributes(user_params) && update_notification_settings
flash[:notice] = "Notification settings saved" flash[:notice] = "Notification settings saved"
else else
flash[:alert] = "Failed to save new settings" flash[:alert] = "Failed to save new settings"
...@@ -16,6 +17,18 @@ class Profiles::NotificationsController < Profiles::ApplicationController ...@@ -16,6 +17,18 @@ class Profiles::NotificationsController < Profiles::ApplicationController
end end
def user_params def user_params
params.require(:user).permit(:notification_email, :notification_level) params.require(:user).permit(:notification_email)
end
def global_notification_setting_params
params.require(:global_notification_setting).permit(:level)
end
private
def update_notification_settings
return true unless global_notification_setting_params
current_user.global_notification_setting.update_attributes(global_notification_setting_params)
end end
end end
...@@ -14,4 +14,8 @@ module BranchesHelper ...@@ -14,4 +14,8 @@ module BranchesHelper
::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name) ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name)
end end
def project_branches
options_for_select(@project.repository.branch_names, @project.default_branch)
end
end end
...@@ -61,4 +61,23 @@ module NotificationsHelper ...@@ -61,4 +61,23 @@ module NotificationsHelper
end end
end end
end end
def notification_level_radio_buttons
html = ""
NotificationSetting.levels.each_key do |level|
level = level.to_sym
next if level == :global
html << content_tag(:div, class: "radio") do
content_tag(:label, { value: level }) do
radio_button_tag(:"global_notification_setting[level]", level, @global_notification_setting.level.to_sym == level) +
content_tag(:div, level.to_s.capitalize, class: "level-title") +
content_tag(:p, notification_description(level))
end
end
end
html.html_safe
end
end end
...@@ -7,7 +7,6 @@ class NotificationSetting < ActiveRecord::Base ...@@ -7,7 +7,6 @@ class NotificationSetting < ActiveRecord::Base
belongs_to :source, polymorphic: true belongs_to :source, polymorphic: true
validates :user, presence: true validates :user, presence: true
validates :source, presence: true
validates :level, presence: true validates :level, presence: true
validates :user_id, uniqueness: { scope: [:source_type, :source_id], validates :user_id, uniqueness: { scope: [:source_type, :source_id],
message: "already exists in source", message: "already exists in source",
......
...@@ -10,6 +10,8 @@ class User < ActiveRecord::Base ...@@ -10,6 +10,8 @@ class User < ActiveRecord::Base
include CaseSensitivity include CaseSensitivity
include TokenAuthenticatable include TokenAuthenticatable
DEFAULT_NOTIFICATION_LEVEL = :participating
add_authentication_token_field :authentication_token add_authentication_token_field :authentication_token
default_value_for :admin, false default_value_for :admin, false
...@@ -99,7 +101,6 @@ class User < ActiveRecord::Base ...@@ -99,7 +101,6 @@ class User < ActiveRecord::Base
presence: true, presence: true,
uniqueness: { case_sensitive: false } uniqueness: { case_sensitive: false }
validates :notification_level, presence: true
validate :namespace_uniq, if: ->(user) { user.username_changed? } validate :namespace_uniq, if: ->(user) { user.username_changed? }
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? } validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
validate :unique_email, if: ->(user) { user.email_changed? } validate :unique_email, if: ->(user) { user.email_changed? }
...@@ -133,13 +134,6 @@ class User < ActiveRecord::Base ...@@ -133,13 +134,6 @@ class User < ActiveRecord::Base
# Note: When adding an option, it MUST go on the end of the array. # Note: When adding an option, it MUST go on the end of the array.
enum project_view: [:readme, :activity, :files] enum project_view: [:readme, :activity, :files]
# Notification level
# Note: When adding an option, it MUST go on the end of the array.
#
# TODO: Add '_prefix: :notification' to enum when update to Rails 5. https://github.com/rails/rails/pull/19813
# Because user.notification_disabled? is much better than user.disabled?
enum notification_level: [:disabled, :participating, :watch, :global, :mention]
alias_attribute :private_token, :authentication_token alias_attribute :private_token, :authentication_token
delegate :path, to: :namespace, allow_nil: true, prefix: true delegate :path, to: :namespace, allow_nil: true, prefix: true
...@@ -800,6 +794,17 @@ class User < ActiveRecord::Base ...@@ -800,6 +794,17 @@ class User < ActiveRecord::Base
notification_settings.find_or_initialize_by(source: source) notification_settings.find_or_initialize_by(source: source)
end end
# Lazy load global notification setting
# Initializes User setting with Participating level if setting not persisted
def global_notification_setting
return @global_notification_setting if defined?(@global_notification_setting)
@global_notification_setting = notification_settings.find_or_initialize_by(source: nil)
@global_notification_setting.update_attributes(level: NotificationSetting.levels[DEFAULT_NOTIFICATION_LEVEL]) unless @global_notification_setting.persisted?
@global_notification_setting
end
def assigned_open_merge_request_count(force: false) def assigned_open_merge_request_count(force: false)
Rails.cache.fetch(['users', id, 'assigned_open_merge_request_count'], force: force) do Rails.cache.fetch(['users', id, 'assigned_open_merge_request_count'], force: force) do
assigned_merge_requests.opened.count assigned_merge_requests.opened.count
......
...@@ -279,10 +279,11 @@ class NotificationService ...@@ -279,10 +279,11 @@ class NotificationService
end end
def users_with_global_level_watch(ids) def users_with_global_level_watch(ids)
User.where( NotificationSetting.where(
id: ids, user_id: ids,
notification_level: NotificationSetting.levels[:watch] source_type: nil,
).pluck(:id) level: NotificationSetting.levels[:watch]
).pluck(:user_id)
end end
# Build a list of users based on project notifcation settings # Build a list of users based on project notifcation settings
...@@ -352,7 +353,9 @@ class NotificationService ...@@ -352,7 +353,9 @@ class NotificationService
users = users.reject(&:blocked?) users = users.reject(&:blocked?)
users.reject do |user| users.reject do |user|
next user.notification_level == level unless project global_notification_setting = user.global_notification_setting
next global_notification_setting.level == level unless project
setting = user.notification_settings_for(project) setting = user.notification_settings_for(project)
...@@ -361,13 +364,13 @@ class NotificationService ...@@ -361,13 +364,13 @@ class NotificationService
end end
# reject users who globally set mention notification and has no setting per project/group # reject users who globally set mention notification and has no setting per project/group
next user.notification_level == level unless setting next global_notification_setting.level == level unless setting
# reject users who set mention notification in project # reject users who set mention notification in project
next true if setting.level == level next true if setting.level == level
# reject users who have mention level in project and disabled in global settings # reject users who have mention level in project and disabled in global settings
setting.global? && user.notification_level == level setting.global? && global_notification_setting.level == level
end end
end end
...@@ -456,7 +459,6 @@ class NotificationService ...@@ -456,7 +459,6 @@ class NotificationService
def build_recipients(target, project, current_user, action: nil, previous_assignee: nil) def build_recipients(target, project, current_user, action: nil, previous_assignee: nil)
recipients = target.participants(current_user) recipients = target.participants(current_user)
recipients = add_project_watchers(recipients, project) recipients = add_project_watchers(recipients, project)
recipients = reject_mention_users(recipients, project) recipients = reject_mention_users(recipients, project)
......
%li.notification-list-item %li.notification-list-item
%span.notification.fa.fa-holder.append-right-5 %span.notification.fa.fa-holder.append-right-5
- if setting.global? - if setting.global?
= notification_icon(current_user.notification_level) = notification_icon(current_user.global_notification_setting.level)
- else - else
= notification_icon(setting.level) = notification_icon(setting.level)
......
%li.notification-list-item %li.notification-list-item
%span.notification.fa.fa-holder.append-right-5 %span.notification.fa.fa-holder.append-right-5
- if setting.global? - if setting.global?
= notification_icon(current_user.notification_level) = notification_icon(current_user.global_notification_setting.level)
- else - else
= notification_icon(setting.level) = notification_icon(setting.level)
......
...@@ -26,33 +26,7 @@ ...@@ -26,33 +26,7 @@
= f.select :notification_email, @user.all_emails, { include_blank: false }, class: "select2" = f.select :notification_email, @user.all_emails, { include_blank: false }, class: "select2"
.form-group .form-group
= f.label :notification_level, class: 'label-light' = f.label :notification_level, class: 'label-light'
.radio = notification_level_radio_buttons
= f.label :notification_level, value: :disabled do
= f.radio_button :notification_level, :disabled
.level-title
Disabled
%p You will not get any notifications via email
.radio
= f.label :notification_level, value: :mention do
= f.radio_button :notification_level, :mention
.level-title
On Mention
%p You will receive notifications only for comments in which you were @mentioned
.radio
= f.label :notification_level, value: :participating do
= f.radio_button :notification_level, :participating
.level-title
Participating
%p You will only receive notifications from related resources (e.g. from your commits or assigned issues)
.radio
= f.label :notification_level, value: :watch do
= f.radio_button :notification_level, :watch
.level-title
Watch
%p You will receive notifications for any activity
.prepend-top-default .prepend-top-default
= f.submit 'Update settings', class: "btn btn-create" = f.submit 'Update settings', class: "btn btn-create"
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
.form-group.branch .form-group.branch
= label_tag 'target_branch', target_label, class: 'control-label' = label_tag 'target_branch', target_label, class: 'control-label'
.col-sm-10 .col-sm-10
= select_tag "target_branch", grouped_options_refs, class: "select2 select2-sm js-target-branch" = select_tag "target_branch", project_branches, class: "select2 select2-sm js-target-branch"
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
.js-create-merge-request-container .js-create-merge-request-container
.checkbox .checkbox
......
...@@ -96,14 +96,19 @@ if Gitlab::Metrics.enabled? ...@@ -96,14 +96,19 @@ if Gitlab::Metrics.enabled?
config.instrument_instance_methods(const) config.instrument_instance_methods(const)
end end
# Instruments all Banzai filters # Instruments all Banzai filters and reference parsers
Dir[Rails.root.join('lib', 'banzai', 'filter', '*.rb')].each do |file| {
Filter: Rails.root.join('lib', 'banzai', 'filter', '*.rb'),
ReferenceParser: Rails.root.join('lib', 'banzai', 'reference_parser', '*.rb')
}.each do |const_name, path|
Dir[path].each do |file|
klass = File.basename(file, File.extname(file)).camelize klass = File.basename(file, File.extname(file)).camelize
const = Banzai::Filter.const_get(klass) const = Banzai.const_get(const_name).const_get(klass)
config.instrument_methods(const) config.instrument_methods(const)
config.instrument_instance_methods(const) config.instrument_instance_methods(const)
end end
end
config.instrument_methods(Banzai::Renderer) config.instrument_methods(Banzai::Renderer)
config.instrument_methods(Banzai::Querying) config.instrument_methods(Banzai::Querying)
......
class RemoveNotificationSettingNotNullConstraints < ActiveRecord::Migration
def up
change_column :notification_settings, :source_type, :string, null: true
change_column :notification_settings, :source_id, :integer, null: true
end
def down
change_column :notification_settings, :source_type, :string, null: false
change_column :notification_settings, :source_id, :integer, null: false
end
end
class MigrateUsersNotificationLevel < ActiveRecord::Migration
# Migrates only users who changed their default notification level :participating
# creating a new record on notification settings table
def up
execute(%Q{
INSERT INTO notification_settings
(user_id, level, created_at, updated_at)
(SELECT id, notification_level, created_at, updated_at FROM users WHERE notification_level != 1)
})
end
# Migrates from notification settings back to user notification_level
# If no value is found the default level of 1 will be used
def down
execute(%Q{
UPDATE users u SET
notification_level = COALESCE((SELECT level FROM notification_settings WHERE user_id = u.id AND source_type IS NULL), 1)
})
end
end
class RemoveNotificationLevelFromUsers < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
def change
remove_column :users, :notification_level, :integer
end
end
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
- [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, Twitter. - [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, Twitter.
- [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages. - [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages.
- [Libravatar](customization/libravatar.md) Use Libravatar for user avatars. - [Libravatar](customization/libravatar.md) Use Libravatar for user avatars.
- [Log system](logs/logs.md) Log system. - [Log system](administration/logs.md) Log system.
- [Environment Variables](administration/environment_variables.md) to configure GitLab. - [Environment Variables](administration/environment_variables.md) to configure GitLab.
- [Operations](operations/README.md) Keeping GitLab up and running - [Operations](operations/README.md) Keeping GitLab up and running
- [Raketasks](raketasks/README.md) Backups, maintenance, automatic webhook setup and the importing of projects. - [Raketasks](raketasks/README.md) Backups, maintenance, automatic webhook setup and the importing of projects.
......
## Log system
GitLab has an advanced log system where everything is logged so that you
can analyze your instance using various system log files. In addition to
system log files, GitLab Enterprise Edition comes with Audit Events.
Find more about them [in Audit Events
documentation](http://docs.gitlab.com/ee/administration/audit_events.html)
System log files are typically plain text in a standard log file format.
This guide talks about how to read and use these system log files.
### production.log
This file lives in `/var/log/gitlab/gitlab-rails/production.log` for
omnibus package or in `/home/git/gitlab/log/production.log` for
installations from source.
It contains information about all performed requests. You can see the
URL and type of request, IP address and what exactly parts of code were
involved to service this particular request. Also you can see all SQL
request that have been performed and how much time it took. This task is
more useful for GitLab contributors and developers. Use part of this log
file when you are going to report bug. For example:
```
Started GET "/gitlabhq/yaml_db/tree/master" for 168.111.56.1 at 2015-02-12 19:34:53 +0200
Processing by Projects::TreeController#show as HTML
Parameters: {"project_id"=>"gitlabhq/yaml_db", "id"=>"master"}
... [CUT OUT]
Namespaces"."created_at" DESC, "namespaces"."id" DESC LIMIT 1 [["id", 26]]
CACHE (0.0ms) SELECT "members".* FROM "members" WHERE "members"."source_type" = 'Project' AND "members"."type" IN ('ProjectMember') AND "members"."source_id" = $1 AND "members"."source_type" = $2 AND "members"."user_id" = 1 ORDER BY "members"."created_at" DESC, "members"."id" DESC LIMIT 1 [["source_id", 18], ["source_type", "Project"]]
CACHE (0.0ms) SELECT "members".* FROM "members" WHERE "members"."source_type" = 'Project' AND "members".
(1.4ms) SELECT COUNT(*) FROM "merge_requests" WHERE "merge_requests"."target_project_id" = $1 AND ("merge_requests"."state" IN ('opened','reopened')) [["target_project_id", 18]]
Rendered layouts/nav/_project.html.haml (28.0ms)
Rendered layouts/_collapse_button.html.haml (0.2ms)
Rendered layouts/_flash.html.haml (0.1ms)
Rendered layouts/_page.html.haml (32.9ms)
Completed 200 OK in 166ms (Views: 117.4ms | ActiveRecord: 27.2ms)
```
In this example we can see that server processed an HTTP request with URL
`/gitlabhq/yaml_db/tree/master` from IP 168.111.56.1 at 2015-02-12
19:34:53 +0200. Also we can see that request was processed by
`Projects::TreeController`.
### application.log
This file lives in `/var/log/gitlab/gitlab-rails/application.log` for
omnibus package or in `/home/git/gitlab/log/application.log` for
installations from source.
It helps you discover events happening in your instance such as user creation,
project removing and so on. For example:
```
October 06, 2014 11:56: User "Administrator" (admin@example.com) was created
October 06, 2014 11:56: Documentcloud created a new project "Documentcloud / Underscore"
October 06, 2014 11:56: Gitlab Org created a new project "Gitlab Org / Gitlab Ce"
October 07, 2014 11:25: User "Claudie Hodkiewicz" (nasir_stehr@olson.co.uk) was removed
October 07, 2014 11:25: Project "project133" was removed
```
### githost.log
This file lives in `/var/log/gitlab/gitlab-rails/githost.log` for
omnibus package or in `/home/git/gitlab/log/githost.log` for
installations from source.
GitLab has to interact with Git repositories but in some rare cases
something can go wrong and in this case you will know what exactly
happened. This log file contains all failed requests from GitLab to Git
repositories. In the majority of cases this file will be useful for developers
only. For example:
```
December 03, 2014 13:20 -> ERROR -> Command failed [1]: /usr/bin/git --git-dir=/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/group184/gitlabhq/.git --work-tree=/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/group184/gitlabhq merge --no-ff -mMerge branch 'feature_conflict' into 'feature' source/feature_conflict
error: failed to push some refs to '/Users/vsizov/gitlab-development-kit/repositories/gitlabhq/gitlab_git.git'
```
### sidekiq.log
This file lives in `/var/log/gitlab/gitlab-rails/sidekiq.log` for
omnibus package or in `/home/git/gitlab/log/sidekiq.log` for
installations from source.
GitLab uses background jobs for processing tasks which can take a long
time. All information about processing these jobs are written down to
this file. For example:
```
2014-06-10T07:55:20Z 2037 TID-tm504 ERROR: /opt/bitnami/apps/discourse/htdocs/vendor/bundle/ruby/1.9.1/gems/redis-3.0.7/lib/redis/client.rb:228:in `read'
2014-06-10T18:18:26Z 14299 TID-55uqo INFO: Booting Sidekiq 3.0.0 with redis options {:url=>"redis://localhost:6379/0", :namespace=>"sidekiq"}
```
### gitlab-shell.log
This file lives in `/var/log/gitlab/gitlab-shell/gitlab-shell.log` for
omnibus package or in `/home/git/gitlab-shell/gitlab-shell.log` for
installations from source.
GitLab shell is used by Gitlab for executing Git commands and provide
SSH access to Git repositories. For example:
```
I, [2015-02-13T06:17:00.671315 #9291] INFO -- : Adding project root/example.git at </var/opt/gitlab/git-data/repositories/root/dcdcdcdcd.git>.
I, [2015-02-13T06:17:00.679433 #9291] INFO -- : Moving existing hooks directory and symlinking global hooks directory for /var/opt/gitlab/git-data/repositories/root/example.git.
```
### unicorn\_stderr.log
This file lives in `/var/log/gitlab/unicorn/unicorn_stderr.log` for
omnibus package or in `/home/git/gitlab/log/unicorn_stderr.log` for
installations from source.
Unicorn is a high-performance forking Web server which is used for
serving the GitLab application. You can look at this log if, for
example, your application does not respond. This log contains all
information about the state of unicorn processes at any given time.
```
I, [2015-02-13T06:14:46.680381 #9047] INFO -- : Refreshing Gem list
I, [2015-02-13T06:14:56.931002 #9047] INFO -- : listening on addr=127.0.0.1:8080 fd=12
I, [2015-02-13T06:14:56.931381 #9047] INFO -- : listening on addr=/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket fd=13
I, [2015-02-13T06:14:56.936638 #9047] INFO -- : master process ready
I, [2015-02-13T06:14:56.946504 #9092] INFO -- : worker=0 spawned pid=9092
I, [2015-02-13T06:14:56.946943 #9092] INFO -- : worker=0 ready
I, [2015-02-13T06:14:56.947892 #9094] INFO -- : worker=1 spawned pid=9094
I, [2015-02-13T06:14:56.948181 #9094] INFO -- : worker=1 ready
W, [2015-02-13T07:16:01.312916 #9094] WARN -- : #<Unicorn::HttpServer:0x0000000208f618>: worker (pid: 9094) exceeds memory limit (320626688 bytes > 247066940 bytes)
W, [2015-02-13T07:16:01.313000 #9094] WARN -- : Unicorn::WorkerKiller send SIGQUIT (pid: 9094) alive: 3621 sec (trial 1)
I, [2015-02-13T07:16:01.530733 #9047] INFO -- : reaped #<Process::Status: pid 9094 exit 0> worker=1
I, [2015-02-13T07:16:01.534501 #13379] INFO -- : worker=1 spawned pid=13379
I, [2015-02-13T07:16:01.534848 #13379] INFO -- : worker=1 ready
```
...@@ -103,14 +103,14 @@ Inside the document: ...@@ -103,14 +103,14 @@ Inside the document:
- Every piece of documentation that comes with a new feature should declare the - Every piece of documentation that comes with a new feature should declare the
GitLab version that feature got introduced. Right below the heading add a GitLab version that feature got introduced. Right below the heading add a
note: `_**Note:** This feature was introduced in GitLab 8.3_` note: `>**Note:** This feature was introduced in GitLab 8.3`
- If possible every feature should have a link to the MR that introduced it. - If possible every feature should have a link to the MR that introduced it.
The above note would be then transformed to: The above note would be then transformed to:
`_**Note:** This feature was [introduced][ce-1242] in GitLab 8.3_`, where `>**Note:** This feature was [introduced][ce-1242] in GitLab 8.3`, where
the [link identifier](#links) is named after the repository (CE) and the MR the [link identifier](#links) is named after the repository (CE) and the MR
number number
- If the feature is only in GitLab EE, don't forget to mention it, like: - If the feature is only in GitLab EE, don't forget to mention it, like:
`_**Note:** This feature was introduced in GitLab EE 8.3_`. Otherwise, leave `>**Note:** This feature was introduced in GitLab EE 8.3`. Otherwise, leave
this mention out this mention out
## References ## References
...@@ -141,6 +141,48 @@ Inside the document: ...@@ -141,6 +141,48 @@ Inside the document:
[ruby-dl]: https://www.ruby-lang.org/en/downloads/ "Ruby download website" [ruby-dl]: https://www.ruby-lang.org/en/downloads/ "Ruby download website"
## Changing document location
Changing a document's location is not to be taken lightly. Remember that the
documentation is available to all installations under `help/` and not only to
GitLab.com or http://docs.gitlab.com. Make sure this is discussed with the
Documentation team beforehand.
If you indeed need to change a document's location, do NOT remove the old
document, but rather put a text in it that points to the new location, like:
```
This document was moved to [path/to/new_doc.md](path/to/new_doc.md).
```
where `path/to/new_doc.md` is the relative path to the root directory `doc/`.
---
For example, if you were to move `doc/workflow/lfs/lfs_administration.md` to
`doc/administration/lfs.md`, then the steps would be:
1. Copy `doc/workflow/lfs/lfs_administration.md` to `doc/administration/lfs.md`
1. Replace the contents of `doc/workflow/lfs/lfs_administration.md` with:
```
This document was moved to [administration/lfs.md](../../administration/lfs.md).
```
1. Find and replace any occurrences of the old location with the new one.
A quick way to find them is to use `grep`:
```
grep -nR "lfs_administration.md" doc/
```
The above command will search in the `doc/` directory for
`lfs_administration.md` recursively and will print the file and the line
where this file is mentioned. Note that we used just the filename
(`lfs_administration.md`) and not the whole the relative path
(`workflow/lfs/lfs_administration.md`).
## API ## API
Here is a list of must-have items. Use them in the exact order that appears Here is a list of must-have items. Use them in the exact order that appears
...@@ -222,8 +264,8 @@ curl --data "name=foo" -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab. ...@@ -222,8 +264,8 @@ curl --data "name=foo" -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.
#### Post data using JSON content #### Post data using JSON content
_**Note:** In this example we create a new group. Watch carefully the single > **Note:** In this example we create a new group. Watch carefully the single
and double quotes._ and double quotes.
```bash ```bash
curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -H "Content-Type: application/json" --data '{"path": "my-group", "name": "My group"}' https://gitlab.example.com/api/v3/groups curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -H "Content-Type: application/json" --data '{"path": "my-group", "name": "My group"}' https://gitlab.example.com/api/v3/groups
......
## Log system This document was moved to [administration/logs.md](../administration/logs.md).
GitLab has an advanced log system where everything is logged so that you can analyze your instance using various system log files.
In addition to system log files, GitLab Enterprise Edition comes with Audit Events. Find more about them [in Audit Events documentation](http://docs.gitlab.com/ee/administration/audit_events.html)
System log files are typically plain text in a standard log file format. This guide talks about how to read and use these system log files.
#### production.log
This file lives in `/var/log/gitlab/gitlab-rails/production.log` for omnibus package or in `/home/git/gitlab/log/production.log` for installations from the source.
This file contains information about all performed requests. You can see url and type of request, IP address and what exactly parts of code were involved to service this particular request. Also you can see all SQL request that have been performed and how much time it took.
This task is more useful for GitLab contributors and developers. Use part of this log file when you are going to report bug.
```
Started GET "/gitlabhq/yaml_db/tree/master" for 168.111.56.1 at 2015-02-12 19:34:53 +0200
Processing by Projects::TreeController#show as HTML
Parameters: {"project_id"=>"gitlabhq/yaml_db", "id"=>"master"}
... [CUT OUT]
amespaces"."created_at" DESC, "namespaces"."id" DESC LIMIT 1 [["id", 26]]
CACHE (0.0ms) SELECT "members".* FROM "members" WHERE "members"."source_type" = 'Project' AND "members"."type" IN ('ProjectMember') AND "members"."source_id" = $1 AND "members"."source_type" = $2 AND "members"."user_id" = 1 ORDER BY "members"."created_at" DESC, "members"."id" DESC LIMIT 1 [["source_id", 18], ["source_type", "Project"]]
CACHE (0.0ms) SELECT "members".* FROM "members" WHERE "members"."source_type" = 'Project' AND "members".
 (1.4ms) SELECT COUNT(*) FROM "merge_requests" WHERE "merge_requests"."target_project_id" = $1 AND ("merge_requests"."state" IN ('opened','reopened')) [["target_project_id", 18]]
Rendered layouts/nav/_project.html.haml (28.0ms)
Rendered layouts/_collapse_button.html.haml (0.2ms)
Rendered layouts/_flash.html.haml (0.1ms)
Rendered layouts/_page.html.haml (32.9ms)
Completed 200 OK in 166ms (Views: 117.4ms | ActiveRecord: 27.2ms)
```
In this example we can see that server processed HTTP request with url `/gitlabhq/yaml_db/tree/master` from IP 168.111.56.1 at 2015-02-12 19:34:53 +0200. Also we can see that request was processed by Projects::TreeController.
#### application.log
This file lives in `/var/log/gitlab/gitlab-rails/application.log` for omnibus package or in `/home/git/gitlab/log/application.log` for installations from the source.
This log file helps you discover events happening in your instance such as user creation, project removing and so on.
```
October 06, 2014 11:56: User "Administrator" (admin@example.com) was created
October 06, 2014 11:56: Documentcloud created a new project "Documentcloud / Underscore"
October 06, 2014 11:56: Gitlab Org created a new project "Gitlab Org / Gitlab Ce"
October 07, 2014 11:25: User "Claudie Hodkiewicz" (nasir_stehr@olson.co.uk) was removed
October 07, 2014 11:25: Project "project133" was removed
```
#### githost.log
This file lives in `/var/log/gitlab/gitlab-rails/githost.log` for omnibus package or in `/home/git/gitlab/log/githost.log` for installations from the source.
The GitLab has to interact with git repositories but in some rare cases something can go wrong and in this case you will know what exactly happened. This log file contains all failed requests from GitLab to git repository. In majority of cases this file will be useful for developers only.
```
December 03, 2014 13:20 -> ERROR -> Command failed [1]: /usr/bin/git --git-dir=/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/group184/gitlabhq/.git --work-tree=/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/group184/gitlabhq merge --no-ff -mMerge branch 'feature_conflict' into 'feature' source/feature_conflict
error: failed to push some refs to '/Users/vsizov/gitlab-development-kit/repositories/gitlabhq/gitlab_git.git'
```
#### sidekiq.log
This file lives in `/var/log/gitlab/gitlab-rails/sidekiq.log` for omnibus package or in `/home/git/gitlab/log/sidekiq.log` for installations from the source.
GitLab uses background jobs for processing tasks which can take a long time. All information about processing these jobs are writing down to this file.
```
2014-06-10T07:55:20Z 2037 TID-tm504 ERROR: /opt/bitnami/apps/discourse/htdocs/vendor/bundle/ruby/1.9.1/gems/redis-3.0.7/lib/redis/client.rb:228:in `read'
2014-06-10T18:18:26Z 14299 TID-55uqo INFO: Booting Sidekiq 3.0.0 with redis options {:url=>"redis://localhost:6379/0", :namespace=>"sidekiq"}
```
#### gitlab-shell.log
This file lives in `/var/log/gitlab/gitlab-shell/gitlab-shell.log` for omnibus package or in `/home/git/gitlab-shell/gitlab-shell.log` for installations from the source.
gitlab-shell is using by Gitlab for executing git commands and provide ssh access to git repositories.
```
I, [2015-02-13T06:17:00.671315 #9291] INFO -- : Adding project root/example.git at </var/opt/gitlab/git-data/repositories/root/dcdcdcdcd.git>.
I, [2015-02-13T06:17:00.679433 #9291] INFO -- : Moving existing hooks directory and symlinking global hooks directory for /var/opt/gitlab/git-data/repositories/root/example.git.
```
#### unicorn_stderr.log
This file lives in `/var/log/gitlab/unicorn/unicorn_stderr.log` for omnibus package or in `/home/git/gitlab/log/unicorn_stderr.log` for installations from the source.
Unicorn is a high-performance forking Web server which is used for serving the GitLab application. You can look at this log if, for example, your application does not respond. This log contains all information about the state of unicorn processes at any given time.
```
I, [2015-02-13T06:14:46.680381 #9047] INFO -- : Refreshing Gem list
I, [2015-02-13T06:14:56.931002 #9047] INFO -- : listening on addr=127.0.0.1:8080 fd=12
I, [2015-02-13T06:14:56.931381 #9047] INFO -- : listening on addr=/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket fd=13
I, [2015-02-13T06:14:56.936638 #9047] INFO -- : master process ready
I, [2015-02-13T06:14:56.946504 #9092] INFO -- : worker=0 spawned pid=9092
I, [2015-02-13T06:14:56.946943 #9092] INFO -- : worker=0 ready
I, [2015-02-13T06:14:56.947892 #9094] INFO -- : worker=1 spawned pid=9094
I, [2015-02-13T06:14:56.948181 #9094] INFO -- : worker=1 ready
W, [2015-02-13T07:16:01.312916 #9094] WARN -- : #<Unicorn::HttpServer:0x0000000208f618>: worker (pid: 9094) exceeds memory limit (320626688 bytes > 247066940 bytes)
W, [2015-02-13T07:16:01.313000 #9094] WARN -- : Unicorn::WorkerKiller send SIGQUIT (pid: 9094) alive: 3621 sec (trial 1)
I, [2015-02-13T07:16:01.530733 #9047] INFO -- : reaped #<Process::Status: pid 9094 exit 0> worker=1
I, [2015-02-13T07:16:01.534501 #13379] INFO -- : worker=1 spawned pid=13379
I, [2015-02-13T07:16:01.534848 #13379] INFO -- : worker=1 ready
```
...@@ -31,8 +31,6 @@ module Gitlab ...@@ -31,8 +31,6 @@ module Gitlab
# Any data inserted while running this method (or after it has finished # Any data inserted while running this method (or after it has finished
# running) is _not_ updated automatically. # running) is _not_ updated automatically.
# #
# This method _only_ updates rows where the column's value is set to NULL.
#
# table - The name of the table. # table - The name of the table.
# column - The name of the column to update. # column - The name of the column to update.
# value - The value for the column. # value - The value for the column.
...@@ -55,10 +53,10 @@ module Gitlab ...@@ -55,10 +53,10 @@ module Gitlab
first['count']. first['count'].
to_i to_i
# Update in batches of 5% # Update in batches of 5% until we run out of any rows to update.
batch_size = ((total / 100.0) * 5.0).ceil batch_size = ((total / 100.0) * 5.0).ceil
while processed < total loop do
start_row = exec_query(%Q{ start_row = exec_query(%Q{
SELECT id SELECT id
FROM #{quoted_table} FROM #{quoted_table}
...@@ -66,6 +64,9 @@ module Gitlab ...@@ -66,6 +64,9 @@ module Gitlab
LIMIT 1 OFFSET #{processed} LIMIT 1 OFFSET #{processed}
}).to_hash.first }).to_hash.first
# There are no more rows to process
break unless start_row
stop_row = exec_query(%Q{ stop_row = exec_query(%Q{
SELECT id SELECT id
FROM #{quoted_table} FROM #{quoted_table}
...@@ -126,6 +127,8 @@ module Gitlab ...@@ -126,6 +127,8 @@ module Gitlab
begin begin
transaction do transaction do
update_column_in_batches(table, column, default) update_column_in_batches(table, column, default)
change_column_null(table, column, false) unless allow_null
end end
# We want to rescue _all_ exceptions here, even those that don't inherit # We want to rescue _all_ exceptions here, even those that don't inherit
# from StandardError. # from StandardError.
...@@ -134,8 +137,6 @@ module Gitlab ...@@ -134,8 +137,6 @@ module Gitlab
raise error raise error
end end
change_column_null(table, column, false) unless allow_null
end end
end end
end end
......
...@@ -16,10 +16,10 @@ retry() { ...@@ -16,10 +16,10 @@ retry() {
} }
if [ -f /.dockerenv ] || [ -f ./dockerinit ]; then if [ -f /.dockerenv ] || [ -f ./dockerinit ]; then
mkdir -p vendor mkdir -p vendor/apt
# Install phantomjs package # Install phantomjs package
pushd vendor pushd vendor/apt
if [ ! -e phantomjs_1.9.8-0jessie_amd64.deb ]; then if [ ! -e phantomjs_1.9.8-0jessie_amd64.deb ]; then
wget -q https://gitlab.com/axil/phantomjs-debian/raw/master/phantomjs_1.9.8-0jessie_amd64.deb wget -q https://gitlab.com/axil/phantomjs-debian/raw/master/phantomjs_1.9.8-0jessie_amd64.deb
fi fi
......
...@@ -16,6 +16,7 @@ describe 'Cherry-pick Commits' do ...@@ -16,6 +16,7 @@ describe 'Cherry-pick Commits' do
it do it do
visit namespace_project_commit_path(project.namespace, project, master_pickable_commit.id) visit namespace_project_commit_path(project.namespace, project, master_pickable_commit.id)
find("a[href='#modal-cherry-pick-commit']").click find("a[href='#modal-cherry-pick-commit']").click
expect(page).not_to have_content('v1.0.0') # Only branches, not tags
page.within('#modal-cherry-pick-commit') do page.within('#modal-cherry-pick-commit') do
uncheck 'create_merge_request' uncheck 'create_merge_request'
click_button 'Cherry-pick' click_button 'Cherry-pick'
......
...@@ -120,6 +120,19 @@ describe Gitlab::Database::MigrationHelpers, lib: true do ...@@ -120,6 +120,19 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
model.add_column_with_default(:projects, :foo, :integer, default: 10) model.add_column_with_default(:projects, :foo, :integer, default: 10)
end.to raise_error(RuntimeError) end.to raise_error(RuntimeError)
end end
it 'removes the added column whenever changing a column NULL constraint fails' do
expect(model).to receive(:change_column_null).
with(:projects, :foo, false).
and_raise(RuntimeError)
expect(model).to receive(:remove_column).
with(:projects, :foo)
expect do
model.add_column_with_default(:projects, :foo, :integer, default: 10)
end.to raise_error(RuntimeError)
end
end end
context 'inside a transaction' do context 'inside a transaction' do
......
...@@ -10,7 +10,6 @@ RSpec.describe NotificationSetting, type: :model do ...@@ -10,7 +10,6 @@ RSpec.describe NotificationSetting, type: :model do
subject { NotificationSetting.new(source_id: 1, source_type: 'Project') } subject { NotificationSetting.new(source_id: 1, source_type: 'Project') }
it { is_expected.to validate_presence_of(:user) } it { is_expected.to validate_presence_of(:user) }
it { is_expected.to validate_presence_of(:source) }
it { is_expected.to validate_presence_of(:level) } it { is_expected.to validate_presence_of(:level) }
it { is_expected.to validate_uniqueness_of(:user_id).scoped_to([:source_id, :source_type]).with_message(/already exists in source/) } it { is_expected.to validate_uniqueness_of(:user_id).scoped_to([:source_id, :source_type]).with_message(/already exists in source/) }
end end
......
...@@ -72,6 +72,7 @@ describe NotificationService, services: true do ...@@ -72,6 +72,7 @@ describe NotificationService, services: true do
should_not_email(@u_disabled) should_not_email(@u_disabled)
should_not_email(@unsubscriber) should_not_email(@unsubscriber)
should_not_email(@u_outsider_mentioned) should_not_email(@u_outsider_mentioned)
should_not_email(@u_lazy_participant)
end end
it 'filters out "mentioned in" notes' do it 'filters out "mentioned in" notes' do
...@@ -80,6 +81,20 @@ describe NotificationService, services: true do ...@@ -80,6 +81,20 @@ describe NotificationService, services: true do
expect(Notify).not_to receive(:note_issue_email) expect(Notify).not_to receive(:note_issue_email)
notification.new_note(mentioned_note) notification.new_note(mentioned_note)
end end
context 'participating' do
context 'by note' do
before do
ActionMailer::Base.deliveries.clear
note.author = @u_lazy_participant
note.save
notification.new_note(note)
end
it { should_not_email(@u_lazy_participant) }
end
end
end end
describe 'new note on issue in project that belongs to a group' do describe 'new note on issue in project that belongs to a group' do
...@@ -106,6 +121,7 @@ describe NotificationService, services: true do ...@@ -106,6 +121,7 @@ describe NotificationService, services: true do
should_not_email(note.author) should_not_email(note.author)
should_not_email(@u_participating) should_not_email(@u_participating)
should_not_email(@u_disabled) should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end end
end end
end end
...@@ -235,6 +251,7 @@ describe NotificationService, services: true do ...@@ -235,6 +251,7 @@ describe NotificationService, services: true do
should_not_email(note.author) should_not_email(note.author)
should_not_email(@u_participating) should_not_email(@u_participating)
should_not_email(@u_disabled) should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end end
it do it do
...@@ -248,10 +265,11 @@ describe NotificationService, services: true do ...@@ -248,10 +265,11 @@ describe NotificationService, services: true do
should_not_email(note.author) should_not_email(note.author)
should_not_email(@u_participating) should_not_email(@u_participating)
should_not_email(@u_disabled) should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end end
it do it do
@u_committer.update_attributes(notification_level: :mention) @u_committer = create_global_setting_for(@u_committer, :mention)
notification.new_note(note) notification.new_note(note)
should_not_email(@u_committer) should_not_email(@u_committer)
end end
...@@ -280,10 +298,11 @@ describe NotificationService, services: true do ...@@ -280,10 +298,11 @@ describe NotificationService, services: true do
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)
should_not_email(@u_lazy_participant)
end end
it do it do
issue.assignee.update_attributes(notification_level: :mention) create_global_setting_for(issue.assignee, :mention)
notification.new_issue(issue, @u_disabled) notification.new_issue(issue, @u_disabled)
should_not_email(issue.assignee) should_not_email(issue.assignee)
...@@ -341,6 +360,7 @@ describe NotificationService, services: true do ...@@ -341,6 +360,7 @@ describe NotificationService, services: true do
should_not_email(@unsubscriber) should_not_email(@unsubscriber)
should_not_email(@u_participating) should_not_email(@u_participating)
should_not_email(@u_disabled) should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end end
it 'emails previous assignee even if he has the "on mention" notif level' do it 'emails previous assignee even if he has the "on mention" notif level' do
...@@ -356,6 +376,7 @@ describe NotificationService, services: true do ...@@ -356,6 +376,7 @@ describe NotificationService, services: true do
should_not_email(@unsubscriber) should_not_email(@unsubscriber)
should_not_email(@u_participating) should_not_email(@u_participating)
should_not_email(@u_disabled) should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end end
it 'emails new assignee even if he has the "on mention" notif level' do it 'emails new assignee even if he has the "on mention" notif level' do
...@@ -371,6 +392,7 @@ describe NotificationService, services: true do ...@@ -371,6 +392,7 @@ describe NotificationService, services: true do
should_not_email(@unsubscriber) should_not_email(@unsubscriber)
should_not_email(@u_participating) should_not_email(@u_participating)
should_not_email(@u_disabled) should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end end
it 'emails new assignee' do it 'emails new assignee' do
...@@ -386,6 +408,7 @@ describe NotificationService, services: true do ...@@ -386,6 +408,7 @@ describe NotificationService, services: true do
should_not_email(@unsubscriber) should_not_email(@unsubscriber)
should_not_email(@u_participating) should_not_email(@u_participating)
should_not_email(@u_disabled) should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end end
it 'does not email new assignee if they are the current user' do it 'does not email new assignee if they are the current user' do
...@@ -401,6 +424,35 @@ describe NotificationService, services: true do ...@@ -401,6 +424,35 @@ describe NotificationService, services: true do
should_not_email(@unsubscriber) should_not_email(@unsubscriber)
should_not_email(@u_participating) should_not_email(@u_participating)
should_not_email(@u_disabled) should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
context 'participating' do
context 'by assignee' do
before do
issue.update_attribute(:assignee, @u_lazy_participant)
notification.reassigned_issue(issue, @u_disabled)
end
it { should_email(@u_lazy_participant) }
end
context 'by note' do
let!(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: 'anything', author: @u_lazy_participant) }
before { notification.reassigned_issue(issue, @u_disabled) }
it { should_email(@u_lazy_participant) }
end
context 'by author' do
before do
issue.author = @u_lazy_participant
notification.reassigned_issue(issue, @u_disabled)
end
it { should_email(@u_lazy_participant) }
end
end end
end end
...@@ -479,6 +531,35 @@ describe NotificationService, services: true do ...@@ -479,6 +531,35 @@ describe NotificationService, services: true do
should_not_email(@unsubscriber) should_not_email(@unsubscriber)
should_not_email(@u_participating) should_not_email(@u_participating)
should_not_email(@u_disabled) should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
context 'participating' do
context 'by assignee' do
before do
issue.update_attribute(:assignee, @u_lazy_participant)
notification.close_issue(issue, @u_disabled)
end
it { should_email(@u_lazy_participant) }
end
context 'by note' do
let!(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: 'anything', author: @u_lazy_participant) }
before { notification.close_issue(issue, @u_disabled) }
it { should_email(@u_lazy_participant) }
end
context 'by author' do
before do
issue.author = @u_lazy_participant
notification.close_issue(issue, @u_disabled)
end
it { should_email(@u_lazy_participant) }
end
end end
end end
...@@ -495,6 +576,35 @@ describe NotificationService, services: true do ...@@ -495,6 +576,35 @@ describe NotificationService, services: true do
should_email(@watcher_and_subscriber) should_email(@watcher_and_subscriber)
should_not_email(@unsubscriber) should_not_email(@unsubscriber)
should_not_email(@u_participating) should_not_email(@u_participating)
should_not_email(@u_lazy_participant)
end
context 'participating' do
context 'by assignee' do
before do
issue.update_attribute(:assignee, @u_lazy_participant)
notification.reopen_issue(issue, @u_disabled)
end
it { should_email(@u_lazy_participant) }
end
context 'by note' do
let!(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: 'anything', author: @u_lazy_participant) }
before { notification.reopen_issue(issue, @u_disabled) }
it { should_email(@u_lazy_participant) }
end
context 'by author' do
before do
issue.author = @u_lazy_participant
notification.reopen_issue(issue, @u_disabled)
end
it { should_email(@u_lazy_participant) }
end
end end
end end
end end
...@@ -520,6 +630,7 @@ describe NotificationService, services: true do ...@@ -520,6 +630,7 @@ describe NotificationService, services: true do
should_email(@u_guest_watcher) should_email(@u_guest_watcher)
should_not_email(@u_participating) should_not_email(@u_participating)
should_not_email(@u_disabled) should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end end
it "emails subscribers of the merge request's labels" do it "emails subscribers of the merge request's labels" do
...@@ -530,6 +641,36 @@ describe NotificationService, services: true do ...@@ -530,6 +641,36 @@ describe NotificationService, services: true do
should_email(subscriber) should_email(subscriber)
end end
context 'participating' do
context 'by assignee' do
before do
merge_request.update_attribute(:assignee, @u_lazy_participant)
notification.new_merge_request(merge_request, @u_disabled)
end
it { should_email(@u_lazy_participant) }
end
context 'by note' do
let!(:note) { create(:note_on_issue, noteable: merge_request, project_id: project.id, note: 'anything', author: @u_lazy_participant) }
before { notification.new_merge_request(merge_request, @u_disabled) }
it { should_email(@u_lazy_participant) }
end
context 'by author' do
before do
merge_request.author = @u_lazy_participant
merge_request.save
notification.new_merge_request(merge_request, @u_disabled)
end
it { should_not_email(@u_lazy_participant) }
end
end
end end
describe '#reassigned_merge_request' do describe '#reassigned_merge_request' do
...@@ -545,6 +686,36 @@ describe NotificationService, services: true do ...@@ -545,6 +686,36 @@ describe NotificationService, services: true do
should_not_email(@unsubscriber) should_not_email(@unsubscriber)
should_not_email(@u_participating) should_not_email(@u_participating)
should_not_email(@u_disabled) should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
context 'participating' do
context 'by assignee' do
before do
merge_request.update_attribute(:assignee, @u_lazy_participant)
notification.reassigned_merge_request(merge_request, @u_disabled)
end
it { should_email(@u_lazy_participant) }
end
context 'by note' do
let!(:note) { create(:note_on_issue, noteable: merge_request, project_id: project.id, note: 'anything', author: @u_lazy_participant) }
before { notification.reassigned_merge_request(merge_request, @u_disabled) }
it { should_email(@u_lazy_participant) }
end
context 'by author' do
before do
merge_request.author = @u_lazy_participant
merge_request.save
notification.reassigned_merge_request(merge_request, @u_disabled)
end
it { should_email(@u_lazy_participant) }
end
end end
end end
...@@ -572,6 +743,7 @@ describe NotificationService, services: true do ...@@ -572,6 +743,7 @@ describe NotificationService, services: true do
should_not_email(@watcher_and_subscriber) should_not_email(@watcher_and_subscriber)
should_not_email(@unsubscriber) should_not_email(@unsubscriber)
should_not_email(@u_participating) should_not_email(@u_participating)
should_not_email(@u_lazy_participant)
should_not_email(subscriber_to_label) should_not_email(subscriber_to_label)
should_email(subscriber_to_label2) should_email(subscriber_to_label2)
end end
...@@ -590,6 +762,36 @@ describe NotificationService, services: true do ...@@ -590,6 +762,36 @@ describe NotificationService, services: true do
should_not_email(@unsubscriber) should_not_email(@unsubscriber)
should_not_email(@u_participating) should_not_email(@u_participating)
should_not_email(@u_disabled) should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
context 'participating' do
context 'by assignee' do
before do
merge_request.update_attribute(:assignee, @u_lazy_participant)
notification.close_mr(merge_request, @u_disabled)
end
it { should_email(@u_lazy_participant) }
end
context 'by note' do
let!(:note) { create(:note_on_issue, noteable: merge_request, project_id: project.id, note: 'anything', author: @u_lazy_participant) }
before { notification.close_mr(merge_request, @u_disabled) }
it { should_email(@u_lazy_participant) }
end
context 'by author' do
before do
merge_request.author = @u_lazy_participant
merge_request.save
notification.close_mr(merge_request, @u_disabled)
end
it { should_email(@u_lazy_participant) }
end
end end
end end
...@@ -606,6 +808,36 @@ describe NotificationService, services: true do ...@@ -606,6 +808,36 @@ describe NotificationService, services: true do
should_not_email(@unsubscriber) should_not_email(@unsubscriber)
should_not_email(@u_participating) should_not_email(@u_participating)
should_not_email(@u_disabled) should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
context 'participating' do
context 'by assignee' do
before do
merge_request.update_attribute(:assignee, @u_lazy_participant)
notification.merge_mr(merge_request, @u_disabled)
end
it { should_email(@u_lazy_participant) }
end
context 'by note' do
let!(:note) { create(:note_on_issue, noteable: merge_request, project_id: project.id, note: 'anything', author: @u_lazy_participant) }
before { notification.merge_mr(merge_request, @u_disabled) }
it { should_email(@u_lazy_participant) }
end
context 'by author' do
before do
merge_request.author = @u_lazy_participant
merge_request.save
notification.merge_mr(merge_request, @u_disabled)
end
it { should_email(@u_lazy_participant) }
end
end end
end end
...@@ -622,6 +854,36 @@ describe NotificationService, services: true do ...@@ -622,6 +854,36 @@ describe NotificationService, services: true do
should_not_email(@unsubscriber) should_not_email(@unsubscriber)
should_not_email(@u_participating) should_not_email(@u_participating)
should_not_email(@u_disabled) should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
context 'participating' do
context 'by assignee' do
before do
merge_request.update_attribute(:assignee, @u_lazy_participant)
notification.reopen_mr(merge_request, @u_disabled)
end
it { should_email(@u_lazy_participant) }
end
context 'by note' do
let!(:note) { create(:note_on_issue, noteable: merge_request, project_id: project.id, note: 'anything', author: @u_lazy_participant) }
before { notification.reopen_mr(merge_request, @u_disabled) }
it { should_email(@u_lazy_participant) }
end
context 'by author' do
before do
merge_request.author = @u_lazy_participant
merge_request.save
notification.reopen_mr(merge_request, @u_disabled)
end
it { should_email(@u_lazy_participant) }
end
end end
end end
end end
...@@ -640,6 +902,7 @@ describe NotificationService, services: true do ...@@ -640,6 +902,7 @@ describe NotificationService, services: true do
should_email(@u_watcher) should_email(@u_watcher)
should_email(@u_participating) should_email(@u_participating)
should_email(@u_lazy_participant)
should_not_email(@u_guest_watcher) should_not_email(@u_guest_watcher)
should_not_email(@u_disabled) should_not_email(@u_disabled)
end end
...@@ -647,15 +910,20 @@ describe NotificationService, services: true do ...@@ -647,15 +910,20 @@ describe NotificationService, services: true do
end end
def build_team(project) def build_team(project)
@u_watcher = create(:user, notification_level: :watch) @u_watcher = create_global_setting_for(create(:user), :watch)
@u_participating = create(:user, notification_level: :participating) @u_participating = create_global_setting_for(create(:user), :participating)
@u_participant_mentioned = create(:user, username: 'participant', notification_level: :participating) @u_participant_mentioned = create_global_setting_for(create(:user, username: 'participant'), :participating)
@u_disabled = create(:user, notification_level: :disabled) @u_disabled = create_global_setting_for(create(:user), :disabled)
@u_mentioned = create(:user, username: 'mention', notification_level: :mention) @u_mentioned = create_global_setting_for(create(:user, username: 'mention'), :mention)
@u_committer = create(:user, username: 'committer') @u_committer = create(:user, username: 'committer')
@u_not_mentioned = create(:user, username: 'regular', notification_level: :participating) @u_not_mentioned = create_global_setting_for(create(:user, username: 'regular'), :participating)
@u_outsider_mentioned = create(:user, username: 'outsider') @u_outsider_mentioned = create(:user, username: 'outsider')
# User to be participant by default
# This user does not contain any record in notification settings table
# It should be treated with a :participating notification_level
@u_lazy_participant = create(:user, username: 'lazy-participant')
create_guest_watcher create_guest_watcher
project.team << [@u_watcher, :master] project.team << [@u_watcher, :master]
...@@ -665,6 +933,15 @@ describe NotificationService, services: true do ...@@ -665,6 +933,15 @@ describe NotificationService, services: true do
project.team << [@u_mentioned, :master] project.team << [@u_mentioned, :master]
project.team << [@u_committer, :master] project.team << [@u_committer, :master]
project.team << [@u_not_mentioned, :master] project.team << [@u_not_mentioned, :master]
project.team << [@u_lazy_participant, :master]
end
def create_global_setting_for(user, level)
setting = user.global_notification_setting
setting.level = level
setting.save
user
end end
def create_guest_watcher def create_guest_watcher
...@@ -677,8 +954,8 @@ describe NotificationService, services: true do ...@@ -677,8 +954,8 @@ describe NotificationService, services: true 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
@subscribed_participant = create(:user, username: 'subscribed_participant', notification_level: :participating) @subscribed_participant = create_global_setting_for(create(:user, username: 'subscribed_participant'), :participating)
@watcher_and_subscriber = create(:user, notification_level: :watch) @watcher_and_subscriber = create_global_setting_for(create(:user), :watch)
project.team << [@subscribed_participant, :master] project.team << [@subscribed_participant, :master]
project.team << [@subscriber, :master] project.team << [@subscriber, :master]
......
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