diff options
Diffstat (limited to 'lib/gitlab')
23 files changed, 382 insertions, 97 deletions
diff --git a/lib/gitlab/authorized_keys.rb b/lib/gitlab/authorized_keys.rb new file mode 100644 index 00000000000..609d2bd9c77 --- /dev/null +++ b/lib/gitlab/authorized_keys.rb @@ -0,0 +1,145 @@ +# frozen_string_literal: true + +module Gitlab + class AuthorizedKeys + KeyError = Class.new(StandardError) + + attr_reader :logger + + # Initializes the class + # + # @param [Gitlab::Logger] logger + def initialize(logger = Gitlab::AppLogger) + @logger = logger + end + + # Add id and its key to the authorized_keys file + # + # @param [String] id identifier of key prefixed by `key-` + # @param [String] key public key to be added + # @return [Boolean] + def add_key(id, key) + lock do + public_key = strip(key) + logger.info("Adding key (#{id}): #{public_key}") + open_authorized_keys_file('a') { |file| file.puts(key_line(id, public_key)) } + end + + true + end + + # Atomically add all the keys to the authorized_keys file + # + # @param [Array<::Key>] keys list of Key objects to be added + # @return [Boolean] + def batch_add_keys(keys) + lock(300) do # Allow 300 seconds (5 minutes) for batch_add_keys + open_authorized_keys_file('a') do |file| + keys.each do |key| + public_key = strip(key.key) + logger.info("Adding key (#{key.shell_id}): #{public_key}") + file.puts(key_line(key.shell_id, public_key)) + end + end + end + + true + rescue Gitlab::AuthorizedKeys::KeyError + false + end + + # Remove key by ID from the authorized_keys file + # + # @param [String] id identifier of the key to be removed prefixed by `key-` + # @return [Boolean] + def rm_key(id) + lock do + logger.info("Removing key (#{id})") + open_authorized_keys_file('r+') do |f| + while line = f.gets + next unless line.start_with?("command=\"#{command(id)}\"") + + f.seek(-line.length, IO::SEEK_CUR) + # Overwrite the line with #'s. Because the 'line' variable contains + # a terminating '\n', we write line.length - 1 '#' characters. + f.write('#' * (line.length - 1)) + end + end + end + + true + end + + # Clear the authorized_keys file + # + # @return [Boolean] + def clear + open_authorized_keys_file('w') { |file| file.puts '# Managed by gitlab-rails' } + + true + end + + # Read the authorized_keys file and return IDs of each key + # + # @return [Array<Integer>] + def list_key_ids + logger.info('Listing all key IDs') + + [].tap do |a| + open_authorized_keys_file('r') do |f| + f.each_line do |line| + key_id = line.match(/key-(\d+)/) + + next unless key_id + + a << key_id[1].chomp.to_i + end + end + end + end + + private + + def lock(timeout = 10) + File.open("#{authorized_keys_file}.lock", "w+") do |f| + f.flock File::LOCK_EX + Timeout.timeout(timeout) { yield } + ensure + f.flock File::LOCK_UN + end + end + + def open_authorized_keys_file(mode) + File.open(authorized_keys_file, mode, 0o600) do |file| + file.chmod(0o600) + yield file + end + end + + def key_line(id, key) + key = key.chomp + + if key.include?("\n") || key.include?("\t") + raise KeyError, "Invalid public_key: #{key.inspect}" + end + + %Q(command="#{command(id)}",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty #{strip(key)}) + end + + def command(id) + unless /\A[a-z0-9-]+\z/ =~ id + raise KeyError, "Invalid ID: #{id.inspect}" + end + + "#{File.join(Gitlab.config.gitlab_shell.path, 'bin', 'gitlab-shell')} #{id}" + end + + def strip(key) + key.split(/[ ]+/)[0, 2].join(' ') + end + + def authorized_keys_file + Gitlab.config.gitlab_shell.authorized_keys_file + end + end +end diff --git a/lib/gitlab/ci/model.rb b/lib/gitlab/ci/model.rb index fbdb84c0522..1625cb841b6 100644 --- a/lib/gitlab/ci/model.rb +++ b/lib/gitlab/ci/model.rb @@ -8,7 +8,7 @@ module Gitlab end def model_name - @model_name ||= ActiveModel::Name.new(self, nil, self.name.split("::").last) + @model_name ||= ActiveModel::Name.new(self, nil, self.name.demodulize) end end end diff --git a/lib/gitlab/ci/pipeline/chain/command.rb b/lib/gitlab/ci/pipeline/chain/command.rb index 7b77e86feae..bf9f03f6134 100644 --- a/lib/gitlab/ci/pipeline/chain/command.rb +++ b/lib/gitlab/ci/pipeline/chain/command.rb @@ -11,7 +11,7 @@ module Gitlab :trigger_request, :schedule, :merge_request, :ignore_skip_ci, :save_incompleted, :seeds_block, :variables_attributes, :push_options, - :chat_data + :chat_data, :allow_mirror_update ) do include Gitlab::Utils::StrongMemoize diff --git a/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml index 4e708f229cd..ef6d7866e85 100644 --- a/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml @@ -21,20 +21,19 @@ dast: allow_failure: true services: - docker:stable-dind - before_script: + script: - export DAST_VERSION=${SP_VERSION:-$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')} - | function dast_run() { docker run \ - --env DAST_TARGET_AVAILABILITY_TIMEOUT \ - --volume "$PWD:/output" \ - --volume /var/run/docker.sock:/var/run/docker.sock \ - -w /output \ - "registry.gitlab.com/gitlab-org/security-products/dast:$DAST_VERSION" \ - /analyze -t $DAST_WEBSITE \ - "$@" + --env DAST_TARGET_AVAILABILITY_TIMEOUT \ + --volume "$PWD:/output" \ + --volume /var/run/docker.sock:/var/run/docker.sock \ + -w /output \ + "registry.gitlab.com/gitlab-org/security-products/dast:$DAST_VERSION" \ + /analyze -t $DAST_WEBSITE \ + "$@" } - script: - | if [ -n "$DAST_AUTH_URL" ] then diff --git a/lib/gitlab/diff/suggestion_diff.rb b/lib/gitlab/diff/suggestion_diff.rb new file mode 100644 index 00000000000..ee153c226b7 --- /dev/null +++ b/lib/gitlab/diff/suggestion_diff.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Gitlab + module Diff + class SuggestionDiff + include Gitlab::Utils::StrongMemoize + + delegate :from_content, :to_content, :from_line, to: :@suggestible + + def initialize(suggestible) + @suggestible = suggestible + end + + def diff_lines + Gitlab::Diff::Parser.new.parse(raw_diff.each_line).to_a + end + + private + + def raw_diff + "#{diff_header}\n#{from_content_as_diff}#{to_content_as_diff}" + end + + def diff_header + "@@ -#{from_line} +#{from_line}" + end + + def from_content_as_diff + from_content.lines.map { |line| line.prepend('-') }.join + end + + def to_content_as_diff + to_content.lines.map { |line| line.prepend('+') }.join + end + end + end +end diff --git a/lib/gitlab/fake_application_settings.rb b/lib/gitlab/fake_application_settings.rb index bd806269bf0..77f7d9490f3 100644 --- a/lib/gitlab/fake_application_settings.rb +++ b/lib/gitlab/fake_application_settings.rb @@ -7,6 +7,8 @@ # column type without parsing db/schema.rb. module Gitlab class FakeApplicationSettings < OpenStruct + include ApplicationSettingImplementation + # Mimic ActiveRecord predicate methods for boolean values def self.define_predicate_methods(options) options.each do |key, value| @@ -26,20 +28,7 @@ module Gitlab FakeApplicationSettings.define_predicate_methods(options) end - def key_restriction_for(type) - 0 - end - - def allowed_key_types - ApplicationSetting::SUPPORTED_KEY_TYPES - end - - def pick_repository_storage - repository_storages.sample - end - - def commit_email_hostname - super.presence || ApplicationSetting.default_commit_email_hostname - end + alias_method :read_attribute, :[] + alias_method :has_attribute?, :[] end end diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb index 1ae2f9dfd93..6e31064f737 100644 --- a/lib/gitlab/favicon.rb +++ b/lib/gitlab/favicon.rb @@ -10,7 +10,7 @@ module Gitlab elsif Gitlab::Utils.to_boolean(ENV['CANARY']) 'favicon-yellow.png' elsif Rails.env.development? - 'favicon-blue.png' + development_favicon else 'favicon.png' end @@ -18,6 +18,12 @@ module Gitlab ActionController::Base.helpers.image_path(image_name, host: host) end + def development_favicon + # This is a separate method so that EE can return a different favicon + # for development environments. + 'favicon-blue.png' + end + def status_overlay(status_name) path = File.join( 'ci_favicons', diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb index e5bbd500e98..88ff9fbceb4 100644 --- a/lib/gitlab/git/commit.rb +++ b/lib/gitlab/git/commit.rb @@ -184,11 +184,12 @@ module Gitlab end end - def initialize(repository, raw_commit, head = nil) + def initialize(repository, raw_commit, head = nil, lazy_load_parents: false) raise "Nil as raw commit passed" unless raw_commit @repository = repository @head = head + @lazy_load_parents = lazy_load_parents init_commit(raw_commit) end @@ -225,6 +226,12 @@ module Gitlab author_name != committer_name || author_email != committer_email end + def parent_ids + return @parent_ids unless @lazy_load_parents + + @parent_ids ||= @repository.commit(id).parent_ids + end + def parent_id parent_ids.first end diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 35dd042ba6a..7d6851a4b8d 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -344,12 +344,12 @@ module Gitlab end end - def new_blobs(newrev) + def new_blobs(newrev, dynamic_timeout: nil) return [] if newrev.blank? || newrev == ::Gitlab::Git::BLANK_SHA strong_memoize("new_blobs_#{newrev}") do wrapped_gitaly_errors do - gitaly_ref_client.list_new_blobs(newrev, REV_LIST_COMMIT_LIMIT) + gitaly_ref_client.list_new_blobs(newrev, REV_LIST_COMMIT_LIMIT, dynamic_timeout: dynamic_timeout) end end end diff --git a/lib/gitlab/gitaly_client/ref_service.rb b/lib/gitlab/gitaly_client/ref_service.rb index d5633d167ac..6f6698607d9 100644 --- a/lib/gitlab/gitaly_client/ref_service.rb +++ b/lib/gitlab/gitaly_client/ref_service.rb @@ -84,15 +84,22 @@ module Gitlab commits end - def list_new_blobs(newrev, limit = 0) + def list_new_blobs(newrev, limit = 0, dynamic_timeout: nil) request = Gitaly::ListNewBlobsRequest.new( repository: @gitaly_repo, commit_id: newrev, limit: limit ) + timeout = + if dynamic_timeout + [dynamic_timeout, GitalyClient.medium_timeout].min + else + GitalyClient.medium_timeout + end + response = GitalyClient - .call(@storage, :ref_service, :list_new_blobs, request, timeout: GitalyClient.medium_timeout) + .call(@storage, :ref_service, :list_new_blobs, request, timeout: timeout) response.flat_map do |msg| # Returns an Array of Gitaly::NewBlobObject objects diff --git a/lib/gitlab/github_import/importer/pull_request_importer.rb b/lib/gitlab/github_import/importer/pull_request_importer.rb index e294173f992..72451e5e01e 100644 --- a/lib/gitlab/github_import/importer/pull_request_importer.rb +++ b/lib/gitlab/github_import/importer/pull_request_importer.rb @@ -89,7 +89,7 @@ module Gitlab return if project.repository.branch_exists?(source_branch) - project.repository.add_branch(merge_request.author, source_branch, pull_request.source_branch_sha) + project.repository.add_branch(project.owner, source_branch, pull_request.source_branch_sha) rescue Gitlab::Git::CommandError => e Gitlab::Sentry.track_acceptable_exception(e, extra: { diff --git a/lib/gitlab/group_search_results.rb b/lib/gitlab/group_search_results.rb new file mode 100644 index 00000000000..7255293b194 --- /dev/null +++ b/lib/gitlab/group_search_results.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Gitlab + class GroupSearchResults < SearchResults + def initialize(current_user, limit_projects, group, query, default_project_filter: false, per_page: 20) + super(current_user, limit_projects, query, default_project_filter: default_project_filter, per_page: per_page) + + @group = group + end + + # rubocop:disable CodeReuse/ActiveRecord + def users + # 1: get all groups the current user has access to + groups = GroupsFinder.new(current_user).execute.joins(:users) + + # 2: Get the group's whole hierarchy + group_users = @group.direct_and_indirect_users + + # 3: get all users the current user has access to (-> + # `SearchResults#users`), which also applies the query. + users = super + + # 4: filter for users that belong to the previously selected groups + users + .where(id: group_users.select('id')) + .where(id: groups.select('members.user_id')) + end + # rubocop:enable CodeReuse/ActiveRecord + end +end diff --git a/lib/gitlab/hashed_storage/migrator.rb b/lib/gitlab/hashed_storage/migrator.rb index f5368d41629..1f0deebea39 100644 --- a/lib/gitlab/hashed_storage/migrator.rb +++ b/lib/gitlab/hashed_storage/migrator.rb @@ -97,7 +97,7 @@ module Gitlab def any_non_empty_queue?(*workers) workers.any? do |worker| - worker.jobs.any? + !Sidekiq::Queue.new(worker.queue).size.zero? end end diff --git a/lib/gitlab/hook_data/issue_builder.rb b/lib/gitlab/hook_data/issue_builder.rb index c99353b9d49..d39ff8c21cc 100644 --- a/lib/gitlab/hook_data/issue_builder.rb +++ b/lib/gitlab/hook_data/issue_builder.rb @@ -48,7 +48,7 @@ module Gitlab } issue.attributes.with_indifferent_access.slice(*self.class.safe_hook_attributes) - .merge!(attrs) + .merge!(attrs) end end end diff --git a/lib/gitlab/hook_data/merge_request_builder.rb b/lib/gitlab/hook_data/merge_request_builder.rb index ad38e26e40a..d77b1d04644 100644 --- a/lib/gitlab/hook_data/merge_request_builder.rb +++ b/lib/gitlab/hook_data/merge_request_builder.rb @@ -55,7 +55,7 @@ module Gitlab } merge_request.attributes.with_indifferent_access.slice(*self.class.safe_hook_attributes) - .merge!(attrs) + .merge!(attrs) end end end diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml index fa54fc17d95..a0aab9fcbaf 100644 --- a/lib/gitlab/import_export/import_export.yml +++ b/lib/gitlab/import_export/import_export.yml @@ -117,6 +117,7 @@ excluded_attributes: - :description_html - :repository_languages - :bfg_object_map + - :tag_list namespaces: - :runners_token - :runners_token_encrypted diff --git a/lib/gitlab/json_cache.rb b/lib/gitlab/json_cache.rb index 24daad638f4..e4bc437d787 100644 --- a/lib/gitlab/json_cache.rb +++ b/lib/gitlab/json_cache.rb @@ -80,8 +80,23 @@ module Gitlab # when the new_record? method incorrectly returns false. # # See https://gitlab.com/gitlab-org/gitlab-ee/issues/9903#note_145329964 - attributes = klass.attributes_builder.build_from_database(raw, {}) - klass.allocate.init_with("attributes" => attributes, "new_record" => new_record?(raw, klass)) + klass + .allocate + .init_with( + "attributes" => attributes_for(klass, raw), + "new_record" => new_record?(raw, klass) + ) + end + + def attributes_for(klass, raw) + # We have models that leave out some fields from the JSON export for + # security reasons, e.g. models that include the CacheMarkdownField. + # The ActiveRecord::AttributeSet we build from raw does know about + # these columns so we need manually set them. + missing_attributes = (klass.columns.map(&:name) - raw.keys) + missing_attributes.each { |column| raw[column] = nil } + + klass.attributes_builder.build_from_database(raw, {}) end def new_record?(raw, klass) diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index a68f8801c2a..58f06b6708c 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -22,11 +22,17 @@ module Gitlab paginated_blobs(wiki_blobs, page) when 'commits' Kaminari.paginate_array(commits).page(page).per(per_page) + when 'users' + users.page(page).per(per_page) else super(scope, page, false) end end + def users + super.where(id: @project.team.members) # rubocop:disable CodeReuse/ActiveRecord + end + def blobs_count @blobs_count ||= blobs.count end diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb index 491148ec1a6..8988b9ad7be 100644 --- a/lib/gitlab/search_results.rb +++ b/lib/gitlab/search_results.rb @@ -32,6 +32,8 @@ module Gitlab merge_requests.page(page).per(per_page) when 'milestones' milestones.page(page).per(per_page) + when 'users' + users.page(page).per(per_page) else Kaminari.paginate_array([]).page(page).per(per_page) end @@ -71,6 +73,12 @@ module Gitlab end # rubocop: enable CodeReuse/ActiveRecord + # rubocop:disable CodeReuse/ActiveRecord + def limited_users_count + @limited_users_count ||= users.limit(count_limit).count + end + # rubocop:enable CodeReuse/ActiveRecord + def single_commit_result? false end @@ -79,6 +87,12 @@ module Gitlab 1001 end + def users + return User.none unless Ability.allowed?(current_user, :read_users_list) + + UsersFinder.new(current_user, search: query).execute + end + private def projects diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb index 40b641b8317..93182607616 100644 --- a/lib/gitlab/shell.rb +++ b/lib/gitlab/shell.rb @@ -10,18 +10,6 @@ module Gitlab Error = Class.new(StandardError) - KeyAdder = Struct.new(:io) do - def add_key(id, key) - key = Gitlab::Shell.strip_key(key) - # Newline and tab are part of the 'protocol' used to transmit id+key to the other end - if key.include?("\t") || key.include?("\n") - raise Error.new("Invalid key: #{key.inspect}") - end - - io.puts("#{id}\t#{key}") - end - end - class << self def secret_token @secret_token ||= begin @@ -40,10 +28,6 @@ module Gitlab .join('GITLAB_SHELL_VERSION')).strip end - def strip_key(key) - key.split(/[ ]+/)[0, 2].join(' ') - end - private # Create (if necessary) and link the secret token file @@ -173,7 +157,7 @@ module Gitlab false end - # Add new key to gitlab-shell + # Add new key to authorized_keys # # Ex. # add_key("key-42", "sha-rsa ...") @@ -181,33 +165,53 @@ module Gitlab def add_key(key_id, key_content) return unless self.authorized_keys_enabled? - gitlab_shell_fast_execute([gitlab_shell_keys_path, - 'add-key', key_id, self.class.strip_key(key_content)]) + if shell_out_for_gitlab_keys? + gitlab_shell_fast_execute([ + gitlab_shell_keys_path, + 'add-key', + key_id, + strip_key(key_content) + ]) + else + gitlab_authorized_keys.add_key(key_id, key_content) + end end # Batch-add keys to authorized_keys # # Ex. - # batch_add_keys { |adder| adder.add_key("key-42", "sha-rsa ...") } - def batch_add_keys(&block) + # batch_add_keys(Key.all) + def batch_add_keys(keys) return unless self.authorized_keys_enabled? - IO.popen(%W(#{gitlab_shell_path}/bin/gitlab-keys batch-add-keys), 'w') do |io| - yield(KeyAdder.new(io)) + if shell_out_for_gitlab_keys? + begin + IO.popen("#{gitlab_shell_keys_path} batch-add-keys", 'w') do |io| + add_keys_to_io(keys, io) + end + + $?.success? + rescue Error + false + end + else + gitlab_authorized_keys.batch_add_keys(keys) end end - # Remove ssh key from gitlab shell + # Remove ssh key from authorized_keys # # Ex. - # remove_key("key-342", "sha-rsa ...") + # remove_key("key-342") # - def remove_key(key_id, key_content = nil) + def remove_key(id, _ = nil) return unless self.authorized_keys_enabled? - args = [gitlab_shell_keys_path, 'rm-key', key_id] - args << key_content if key_content - gitlab_shell_fast_execute(args) + if shell_out_for_gitlab_keys? + gitlab_shell_fast_execute([gitlab_shell_keys_path, 'rm-key', id]) + else + gitlab_authorized_keys.rm_key(id) + end end # Remove all ssh keys from gitlab shell @@ -218,7 +222,11 @@ module Gitlab def remove_all_keys return unless self.authorized_keys_enabled? - gitlab_shell_fast_execute([gitlab_shell_keys_path, 'clear']) + if shell_out_for_gitlab_keys? + gitlab_shell_fast_execute([gitlab_shell_keys_path, 'clear']) + else + gitlab_authorized_keys.clear + end end # Remove ssh keys from gitlab shell that are not in the DB @@ -247,33 +255,6 @@ module Gitlab end # rubocop: enable CodeReuse/ActiveRecord - # Iterate over all ssh key IDs from gitlab shell, in batches - # - # Ex. - # batch_read_key_ids { |batch| keys = Key.where(id: batch) } - # - def batch_read_key_ids(batch_size: 100, &block) - return unless self.authorized_keys_enabled? - - list_key_ids do |key_id_stream| - key_id_stream.lazy.each_slice(batch_size) do |lines| - key_ids = lines.map { |l| l.chomp.to_i } - yield(key_ids) - end - end - end - - # Stream all ssh key IDs from gitlab shell, separated by newlines - # - # Ex. - # list_key_ids - # - def list_key_ids(&block) - return unless self.authorized_keys_enabled? - - IO.popen(%W(#{gitlab_shell_path}/bin/gitlab-keys list-key-ids), &block) - end - # Add empty directory for storing repositories # # Ex. @@ -378,6 +359,10 @@ module Gitlab private + def shell_out_for_gitlab_keys? + Gitlab.config.gitlab_shell.authorized_keys_file.blank? + end + def gitlab_shell_fast_execute(cmd) output, status = gitlab_shell_fast_execute_helper(cmd) @@ -415,6 +400,40 @@ module Gitlab raise Error, e end + def gitlab_authorized_keys + @gitlab_authorized_keys ||= Gitlab::AuthorizedKeys.new + end + + def batch_read_key_ids(batch_size: 100, &block) + return unless self.authorized_keys_enabled? + + if shell_out_for_gitlab_keys? + IO.popen("#{gitlab_shell_keys_path} list-key-ids") do |key_id_stream| + key_id_stream.lazy.each_slice(batch_size) do |lines| + yield(lines.map { |l| l.chomp.to_i }) + end + end + else + gitlab_authorized_keys.list_key_ids.lazy.each_slice(batch_size) do |key_ids| + yield(key_ids) + end + end + end + + def strip_key(key) + key.split(/[ ]+/)[0, 2].join(' ') + end + + def add_keys_to_io(keys, io) + keys.each do |k| + key = strip_key(k.key) + + raise Error.new("Invalid key: #{key.inspect}") if key.include?("\t") || key.include?("\n") + + io.puts("#{k.shell_id}\t#{key}") + end + end + class GitalyGitlabProjects attr_reader :shard_name, :repository_relative_path, :output, :gl_project_path diff --git a/lib/gitlab/sidekiq_config.rb b/lib/gitlab/sidekiq_config.rb index 3b8de64913b..fb303e3fb0c 100644 --- a/lib/gitlab/sidekiq_config.rb +++ b/lib/gitlab/sidekiq_config.rb @@ -48,7 +48,9 @@ module Gitlab end def self.workers - @workers ||= find_workers(Rails.root.join('app', 'workers')) + @workers ||= + find_workers(Rails.root.join('app', 'workers')) + + find_workers(Rails.root.join('ee', 'app', 'workers')) end def self.find_workers(root) diff --git a/lib/gitlab/user_extractor.rb b/lib/gitlab/user_extractor.rb index b41d085ee77..f0557f6ad68 100644 --- a/lib/gitlab/user_extractor.rb +++ b/lib/gitlab/user_extractor.rb @@ -11,7 +11,9 @@ module Gitlab USERNAME_REGEXP = User.reference_pattern def initialize(text) - @text = text + # EE passes an Array to `text` in a few places, so we want to support both + # here. + @text = Array(text).join(' ') end def users diff --git a/lib/gitlab/utils.rb b/lib/gitlab/utils.rb index 99fa65e0e90..16ec8a8bb28 100644 --- a/lib/gitlab/utils.rb +++ b/lib/gitlab/utils.rb @@ -104,6 +104,12 @@ module Gitlab nil end + def try_megabytes_to_bytes(size) + Integer(size).megabytes + rescue ArgumentError + size + end + def bytes_to_megabytes(bytes) bytes.to_f / Numeric::MEGABYTE end |