summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/api/branches.rb7
-rw-r--r--lib/api/deploy_keys.rb27
-rw-r--r--lib/api/helpers.rb6
-rw-r--r--lib/api/merge_request_diffs.rb8
-rw-r--r--lib/api/merge_requests.rb25
-rw-r--r--lib/api/notes.rb26
-rw-r--r--lib/api/subscriptions.rb4
-rw-r--r--lib/api/todos.rb2
-rw-r--r--lib/banzai/filter/reference_filter.rb4
-rw-r--r--lib/banzai/filter/user_reference_filter.rb4
-rw-r--r--lib/banzai/renderer.rb4
-rw-r--r--lib/ci/api/builds.rb25
-rw-r--r--lib/gitlab/current_settings.rb44
-rw-r--r--lib/gitlab/email/handler.rb3
-rw-r--r--lib/gitlab/email/handler/base_handler.rb43
-rw-r--r--lib/gitlab/email/handler/create_issue_handler.rb1
-rw-r--r--lib/gitlab/email/handler/create_note_handler.rb7
-rw-r--r--lib/gitlab/email/handler/reply_processing.rb54
-rw-r--r--lib/gitlab/email/handler/unsubscribe_handler.rb32
-rw-r--r--lib/gitlab/github_import/project_creator.rb4
-rw-r--r--lib/gitlab/import_sources.rb2
-rw-r--r--lib/gitlab/incoming_email.rb9
-rw-r--r--lib/gitlab/job_waiter.rb27
-rw-r--r--lib/gitlab/project_search_results.rb28
-rw-r--r--lib/gitlab/search_results.rb4
-rw-r--r--lib/gitlab/sidekiq_status.rb66
-rw-r--r--lib/gitlab/sidekiq_status/client_middleware.rb10
-rw-r--r--lib/gitlab/sidekiq_status/server_middleware.rb13
-rw-r--r--lib/gitlab/user_access.rb4
-rw-r--r--lib/gitlab/visibility_level.rb1
-rw-r--r--lib/mattermost/client.rb22
-rw-r--r--lib/mattermost/command.rb2
-rw-r--r--lib/mattermost/team.rb2
-rw-r--r--lib/tasks/gitlab/info.rake5
34 files changed, 369 insertions, 156 deletions
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index 0950c3d2e88..be659fa4a6a 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -129,12 +129,7 @@ module API
end
end
- # Delete all merged branches
- #
- # Parameters:
- # id (required) - The ID of a project
- # Example Request:
- # DELETE /projects/:id/repository/branches/delete_merged
+ desc 'Delete all merged branches'
delete ":id/repository/merged_branches" do
DeleteMergedBranchesService.new(user_project, current_user).async_execute
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index 85360730841..64da7d6b86f 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -38,26 +38,25 @@ module API
present key, with: Entities::SSHKey
end
- # TODO: for 9.0 we should check if params are there with the params block
- # grape provides, at this point we'd change behaviour so we can't
- # Behaviour now if you don't provide all required params: it renders a
- # validation error or two.
desc 'Add new deploy key to currently authenticated user' do
success Entities::SSHKey
end
+ params do
+ requires :key, type: String, desc: 'The new deploy key'
+ requires :title, type: String, desc: 'The name of the deploy key'
+ end
post ":id/#{path}" do
- attrs = attributes_for_keys [:title, :key]
- attrs[:key].strip! if attrs[:key]
+ params[:key].strip!
# Check for an existing key joined to this project
- key = user_project.deploy_keys.find_by(key: attrs[:key])
+ key = user_project.deploy_keys.find_by(key: params[:key])
if key
present key, with: Entities::SSHKey
break
end
# Check for available deploy keys in other projects
- key = current_user.accessible_deploy_keys.find_by(key: attrs[:key])
+ key = current_user.accessible_deploy_keys.find_by(key: params[:key])
if key
user_project.deploy_keys << key
present key, with: Entities::SSHKey
@@ -65,7 +64,7 @@ module API
end
# Create a new deploy key
- key = DeployKey.new attrs
+ key = DeployKey.new(declared_params(include_missing: false))
if key.valid? && user_project.deploy_keys << key
present key, with: Entities::SSHKey
else
@@ -105,15 +104,19 @@ module API
present key.deploy_key, with: Entities::SSHKey
end
- desc 'Delete existing deploy key of currently authenticated user' do
+ desc 'Delete deploy key for a project' do
success Key
end
params do
requires :key_id, type: Integer, desc: 'The ID of the deploy key'
end
delete ":id/#{path}/:key_id" do
- key = user_project.deploy_keys.find(params[:key_id])
- key.destroy
+ key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id])
+ if key
+ key.destroy
+ else
+ not_found!('Deploy Key')
+ end
end
end
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 49c5f0652ab..a1d7b323f4f 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -90,6 +90,12 @@ module API
MergeRequestsFinder.new(current_user, project_id: user_project.id).find(id)
end
+ def find_merge_request_with_access(id, access_level = :read_merge_request)
+ merge_request = user_project.merge_requests.find(id)
+ authorize! access_level, merge_request
+ merge_request
+ end
+
def authenticate!
unauthorized! unless current_user
end
diff --git a/lib/api/merge_request_diffs.rb b/lib/api/merge_request_diffs.rb
index 07435d78468..bc3d69f6904 100644
--- a/lib/api/merge_request_diffs.rb
+++ b/lib/api/merge_request_diffs.rb
@@ -15,10 +15,8 @@ module API
end
get ":id/merge_requests/:merge_request_id/versions" do
- merge_request = user_project.merge_requests.
- find(params[:merge_request_id])
+ merge_request = find_merge_request_with_access(params[:merge_request_id])
- authorize! :read_merge_request, merge_request
present merge_request.merge_request_diffs, with: Entities::MergeRequestDiff
end
@@ -34,10 +32,8 @@ module API
end
get ":id/merge_requests/:merge_request_id/versions/:version_id" do
- merge_request = user_project.merge_requests.
- find(params[:merge_request_id])
+ merge_request = find_merge_request_with_access(params[:merge_request_id])
- authorize! :read_merge_request, merge_request
present merge_request.merge_request_diffs.find(params[:version_id]), with: Entities::MergeRequestDiffFull
end
end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index e77af4b7a0d..7ffb38e62da 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -118,8 +118,8 @@ module API
success Entities::MergeRequest
end
get path do
- merge_request = find_project_merge_request(params[:merge_request_id])
- authorize! :read_merge_request, merge_request
+ merge_request = find_merge_request_with_access(params[:merge_request_id])
+
present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
end
@@ -127,8 +127,8 @@ module API
success Entities::RepoCommit
end
get "#{path}/commits" do
- merge_request = find_project_merge_request(params[:merge_request_id])
- authorize! :read_merge_request, merge_request
+ merge_request = find_merge_request_with_access(params[:merge_request_id])
+
present merge_request.commits, with: Entities::RepoCommit
end
@@ -136,8 +136,8 @@ module API
success Entities::MergeRequestChanges
end
get "#{path}/changes" do
- merge_request = find_project_merge_request(params[:merge_request_id])
- authorize! :read_merge_request, merge_request
+ merge_request = find_merge_request_with_access(params[:merge_request_id])
+
present merge_request, with: Entities::MergeRequestChanges, current_user: current_user
end
@@ -155,8 +155,7 @@ module API
:remove_source_branch
end
put path do
- merge_request = find_project_merge_request(params.delete(:merge_request_id))
- authorize! :update_merge_request, merge_request
+ merge_request = find_merge_request_with_access(params.delete(:merge_request_id), :update_merge_request)
mr_params = declared_params(include_missing: false)
mr_params[:force_remove_source_branch] = mr_params.delete(:remove_source_branch) if mr_params[:remove_source_branch].present?
@@ -235,10 +234,7 @@ module API
use :pagination
end
get "#{path}/comments" do
- merge_request = find_project_merge_request(params[:merge_request_id])
-
- authorize! :read_merge_request, merge_request
-
+ merge_request = find_merge_request_with_access(params[:merge_request_id])
present paginate(merge_request.notes.fresh), with: Entities::MRNote
end
@@ -250,8 +246,7 @@ module API
requires :note, type: String, desc: 'The text of the comment'
end
post "#{path}/comments" do
- merge_request = find_project_merge_request(params[:merge_request_id])
- authorize! :create_note, merge_request
+ merge_request = find_merge_request_with_access(params[:merge_request_id], :create_note)
opts = {
note: params[:note],
@@ -275,7 +270,7 @@ module API
use :pagination
end
get "#{path}/closes_issues" do
- merge_request = find_project_merge_request(params[:merge_request_id])
+ merge_request = find_merge_request_with_access(params[:merge_request_id])
issues = ::Kaminari.paginate_array(merge_request.closes_issues(current_user))
present paginate(issues), with: issue_entity(user_project), current_user: current_user
end
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index 284e4cf549a..4d2a8f48267 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -70,21 +70,27 @@ module API
end
post ":id/#{noteables_str}/:noteable_id/notes" do
opts = {
- note: params[:body],
- noteable_type: noteables_str.classify,
- noteable_id: params[:noteable_id]
+ note: params[:body],
+ noteable_type: noteables_str.classify,
+ noteable_id: params[:noteable_id]
}
- if params[:created_at] && (current_user.is_admin? || user_project.owner == current_user)
- opts[:created_at] = params[:created_at]
- end
+ noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id])
+
+ if can?(current_user, noteable_read_ability_name(noteable), noteable)
+ if params[:created_at] && (current_user.is_admin? || user_project.owner == current_user)
+ opts[:created_at] = params[:created_at]
+ end
- note = ::Notes::CreateService.new(user_project, current_user, opts).execute
+ note = ::Notes::CreateService.new(user_project, current_user, opts).execute
- if note.valid?
- present note, with: Entities::const_get(note.class.name)
+ if note.valid?
+ present note, with: Entities::const_get(note.class.name)
+ else
+ not_found!("Note #{note.errors.messages}")
+ end
else
- not_found!("Note #{note.errors.messages}")
+ not_found!("Note")
end
end
diff --git a/lib/api/subscriptions.rb b/lib/api/subscriptions.rb
index 10749b34004..e11d7537cc9 100644
--- a/lib/api/subscriptions.rb
+++ b/lib/api/subscriptions.rb
@@ -3,8 +3,8 @@ module API
before { authenticate! }
subscribable_types = {
- 'merge_request' => proc { |id| user_project.merge_requests.find(id) },
- 'merge_requests' => proc { |id| user_project.merge_requests.find(id) },
+ 'merge_request' => proc { |id| find_merge_request_with_access(id, :update_merge_request) },
+ 'merge_requests' => proc { |id| find_merge_request_with_access(id, :update_merge_request) },
'issues' => proc { |id| find_project_issue(id) },
'labels' => proc { |id| find_project_label(id) },
}
diff --git a/lib/api/todos.rb b/lib/api/todos.rb
index ed8f48aa1e3..9bd077263a7 100644
--- a/lib/api/todos.rb
+++ b/lib/api/todos.rb
@@ -5,7 +5,7 @@ module API
before { authenticate! }
ISSUABLE_TYPES = {
- 'merge_requests' => ->(id) { user_project.merge_requests.find(id) },
+ 'merge_requests' => ->(id) { find_merge_request_with_access(id) },
'issues' => ->(id) { find_project_issue(id) }
}
diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb
index ab7af1cad21..6640168bfa2 100644
--- a/lib/banzai/filter/reference_filter.rb
+++ b/lib/banzai/filter/reference_filter.rb
@@ -53,6 +53,10 @@ module Banzai
context[:project]
end
+ def skip_project_check?
+ context[:skip_project_check]
+ end
+
def reference_class(type)
"gfm gfm-#{type} has-tooltip"
end
diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb
index f842b1fb779..1aa9355b256 100644
--- a/lib/banzai/filter/user_reference_filter.rb
+++ b/lib/banzai/filter/user_reference_filter.rb
@@ -24,7 +24,7 @@ module Banzai
end
def call
- return doc if project.nil?
+ return doc if project.nil? && !skip_project_check?
ref_pattern = User.reference_pattern
ref_pattern_start = /\A#{ref_pattern}\z/
@@ -58,7 +58,7 @@ module Banzai
# have `gfm` and `gfm-project_member` class names attached for styling.
def user_link_filter(text, link_content: nil)
self.class.references_in(text) do |match, username|
- if username == 'all'
+ if username == 'all' && !skip_project_check?
link_to_all(link_content: link_content)
elsif namespace = namespaces[username]
link_to_namespace(namespace, link_content: link_content) || match
diff --git a/lib/banzai/renderer.rb b/lib/banzai/renderer.rb
index f31fb6c3f71..74663556cbb 100644
--- a/lib/banzai/renderer.rb
+++ b/lib/banzai/renderer.rb
@@ -52,9 +52,9 @@ module Banzai
end
# Same as +render_field+, but without consulting or updating the cache field
- def cacheless_render_field(object, field)
+ def cacheless_render_field(object, field, options = {})
text = object.__send__(field)
- context = object.banzai_render_context(field)
+ context = object.banzai_render_context(field).merge(options)
cacheless_render(text, context)
end
diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb
index c4bdef781f7..8b939663ffd 100644
--- a/lib/ci/api/builds.rb
+++ b/lib/ci/api/builds.rb
@@ -18,24 +18,31 @@ module Ci
if current_runner.is_runner_queue_value_latest?(params[:last_update])
header 'X-GitLab-Last-Update', params[:last_update]
+ Gitlab::Metrics.add_event(:build_not_found_cached)
return build_not_found!
end
new_update = current_runner.ensure_runner_queue_value
- build = Ci::RegisterBuildService.new.execute(current_runner)
+ result = Ci::RegisterBuildService.new(current_runner).execute
- if build
- Gitlab::Metrics.add_event(:build_found,
- project: build.project.path_with_namespace)
+ if result.valid?
+ if result.build
+ Gitlab::Metrics.add_event(:build_found,
+ project: result.build.project.path_with_namespace)
- present build, with: Entities::BuildDetails
- else
- Gitlab::Metrics.add_event(:build_not_found)
+ present result.build, with: Entities::BuildDetails
+ else
+ Gitlab::Metrics.add_event(:build_not_found)
- header 'X-GitLab-Last-Update', new_update
+ header 'X-GitLab-Last-Update', new_update
- build_not_found!
+ build_not_found!
+ end
+ else
+ # We received build that is invalid due to concurrency conflict
+ Gitlab::Metrics.add_event(:build_invalid)
+ conflict!
end
end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 2ff27e46d64..4ebd48a3fc7 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -9,7 +9,9 @@ module Gitlab
end
def ensure_application_settings!
- if connect_to_db?
+ return fake_application_settings unless connect_to_db?
+
+ unless ENV['IN_MEMORY_APPLICATION_SETTINGS'] == 'true'
begin
settings = ::ApplicationSetting.current
# In case Redis isn't running or the Redis UNIX socket file is not available
@@ -20,43 +22,23 @@ module Gitlab
settings ||= ::ApplicationSetting.create_from_defaults unless ActiveRecord::Migrator.needs_migration?
end
- settings || fake_application_settings
+ settings || in_memory_application_settings
end
def sidekiq_throttling_enabled?
current_application_settings.sidekiq_throttling_enabled?
end
+ def in_memory_application_settings
+ @in_memory_application_settings ||= ::ApplicationSetting.new(::ApplicationSetting::DEFAULTS)
+ # In case migrations the application_settings table is not created yet,
+ # we fallback to a simple OpenStruct
+ rescue ActiveRecord::StatementInvalid, ActiveRecord::UnknownAttributeError
+ fake_application_settings
+ end
+
def fake_application_settings
- OpenStruct.new(
- default_projects_limit: Settings.gitlab['default_projects_limit'],
- default_branch_protection: Settings.gitlab['default_branch_protection'],
- signup_enabled: Settings.gitlab['signup_enabled'],
- signin_enabled: Settings.gitlab['signin_enabled'],
- gravatar_enabled: Settings.gravatar['enabled'],
- koding_enabled: false,
- plantuml_enabled: false,
- sign_in_text: nil,
- after_sign_up_text: nil,
- help_page_text: nil,
- shared_runners_text: nil,
- restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
- max_attachment_size: Settings.gitlab['max_attachment_size'],
- session_expire_delay: Settings.gitlab['session_expire_delay'],
- default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
- default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
- domain_whitelist: Settings.gitlab['domain_whitelist'],
- import_sources: %w[gitea github bitbucket gitlab google_code fogbugz git gitlab_project],
- shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
- max_artifacts_size: Settings.artifacts['max_size'],
- require_two_factor_authentication: false,
- two_factor_grace_period: 48,
- akismet_enabled: false,
- repository_checks_enabled: true,
- container_registry_token_expire_delay: 5,
- user_default_external: false,
- sidekiq_throttling_enabled: false,
- )
+ OpenStruct.new(::ApplicationSetting::DEFAULTS)
end
private
diff --git a/lib/gitlab/email/handler.rb b/lib/gitlab/email/handler.rb
index bd3267e2a80..bd2f5d3615e 100644
--- a/lib/gitlab/email/handler.rb
+++ b/lib/gitlab/email/handler.rb
@@ -1,10 +1,11 @@
require 'gitlab/email/handler/create_note_handler'
require 'gitlab/email/handler/create_issue_handler'
+require 'gitlab/email/handler/unsubscribe_handler'
module Gitlab
module Email
module Handler
- HANDLERS = [CreateNoteHandler, CreateIssueHandler]
+ HANDLERS = [UnsubscribeHandler, CreateNoteHandler, CreateIssueHandler]
def self.for(mail, mail_key)
HANDLERS.find do |klass|
diff --git a/lib/gitlab/email/handler/base_handler.rb b/lib/gitlab/email/handler/base_handler.rb
index 7cccf465334..3f6ace0311a 100644
--- a/lib/gitlab/email/handler/base_handler.rb
+++ b/lib/gitlab/email/handler/base_handler.rb
@@ -9,52 +9,13 @@ module Gitlab
@mail_key = mail_key
end
- def message
- @message ||= process_message
- end
-
- def author
+ def can_execute?
raise NotImplementedError
end
- def project
+ def execute
raise NotImplementedError
end
-
- private
-
- def validate_permission!(permission)
- raise UserNotFoundError unless author
- raise UserBlockedError if author.blocked?
- raise ProjectNotFound unless author.can?(:read_project, project)
- raise UserNotAuthorizedError unless author.can?(permission, project)
- end
-
- def process_message
- message = ReplyParser.new(mail).execute.strip
- add_attachments(message)
- end
-
- def add_attachments(reply)
- attachments = Email::AttachmentUploader.new(mail).execute(project)
-
- reply + attachments.map do |link|
- "\n\n#{link[:markdown]}"
- end.join
- end
-
- def verify_record!(record:, invalid_exception:, record_name:)
- return if record.persisted?
- return if record.errors.key?(:commands_only)
-
- error_title = "The #{record_name} could not be created for the following reasons:"
-
- msg = error_title + record.errors.full_messages.map do |error|
- "\n\n- #{error}"
- end.join
-
- raise invalid_exception, msg
- end
end
end
end
diff --git a/lib/gitlab/email/handler/create_issue_handler.rb b/lib/gitlab/email/handler/create_issue_handler.rb
index 9f90a3ec2b2..127fae159d5 100644
--- a/lib/gitlab/email/handler/create_issue_handler.rb
+++ b/lib/gitlab/email/handler/create_issue_handler.rb
@@ -5,6 +5,7 @@ module Gitlab
module Email
module Handler
class CreateIssueHandler < BaseHandler
+ include ReplyProcessing
attr_reader :project_path, :incoming_email_token
def initialize(mail, mail_key)
diff --git a/lib/gitlab/email/handler/create_note_handler.rb b/lib/gitlab/email/handler/create_note_handler.rb
index 447c7a6a6b9..d87ba427f4b 100644
--- a/lib/gitlab/email/handler/create_note_handler.rb
+++ b/lib/gitlab/email/handler/create_note_handler.rb
@@ -1,10 +1,13 @@
require 'gitlab/email/handler/base_handler'
+require 'gitlab/email/handler/reply_processing'
module Gitlab
module Email
module Handler
class CreateNoteHandler < BaseHandler
+ include ReplyProcessing
+
def can_handle?
mail_key =~ /\A\w+\z/
end
@@ -24,6 +27,8 @@ module Gitlab
record_name: 'comment')
end
+ private
+
def author
sent_notification.recipient
end
@@ -36,8 +41,6 @@ module Gitlab
@sent_notification ||= SentNotification.for(mail_key)
end
- private
-
def create_note
Notes::CreateService.new(
project,
diff --git a/lib/gitlab/email/handler/reply_processing.rb b/lib/gitlab/email/handler/reply_processing.rb
new file mode 100644
index 00000000000..32c5caf93e8
--- /dev/null
+++ b/lib/gitlab/email/handler/reply_processing.rb
@@ -0,0 +1,54 @@
+module Gitlab
+ module Email
+ module Handler
+ module ReplyProcessing
+ private
+
+ def author
+ raise NotImplementedError
+ end
+
+ def project
+ raise NotImplementedError
+ end
+
+ def message
+ @message ||= process_message
+ end
+
+ def process_message
+ message = ReplyParser.new(mail).execute.strip
+ add_attachments(message)
+ end
+
+ def add_attachments(reply)
+ attachments = Email::AttachmentUploader.new(mail).execute(project)
+
+ reply + attachments.map do |link|
+ "\n\n#{link[:markdown]}"
+ end.join
+ end
+
+ def validate_permission!(permission)
+ raise UserNotFoundError unless author
+ raise UserBlockedError if author.blocked?
+ raise ProjectNotFound unless author.can?(:read_project, project)
+ raise UserNotAuthorizedError unless author.can?(permission, project)
+ end
+
+ def verify_record!(record:, invalid_exception:, record_name:)
+ return if record.persisted?
+ return if record.errors.key?(:commands_only)
+
+ error_title = "The #{record_name} could not be created for the following reasons:"
+
+ msg = error_title + record.errors.full_messages.map do |error|
+ "\n\n- #{error}"
+ end.join
+
+ raise invalid_exception, msg
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/handler/unsubscribe_handler.rb b/lib/gitlab/email/handler/unsubscribe_handler.rb
new file mode 100644
index 00000000000..97d7a8d65ff
--- /dev/null
+++ b/lib/gitlab/email/handler/unsubscribe_handler.rb
@@ -0,0 +1,32 @@
+require 'gitlab/email/handler/base_handler'
+
+module Gitlab
+ module Email
+ module Handler
+ class UnsubscribeHandler < BaseHandler
+ def can_handle?
+ mail_key =~ /\A\w+#{Regexp.escape(Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX)}\z/
+ end
+
+ def execute
+ raise SentNotificationNotFoundError unless sent_notification
+ return unless sent_notification.unsubscribable?
+
+ noteable = sent_notification.noteable
+ raise NoteableNotFoundError unless noteable
+ noteable.unsubscribe(sent_notification.recipient)
+ end
+
+ private
+
+ def sent_notification
+ @sent_notification ||= SentNotification.for(reply_key)
+ end
+
+ def reply_key
+ mail_key.sub(Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX, '')
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/project_creator.rb b/lib/gitlab/github_import/project_creator.rb
index 3f635be22ba..a55adc9b1c8 100644
--- a/lib/gitlab/github_import/project_creator.rb
+++ b/lib/gitlab/github_import/project_creator.rb
@@ -1,6 +1,8 @@
module Gitlab
module GithubImport
class ProjectCreator
+ include Gitlab::CurrentSettings
+
attr_reader :repo, :name, :namespace, :current_user, :session_data, :type
def initialize(repo, name, namespace, current_user, session_data, type: 'github')
@@ -34,7 +36,7 @@ module Gitlab
end
def visibility_level
- repo.private ? Gitlab::VisibilityLevel::PRIVATE : ApplicationSetting.current.default_project_visibility
+ repo.private ? Gitlab::VisibilityLevel::PRIVATE : current_application_settings.default_project_visibility
end
#
diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb
index 45958710c13..52276cbcd9a 100644
--- a/lib/gitlab/import_sources.rb
+++ b/lib/gitlab/import_sources.rb
@@ -5,8 +5,6 @@
#
module Gitlab
module ImportSources
- extend CurrentSettings
-
ImportSource = Struct.new(:name, :title, :importer)
ImportTable = [
diff --git a/lib/gitlab/incoming_email.rb b/lib/gitlab/incoming_email.rb
index 801dfde9a36..b91012d6405 100644
--- a/lib/gitlab/incoming_email.rb
+++ b/lib/gitlab/incoming_email.rb
@@ -1,5 +1,6 @@
module Gitlab
module IncomingEmail
+ UNSUBSCRIBE_SUFFIX = '+unsubscribe'.freeze
WILDCARD_PLACEHOLDER = '%{key}'.freeze
class << self
@@ -18,7 +19,11 @@ module Gitlab
end
def reply_address(key)
- config.address.gsub(WILDCARD_PLACEHOLDER, key)
+ config.address.sub(WILDCARD_PLACEHOLDER, key)
+ end
+
+ def unsubscribe_address(key)
+ config.address.sub(WILDCARD_PLACEHOLDER, "#{key}#{UNSUBSCRIBE_SUFFIX}")
end
def key_from_address(address)
@@ -49,7 +54,7 @@ module Gitlab
return nil unless wildcard_address
regex = Regexp.escape(wildcard_address)
- regex = regex.gsub(Regexp.escape('%{key}'), "(.+)")
+ regex = regex.sub(Regexp.escape(WILDCARD_PLACEHOLDER), '(.+)')
Regexp.new(regex).freeze
end
end
diff --git a/lib/gitlab/job_waiter.rb b/lib/gitlab/job_waiter.rb
new file mode 100644
index 00000000000..8db91d25a4b
--- /dev/null
+++ b/lib/gitlab/job_waiter.rb
@@ -0,0 +1,27 @@
+module Gitlab
+ # JobWaiter can be used to wait for a number of Sidekiq jobs to complete.
+ class JobWaiter
+ # The sleep interval between checking keys, in seconds.
+ INTERVAL = 0.1
+
+ # jobs - The job IDs to wait for.
+ def initialize(jobs)
+ @jobs = jobs
+ end
+
+ # Waits for all the jobs to be completed.
+ #
+ # timeout - The maximum amount of seconds to block the caller for. This
+ # ensures we don't indefinitely block a caller in case a job takes
+ # long to process, or is never processed.
+ def wait(timeout = 60)
+ start = Time.current
+
+ while (Time.current - start) <= timeout
+ break if SidekiqStatus.all_completed?(@jobs)
+
+ sleep(INTERVAL) # to not overload Redis too much.
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb
index 6bdf3db9cb8..db325c00705 100644
--- a/lib/gitlab/project_search_results.rb
+++ b/lib/gitlab/project_search_results.rb
@@ -71,6 +71,14 @@ module Gitlab
)
end
+ def single_commit_result?
+ commits_count == 1 && total_result_count == 1
+ end
+
+ def total_result_count
+ issues_count + merge_requests_count + milestones_count + notes_count + blobs_count + wiki_blobs_count + commits_count
+ end
+
private
def blobs
@@ -114,7 +122,25 @@ module Gitlab
end
def commits
- @commits ||= project.repository.find_commits_by_message(query)
+ @commits ||= find_commits(query)
+ end
+
+ def find_commits(query)
+ return [] unless Ability.allowed?(@current_user, :download_code, @project)
+
+ commits = find_commits_by_message(query)
+ commit_by_sha = find_commit_by_sha(query)
+ commits |= [commit_by_sha] if commit_by_sha
+ commits
+ end
+
+ def find_commits_by_message(query)
+ project.repository.find_commits_by_message(query)
+ end
+
+ def find_commit_by_sha(query)
+ key = query.strip
+ project.repository.commit(key) if Commit.valid_hash?(key)
end
def project_ids_relation
diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb
index 35212992698..c9c65f76f4b 100644
--- a/lib/gitlab/search_results.rb
+++ b/lib/gitlab/search_results.rb
@@ -43,6 +43,10 @@ module Gitlab
@milestones_count ||= milestones.count
end
+ def single_commit_result?
+ false
+ end
+
private
def projects
diff --git a/lib/gitlab/sidekiq_status.rb b/lib/gitlab/sidekiq_status.rb
new file mode 100644
index 00000000000..aadc401ff8d
--- /dev/null
+++ b/lib/gitlab/sidekiq_status.rb
@@ -0,0 +1,66 @@
+module Gitlab
+ # The SidekiqStatus module and its child classes can be used for checking if a
+ # Sidekiq job has been processed or not.
+ #
+ # To check if a job has been completed, simply pass the job ID to the
+ # `completed?` method:
+ #
+ # job_id = SomeWorker.perform_async(...)
+ #
+ # if Gitlab::SidekiqStatus.completed?(job_id)
+ # ...
+ # end
+ #
+ # For each job ID registered a separate key is stored in Redis, making lookups
+ # much faster than using Sidekiq's built-in job finding/status API. These keys
+ # expire after a certain period of time to prevent storing too many keys in
+ # Redis.
+ module SidekiqStatus
+ STATUS_KEY = 'gitlab-sidekiq-status:%s'.freeze
+
+ # The default time (in seconds) after which a status key is expired
+ # automatically. The default of 30 minutes should be more than sufficient
+ # for most jobs.
+ DEFAULT_EXPIRATION = 30.minutes.to_i
+
+ # Starts tracking of the given job.
+ #
+ # jid - The Sidekiq job ID
+ # expire - The expiration time of the Redis key.
+ def self.set(jid, expire = DEFAULT_EXPIRATION)
+ Sidekiq.redis do |redis|
+ redis.set(key_for(jid), 1, ex: expire)
+ end
+ end
+
+ # Stops the tracking of the given job.
+ #
+ # jid - The Sidekiq job ID to remove.
+ def self.unset(jid)
+ Sidekiq.redis do |redis|
+ redis.del(key_for(jid))
+ end
+ end
+
+ # Returns true if all the given job have been completed.
+ #
+ # jids - The Sidekiq job IDs to check.
+ #
+ # Returns true or false.
+ def self.all_completed?(jids)
+ keys = jids.map { |jid| key_for(jid) }
+
+ responses = Sidekiq.redis do |redis|
+ redis.pipelined do
+ keys.each { |key| redis.exists(key) }
+ end
+ end
+
+ responses.all? { |value| !value }
+ end
+
+ def self.key_for(jid)
+ STATUS_KEY % jid
+ end
+ end
+end
diff --git a/lib/gitlab/sidekiq_status/client_middleware.rb b/lib/gitlab/sidekiq_status/client_middleware.rb
new file mode 100644
index 00000000000..779a9998b22
--- /dev/null
+++ b/lib/gitlab/sidekiq_status/client_middleware.rb
@@ -0,0 +1,10 @@
+module Gitlab
+ module SidekiqStatus
+ class ClientMiddleware
+ def call(_, job, _, _)
+ SidekiqStatus.set(job['jid'])
+ yield
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/sidekiq_status/server_middleware.rb b/lib/gitlab/sidekiq_status/server_middleware.rb
new file mode 100644
index 00000000000..31dfa46ff9d
--- /dev/null
+++ b/lib/gitlab/sidekiq_status/server_middleware.rb
@@ -0,0 +1,13 @@
+module Gitlab
+ module SidekiqStatus
+ class ServerMiddleware
+ def call(worker, job, queue)
+ ret = yield
+
+ SidekiqStatus.unset(job['jid'])
+
+ ret
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/user_access.rb b/lib/gitlab/user_access.rb
index 6c7e673fb9f..6ce9b229294 100644
--- a/lib/gitlab/user_access.rb
+++ b/lib/gitlab/user_access.rb
@@ -35,7 +35,9 @@ module Gitlab
return true if project.empty_repo? && project.user_can_push_to_empty_repo?(user)
access_levels = project.protected_branches.matching(ref).map(&:push_access_levels).flatten
- access_levels.any? { |access_level| access_level.check_access(user) }
+ has_access = access_levels.any? { |access_level| access_level.check_access(user) }
+
+ has_access || !project.repository.branch_exists?(ref) && can_merge_to_branch?(ref)
else
user.can?(:push_code, project)
end
diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb
index 9462f3368e6..c7953af29dd 100644
--- a/lib/gitlab/visibility_level.rb
+++ b/lib/gitlab/visibility_level.rb
@@ -11,6 +11,7 @@ module Gitlab
included do
scope :public_only, -> { where(visibility_level: PUBLIC) }
scope :public_and_internal_only, -> { where(visibility_level: [PUBLIC, INTERNAL] ) }
+ scope :non_public_only, -> { where.not(visibility_level: PUBLIC) }
scope :public_to_user, -> (user) { user && !user.external ? public_and_internal_only : public_only }
end
diff --git a/lib/mattermost/client.rb b/lib/mattermost/client.rb
index ec2903b7ec6..e55c0d6ac49 100644
--- a/lib/mattermost/client.rb
+++ b/lib/mattermost/client.rb
@@ -8,21 +8,31 @@ module Mattermost
@user = user
end
- private
-
def with_session(&blk)
Mattermost::Session.new(user).with_session(&blk)
end
- def json_get(path, options = {})
+ private
+
+ # Should be used in a session manually
+ def get(session, path, options = {})
+ json_response session.get(path, options)
+ end
+
+ # Should be used in a session manually
+ def post(session, path, options = {})
+ json_response session.post(path, options)
+ end
+
+ def session_get(path, options = {})
with_session do |session|
- json_response session.get(path, options)
+ get(session, path, options)
end
end
- def json_post(path, options = {})
+ def session_post(path, options = {})
with_session do |session|
- json_response session.post(path, options)
+ post(session, path, options)
end
end
diff --git a/lib/mattermost/command.rb b/lib/mattermost/command.rb
index d1e4bb0eccf..33e450d7f0a 100644
--- a/lib/mattermost/command.rb
+++ b/lib/mattermost/command.rb
@@ -1,7 +1,7 @@
module Mattermost
class Command < Client
def create(params)
- response = json_post("/api/v3/teams/#{params[:team_id]}/commands/create",
+ response = session_post("/api/v3/teams/#{params[:team_id]}/commands/create",
body: params.to_json)
response['token']
diff --git a/lib/mattermost/team.rb b/lib/mattermost/team.rb
index 784eca6ab5a..09dfd082b3a 100644
--- a/lib/mattermost/team.rb
+++ b/lib/mattermost/team.rb
@@ -1,7 +1,7 @@
module Mattermost
class Team < Client
def all
- json_get('/api/v3/teams/all')
+ session_get('/api/v3/teams/all')
end
end
end
diff --git a/lib/tasks/gitlab/info.rake b/lib/tasks/gitlab/info.rake
index dffea8ed155..f7c831892ee 100644
--- a/lib/tasks/gitlab/info.rake
+++ b/lib/tasks/gitlab/info.rake
@@ -11,8 +11,10 @@ namespace :gitlab do
gem_version = run_command(%W(gem --version))
# check Bundler version
bunder_version = run_and_match(%W(bundle --version), /[\d\.]+/).try(:to_s)
- # check Bundler version
+ # check Rake version
rake_version = run_and_match(%W(rake --version), /[\d\.]+/).try(:to_s)
+ # check redis version
+ redis_version = run_and_match(%W(redis-cli --version), /redis-cli (\d+\.\d+\.\d+)/).to_a
puts ""
puts "System information".color(:yellow)
@@ -24,6 +26,7 @@ namespace :gitlab do
puts "Gem Version:\t#{gem_version || "unknown".color(:red)}"
puts "Bundler Version:#{bunder_version || "unknown".color(:red)}"
puts "Rake Version:\t#{rake_version || "unknown".color(:red)}"
+ puts "Redis Version:\t#{redis_version[1] || "unknown".color(:red)}"
puts "Sidekiq Version:#{Sidekiq::VERSION}"