diff options
Diffstat (limited to 'lib/gitlab')
-rw-r--r-- | lib/gitlab/ci/variables/collection.rb | 38 | ||||
-rw-r--r-- | lib/gitlab/ci/variables/collection/item.rb | 50 | ||||
-rw-r--r-- | lib/gitlab/current_settings.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/database.rb | 9 | ||||
-rw-r--r-- | lib/gitlab/database/migration_helpers.rb | 13 | ||||
-rw-r--r-- | lib/gitlab/diff/file.rb | 39 | ||||
-rw-r--r-- | lib/gitlab/diff/file_collection/merge_request_diff.rb | 14 | ||||
-rw-r--r-- | lib/gitlab/git/lfs_pointer_file.rb | 9 | ||||
-rw-r--r-- | lib/gitlab/git/repository.rb | 5 | ||||
-rw-r--r-- | lib/gitlab/import_export/project_tree_restorer.rb | 10 | ||||
-rw-r--r-- | lib/gitlab/import_export/project_tree_saver.rb | 7 | ||||
-rw-r--r-- | lib/gitlab/kubernetes/namespace.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/metrics/methods.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/slash_commands/command.rb | 1 | ||||
-rw-r--r-- | lib/gitlab/slash_commands/issue_move.rb | 45 | ||||
-rw-r--r-- | lib/gitlab/slash_commands/presenters/issue_move.rb | 53 |
16 files changed, 267 insertions, 32 deletions
diff --git a/lib/gitlab/ci/variables/collection.rb b/lib/gitlab/ci/variables/collection.rb new file mode 100644 index 00000000000..0deca55fe8f --- /dev/null +++ b/lib/gitlab/ci/variables/collection.rb @@ -0,0 +1,38 @@ +module Gitlab + module Ci + module Variables + class Collection + include Enumerable + + def initialize(variables = []) + @variables = [] + + variables.each { |variable| self.append(variable) } + end + + def append(resource) + tap { @variables.append(Collection::Item.fabricate(resource)) } + end + + def concat(resources) + tap { resources.each { |variable| self.append(variable) } } + end + + def each + @variables.each { |variable| yield variable } + end + + def +(other) + self.class.new.tap do |collection| + self.each { |variable| collection.append(variable) } + other.each { |variable| collection.append(variable) } + end + end + + def to_runner_variables + self.map(&:to_hash) + end + end + end + end +end diff --git a/lib/gitlab/ci/variables/collection/item.rb b/lib/gitlab/ci/variables/collection/item.rb new file mode 100644 index 00000000000..939912981e6 --- /dev/null +++ b/lib/gitlab/ci/variables/collection/item.rb @@ -0,0 +1,50 @@ +module Gitlab + module Ci + module Variables + class Collection + class Item + def initialize(**options) + @variable = { + key: options.fetch(:key), + value: options.fetch(:value), + public: options.fetch(:public, true), + file: options.fetch(:files, false) + } + end + + def [](key) + @variable.fetch(key) + end + + def ==(other) + to_hash == self.class.fabricate(other).to_hash + end + + ## + # If `file: true` has been provided we expose it, otherwise we + # don't expose `file` attribute at all (stems from what the runner + # expects). + # + def to_hash + @variable.reject do |hash_key, hash_value| + hash_key == :file && hash_value == false + end + end + + def self.fabricate(resource) + case resource + when Hash + self.new(resource) + when ::HasVariable + self.new(resource.to_runner_variable) + when self + resource.dup + else + raise ArgumentError, "Unknown `#{resource.class}` variable resource!" + end + end + end + end + end + end +end diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index b7c596a973d..e392a015b91 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -70,7 +70,7 @@ module Gitlab active_db_connection = ActiveRecord::Base.connection.active? rescue false active_db_connection && - ActiveRecord::Base.connection.table_exists?('application_settings') + Gitlab::Database.cached_table_exists?('application_settings') rescue ActiveRecord::NoDatabaseError false end diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb index e51794fef99..76501dd50e8 100644 --- a/lib/gitlab/database.rb +++ b/lib/gitlab/database.rb @@ -183,6 +183,15 @@ module Gitlab ActiveRecord::Base.connection end + def self.cached_column_exists?(table_name, column_name) + connection.schema_cache.columns_hash(table_name).has_key?(column_name.to_s) + end + + def self.cached_table_exists?(table_name) + # Rails 5 uses data_source_exists? instead of table_exists? + connection.schema_cache.table_exists?(table_name) + end + private_class_method :connection def self.database_version diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb index dbe6259fce7..21287a8efd0 100644 --- a/lib/gitlab/database/migration_helpers.rb +++ b/lib/gitlab/database/migration_helpers.rb @@ -859,6 +859,19 @@ into similar problems in the future (e.g. when new tables are created). BackgroundMigrationWorker.perform_in(delay_interval * index, job_class_name, [start_id, end_id]) end end + + def foreign_key_exists?(table, column) + foreign_keys(table).any? do |key| + key.options[:column] == column.to_s + end + end + + # Rails' index_exists? doesn't work when you only give it a table and index + # name. As such we have to use some extra code to check if an index exists for + # a given name. + def index_exists_by_name?(table, index) + indexes(table).map(&:name).include?(index) + end end end end diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb index 34b070dd375..014854da55c 100644 --- a/lib/gitlab/diff/file.rb +++ b/lib/gitlab/diff/file.rb @@ -27,8 +27,8 @@ module Gitlab @fallback_diff_refs = fallback_diff_refs # Ensure items are collected in the the batch - new_blob - old_blob + new_blob_lazy + old_blob_lazy end def position(position_marker, position_type: :text) @@ -101,25 +101,19 @@ module Gitlab end def new_blob - return unless new_content_sha - - Blob.lazy(repository.project, new_content_sha, file_path) + new_blob_lazy&.itself end def old_blob - return unless old_content_sha - - Blob.lazy(repository.project, old_content_sha, old_path) + old_blob_lazy&.itself end def content_sha new_content_sha || old_content_sha end - # Use #itself to check the value wrapped by a BatchLoader instance, rather - # than if the BatchLoader instance itself is falsey. def blob - new_blob&.itself || old_blob&.itself + new_blob || old_blob end attr_writer :highlighted_diff_lines @@ -237,17 +231,14 @@ module Gitlab private - # The blob instances are instances of BatchLoader, which means calling - # &. directly on them won't work. Object#try also won't work, because Blob - # doesn't inherit from Object, but from BasicObject (via SimpleDelegator). + # We can't use Object#try because Blob doesn't inherit from Object, but + # from BasicObject (via SimpleDelegator). def try_blobs(meth) - old_blob&.itself&.public_send(meth) || new_blob&.itself&.public_send(meth) + old_blob&.public_send(meth) || new_blob&.public_send(meth) end - # We can't use #compact for the same reason we can't use &., but calling - # #nil? explicitly does work because it is proxied to the blob itself. def valid_blobs - [old_blob, new_blob].reject(&:nil?) + [old_blob, new_blob].compact end def text_position_properties(line) @@ -262,6 +253,18 @@ module Gitlab old_blob && new_blob && old_blob.id != new_blob.id end + def new_blob_lazy + return unless new_content_sha + + Blob.lazy(repository.project, new_content_sha, file_path) + end + + def old_blob_lazy + return unless old_content_sha + + Blob.lazy(repository.project, old_content_sha, old_path) + end + def simple_viewer_class return DiffViewer::NotDiffable unless diffable? diff --git a/lib/gitlab/diff/file_collection/merge_request_diff.rb b/lib/gitlab/diff/file_collection/merge_request_diff.rb index ff68bc7303a..c358ae428cf 100644 --- a/lib/gitlab/diff/file_collection/merge_request_diff.rb +++ b/lib/gitlab/diff/file_collection/merge_request_diff.rb @@ -29,6 +29,14 @@ module Gitlab @merge_request_diff.real_size end + def clear_cache! + Rails.cache.delete(cache_key) + end + + def cache_key + [@merge_request_diff, 'highlighted-diff-files', diff_options] + end + private def highlight_diff_file_from_cache!(diff_file, cache_diff_lines) @@ -64,16 +72,12 @@ module Gitlab end def store_highlight_cache - Rails.cache.write(cache_key, highlight_cache) if @highlight_cache_was_empty + Rails.cache.write(cache_key, highlight_cache, expires_in: 1.week) if @highlight_cache_was_empty end def cacheable?(diff_file) @merge_request_diff.present? && diff_file.text? && diff_file.diffable? end - - def cache_key - [@merge_request_diff, 'highlighted-diff-files', diff_options] - end end end end diff --git a/lib/gitlab/git/lfs_pointer_file.rb b/lib/gitlab/git/lfs_pointer_file.rb index da12ed7d125..2ae0a889590 100644 --- a/lib/gitlab/git/lfs_pointer_file.rb +++ b/lib/gitlab/git/lfs_pointer_file.rb @@ -1,13 +1,16 @@ module Gitlab module Git class LfsPointerFile + VERSION = "https://git-lfs.github.com/spec/v1".freeze + VERSION_LINE = "version #{VERSION}".freeze + def initialize(data) @data = data end def pointer @pointer ||= <<~FILE - version https://git-lfs.github.com/spec/v1 + #{VERSION_LINE} oid sha256:#{sha256} size #{size} FILE @@ -20,6 +23,10 @@ module Gitlab def sha256 @sha256 ||= Digest::SHA256.hexdigest(@data) end + + def inspect + "#<#{self.class}:#{object_id} @size=#{size}, @sha256=#{sha256.inspect}>" + end end end end diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index fbc93542619..9811c447a01 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -1002,8 +1002,9 @@ module Gitlab # This only checks the root .gitattributes file, # it does not traverse subfolders to find additional .gitattributes files # - # This method is around 30 times slower than `attributes`, - # which uses `$GIT_DIR/info/attributes` + # This method is around 30 times slower than `attributes`, which uses + # `$GIT_DIR/info/attributes`. Consider caching AttributesAtRefParser + # and reusing that for multiple calls instead of this method. def attributes_at(ref, file_path) parser = AttributesAtRefParser.new(self, ref) parser.attributes(file_path) diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb index 4b5f9f3a926..8f5bb8f9597 100644 --- a/lib/gitlab/import_export/project_tree_restorer.rb +++ b/lib/gitlab/import_export/project_tree_restorer.rb @@ -35,6 +35,8 @@ module Gitlab end def restored_project + return @project unless @tree_hash + @restored_project ||= restore_project end @@ -81,9 +83,13 @@ module Gitlab end def restore_project - return @project unless @tree_hash + params = project_params + + if params[:description].present? + params[:description_html] = nil + end - @project.update_columns(project_params) + @project.update_columns(params) @project end diff --git a/lib/gitlab/import_export/project_tree_saver.rb b/lib/gitlab/import_export/project_tree_saver.rb index 3473b466936..5510c0b8b2f 100644 --- a/lib/gitlab/import_export/project_tree_saver.rb +++ b/lib/gitlab/import_export/project_tree_saver.rb @@ -5,7 +5,8 @@ module Gitlab attr_reader :full_path - def initialize(project:, current_user:, shared:) + def initialize(project:, current_user:, shared:, params: {}) + @params = params @project = project @current_user = current_user @shared = shared @@ -25,6 +26,10 @@ module Gitlab private def project_json_tree + if @params[:description].present? + project_json['description'] = @params[:description] + end + project_json['project_members'] += group_members_json project_json.to_json diff --git a/lib/gitlab/kubernetes/namespace.rb b/lib/gitlab/kubernetes/namespace.rb index fbbddb7bffa..e6ff6160ab9 100644 --- a/lib/gitlab/kubernetes/namespace.rb +++ b/lib/gitlab/kubernetes/namespace.rb @@ -10,7 +10,7 @@ module Gitlab def exists? @client.get_namespace(name) - rescue ::KubeException => ke + rescue ::Kubeclient::HttpError => ke raise ke unless ke.error_code == 404 false diff --git a/lib/gitlab/metrics/methods.rb b/lib/gitlab/metrics/methods.rb index cd7c1e507f7..f79eb0cd1bf 100644 --- a/lib/gitlab/metrics/methods.rb +++ b/lib/gitlab/metrics/methods.rb @@ -50,7 +50,7 @@ module Gitlab end def disabled_by_feature(options) - options.with_feature && !Feature.get(options.with_feature).enabled? + options.with_feature && !::Feature.get(options.with_feature).enabled? end def build_metric!(type, name, options) diff --git a/lib/gitlab/slash_commands/command.rb b/lib/gitlab/slash_commands/command.rb index 85aaa6b0eba..bb778f37096 100644 --- a/lib/gitlab/slash_commands/command.rb +++ b/lib/gitlab/slash_commands/command.rb @@ -5,6 +5,7 @@ module Gitlab Gitlab::SlashCommands::IssueShow, Gitlab::SlashCommands::IssueNew, Gitlab::SlashCommands::IssueSearch, + Gitlab::SlashCommands::IssueMove, Gitlab::SlashCommands::Deploy ].freeze diff --git a/lib/gitlab/slash_commands/issue_move.rb b/lib/gitlab/slash_commands/issue_move.rb new file mode 100644 index 00000000000..3985e635983 --- /dev/null +++ b/lib/gitlab/slash_commands/issue_move.rb @@ -0,0 +1,45 @@ +module Gitlab + module SlashCommands + class IssueMove < IssueCommand + def self.match(text) + %r{ + \A # the beginning of a string + issue\s+move\s+ # the command + \#?(?<iid>\d+)\s+ # the issue id, may preceded by hash sign + (to\s+)? # aid the command to be much more human-ly + (?<project_path>[^\s]+) # named group for id of dest. project + }x.match(text) + end + + def self.help_message + 'issue move <issue_id> (to)? <project_path>' + end + + def self.allowed?(project, user) + can?(user, :admin_issue, project) + end + + def execute(match) + old_issue = find_by_iid(match[:iid]) + target_project = Project.find_by_full_path(match[:project_path]) + + unless current_user.can?(:read_project, target_project) && old_issue + return Gitlab::SlashCommands::Presenters::Access.new.not_found + end + + new_issue = Issues::MoveService.new(project, current_user) + .execute(old_issue, target_project) + + presenter(new_issue).present(old_issue) + rescue Issues::MoveService::MoveError => e + presenter(old_issue).display_move_error(e.message) + end + + private + + def presenter(issue) + Gitlab::SlashCommands::Presenters::IssueMove.new(issue) + end + end + end +end diff --git a/lib/gitlab/slash_commands/presenters/issue_move.rb b/lib/gitlab/slash_commands/presenters/issue_move.rb new file mode 100644 index 00000000000..03921729941 --- /dev/null +++ b/lib/gitlab/slash_commands/presenters/issue_move.rb @@ -0,0 +1,53 @@ +# coding: utf-8 +module Gitlab + module SlashCommands + module Presenters + class IssueMove < Presenters::Base + include Presenters::IssueBase + + def present(old_issue) + in_channel_response(moved_issue(old_issue)) + end + + def display_move_error(error) + message = header_with_list("The action was not successful, because:", [error]) + + ephemeral_response(text: message) + end + + private + + def moved_issue(old_issue) + { + attachments: [ + { + title: "#{@resource.title} ยท #{@resource.to_reference}", + title_link: resource_url, + author_name: author.name, + author_icon: author.avatar_url, + fallback: "Issue #{@resource.to_reference}: #{@resource.title}", + pretext: pretext(old_issue), + color: color(@resource), + fields: fields, + mrkdwn_in: [ + :title, + :pretext, + :text, + :fields + ] + } + ] + } + end + + def pretext(old_issue) + "Moved issue *#{issue_link(old_issue)}* to *#{issue_link(@resource)}*" + end + + def issue_link(issue) + "[#{issue.to_reference}](#{project_issue_url(issue.project, issue)})" + end + end + end + end +end |