diff options
| author | Micaรซl Bergeron <mbergeron@gitlab.com> | 2018-03-22 09:06:10 -0400 |
|---|---|---|
| committer | Micaรซl Bergeron <mbergeron@gitlab.com> | 2018-03-22 09:06:10 -0400 |
| commit | 9c6663ea079128bb730ec2a168b43961cd9462ec (patch) | |
| tree | 4f62c2e745c7f3e8571ee8c023abcce316c68275 /lib | |
| parent | 6801a93e5e7447199b091e44f33c96d22a1a1960 (diff) | |
| parent | c01697539c3da4e72b1812662ff35d1f709d1dcc (diff) | |
| download | gitlab-ce-9c6663ea079128bb730ec2a168b43961cd9462ec.tar.gz | |
Merge remote-tracking branch 'origin/master' into 40781-os-to-ce
Diffstat (limited to 'lib')
56 files changed, 732 insertions, 161 deletions
diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb index 4b564cfdef2..14648588dfd 100644 --- a/lib/api/helpers/internal_helpers.rb +++ b/lib/api/helpers/internal_helpers.rb @@ -109,7 +109,7 @@ module API # Return the Gitaly Address if it is enabled def gitaly_payload(action) - return unless %w[git-receive-pack git-upload-pack].include?(action) + return unless %w[git-receive-pack git-upload-pack git-upload-archive].include?(action) { repository: repository.gitaly_repository, diff --git a/lib/api/helpers/related_resources_helpers.rb b/lib/api/helpers/related_resources_helpers.rb index 1f677529b07..7f4d6e58b34 100644 --- a/lib/api/helpers/related_resources_helpers.rb +++ b/lib/api/helpers/related_resources_helpers.rb @@ -15,7 +15,7 @@ module API url_options = Gitlab::Application.routes.default_url_options protocol, host, port = url_options.slice(:protocol, :host, :port).values - URI::HTTP.build(scheme: protocol, host: host, port: port, path: path).to_s + URI::Generic.build(scheme: protocol, host: host, port: port, path: path).to_s end private diff --git a/lib/api/project_export.rb b/lib/api/project_export.rb index 03d5373312e..efc4a33ae1b 100644 --- a/lib/api/project_export.rb +++ b/lib/api/project_export.rb @@ -31,8 +31,13 @@ module API desc 'Start export' do detail 'This feature was introduced in GitLab 10.6.' end + params do + optional :description, type: String, desc: 'Override the project description' + end post ':id/export' do - user_project.add_export_job(current_user: current_user) + project_export_params = declared_params(include_missing: false) + + user_project.add_export_job(current_user: current_user, params: project_export_params) accepted! end diff --git a/lib/api/search.rb b/lib/api/search.rb index 3556ad98c52..5d9ec617cb7 100644 --- a/lib/api/search.rb +++ b/lib/api/search.rb @@ -84,7 +84,7 @@ module API values: %w(projects issues merge_requests milestones) use :pagination end - get ':id/-/search' do + get ':id/(-/)search' do present search(group_id: user_group.id), with: entity end end @@ -103,7 +103,7 @@ module API values: %w(issues merge_requests milestones notes wiki_blobs commits blobs) use :pagination end - get ':id/-/search' do + get ':id/(-/)search' do present search(project_id: user_project.id), with: entity end end diff --git a/lib/api/services.rb b/lib/api/services.rb index 6c97659166d..794fdab8f2b 100644 --- a/lib/api/services.rb +++ b/lib/api/services.rb @@ -735,7 +735,7 @@ module API required: false, name: event_name.to_sym, type: String, - desc: ServicesHelper.service_event_description(event_name) + desc: service.event_description(event_name) } end end diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb index 9bdedeb6615..262458a872a 100644 --- a/lib/banzai/filter/relative_link_filter.rb +++ b/lib/banzai/filter/relative_link_filter.rb @@ -84,7 +84,7 @@ module Banzai relative_url_root, project.full_path, uri_type(file_path), - Addressable::URI.escape(ref), + Addressable::URI.escape(ref).gsub('#', '%23'), Addressable::URI.escape(file_path) ].compact.join('/').squeeze('/').chomp('/') diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb index 4001b8a85e3..8b2f05fffec 100644 --- a/lib/banzai/pipeline/gfm_pipeline.rb +++ b/lib/banzai/pipeline/gfm_pipeline.rb @@ -2,10 +2,10 @@ module Banzai module Pipeline class GfmPipeline < BasePipeline # These filters convert GitLab Flavored Markdown (GFM) to HTML. - # The handlers defined in app/assets/javascripts/copy_as_gfm.js + # The handlers defined in app/assets/javascripts/behaviors/markdown/copy_as_gfm.js # consequently convert that same HTML to GFM to be copied to the clipboard. # Every filter that generates HTML from GFM should have a handler in - # app/assets/javascripts/copy_as_gfm.js, in reverse order. + # app/assets/javascripts/behaviors/markdown/copy_as_gfm.js, in reverse order. # The GFM-to-HTML-to-GFM cycle is tested in spec/features/copy_as_gfm_spec.rb. def self.filters @filters ||= FilterArray[ diff --git a/lib/container_registry/client.rb b/lib/container_registry/client.rb index c7263f302ab..010ca1ec27b 100644 --- a/lib/container_registry/client.rb +++ b/lib/container_registry/client.rb @@ -52,6 +52,8 @@ module ContainerRegistry conn.request(:authorization, :bearer, options[:token].to_s) end + yield(conn) if block_given? + conn.adapter :net_http end @@ -80,8 +82,7 @@ module ContainerRegistry def faraday @faraday ||= Faraday.new(@base_uri) do |conn| - initialize_connection(conn, @options) - accept_manifest(conn) + initialize_connection(conn, @options, &method(:accept_manifest)) end end diff --git a/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb b/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb index 7cb4bccb23c..91175b49c79 100644 --- a/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb +++ b/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb @@ -3,7 +3,7 @@ require 'rails/generators' module Rails class PostDeploymentMigrationGenerator < Rails::Generators::NamedBase def create_migration_file - timestamp = Time.now.strftime('%Y%m%d%H%I%S') + timestamp = Time.now.strftime('%Y%m%d%H%M%S') template "migration.rb", "db/post_migrate/#{timestamp}_#{file_name}.rb" end diff --git a/lib/gitlab.rb b/lib/gitlab.rb index 11f7c8b9510..aa9fd36d9ff 100644 --- a/lib/gitlab.rb +++ b/lib/gitlab.rb @@ -2,6 +2,7 @@ require_dependency 'gitlab/git' module Gitlab COM_URL = 'https://gitlab.com'.freeze + APP_DIRS_PATTERN = %r{^/?(app|config|ee|lib|spec|\(\w*\))} def self.com? # Check `staging?` as well to keep parity with gitlab.com diff --git a/lib/gitlab/auth/saml/config.rb b/lib/gitlab/auth/saml/config.rb index e654e7fe438..2760b1a3247 100644 --- a/lib/gitlab/auth/saml/config.rb +++ b/lib/gitlab/auth/saml/config.rb @@ -4,7 +4,7 @@ module Gitlab class Config class << self def options - Gitlab.config.omniauth.providers.find { |provider| provider.name == 'saml' } + Gitlab::Auth::OAuth::Provider.config_for('saml') end def groups diff --git a/lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb b/lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb index 7bffffec94d..d5cf9e0d53a 100644 --- a/lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb +++ b/lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb @@ -19,7 +19,7 @@ module Gitlab WHERE merge_request_diffs.id = merge_request_diff_commits.merge_request_diff_id )'.squish - MergeRequestDiff.where(id: start_id..stop_id).update_all(update) + MergeRequestDiff.where(id: start_id..stop_id).where(commits_count: nil).update_all(update) end end end 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/conflict/file_collection.rb b/lib/gitlab/conflict/file_collection.rb index 0a3ae2c3760..3ccfd9a739d 100644 --- a/lib/gitlab/conflict/file_collection.rb +++ b/lib/gitlab/conflict/file_collection.rb @@ -1,14 +1,18 @@ module Gitlab module Conflict class FileCollection + include Gitlab::RepositoryCacheAdapter + attr_reader :merge_request, :resolver def initialize(merge_request) our_commit = merge_request.source_branch_head.raw their_commit = merge_request.target_branch_head.raw - target_repo = merge_request.target_project.repository.raw + @target_repo = merge_request.target_project.repository @source_repo = merge_request.source_project.repository.raw - @resolver = Gitlab::Git::Conflict::Resolver.new(target_repo, our_commit.id, their_commit.id) + @our_commit_id = our_commit.id + @their_commit_id = their_commit.id + @resolver = Gitlab::Git::Conflict::Resolver.new(@target_repo.raw, @our_commit_id, @their_commit_id) @merge_request = merge_request end @@ -30,6 +34,17 @@ module Gitlab end end + def can_be_resolved_in_ui? + # Try to parse each conflict. If the MR's mergeable status hasn't been + # updated, ensure that we don't say there are conflicts to resolve + # when there are no conflict files. + files.each(&:lines) + files.any? + rescue Gitlab::Git::CommandError, Gitlab::Git::Conflict::Parser::UnresolvableError, Gitlab::Git::Conflict::Resolver::ConflictSideMissing + false + end + cache_method :can_be_resolved_in_ui? + def file_for_path(old_path, new_path) files.find { |file| file.their_path == old_path && file.our_path == new_path } end @@ -56,6 +71,19 @@ Merge branch '#{merge_request.target_branch}' into '#{merge_request.source_branc #{conflict_filenames.join("\n")} EOM end + + private + + def cache + @cache ||= begin + # Use the commit ids as a namespace so if the MR branches get + # updated we instantiate the cache under a different namespace. That + # way don't have to worry about explicitly invalidating the cache + namespace = "#{@our_commit_id}:#{@their_commit_id}" + + Gitlab::RepositoryCache.new(@target_repo, extra_namespace: namespace) + 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..44ca434056f 100644 --- a/lib/gitlab/database/migration_helpers.rb +++ b/lib/gitlab/database/migration_helpers.rb @@ -59,6 +59,11 @@ module Gitlab disable_statement_timeout end + if index_exists?(table_name, column_name, options) + Rails.logger.warn "Index not created because it already exists (this may be due to an aborted migration or similar): table_name: #{table_name}, column_name: #{column_name}" + return + end + add_index(table_name, column_name, options) end @@ -83,6 +88,11 @@ module Gitlab disable_statement_timeout end + unless index_exists?(table_name, column_name, options) + Rails.logger.warn "Index not removed because it does not exist (this may be due to an aborted migration or similar): table_name: #{table_name}, column_name: #{column_name}" + return + end + remove_index(table_name, options.merge({ column: column_name })) end @@ -107,6 +117,11 @@ module Gitlab disable_statement_timeout end + unless index_exists_by_name?(table_name, index_name) + Rails.logger.warn "Index not removed because it does not exist (this may be due to an aborted migration or similar): table_name: #{table_name}, index_name: #{index_name}" + return + end + remove_index(table_name, options.merge({ name: index_name })) end @@ -140,6 +155,13 @@ module Gitlab # of PostgreSQL's "VALIDATE CONSTRAINT". As a result we'll just fall # back to the normal foreign key procedure. if Database.mysql? + if foreign_key_exists?(source, target, column: column) + Rails.logger.warn "Foreign key not created because it exists already " \ + "(this may be due to an aborted migration or similar): " \ + "source: #{source}, target: #{target}, column: #{column}" + return + end + return add_foreign_key(source, target, column: column, on_delete: on_delete) @@ -151,25 +173,43 @@ module Gitlab key_name = concurrent_foreign_key_name(source, column) - # Using NOT VALID allows us to create a key without immediately - # validating it. This means we keep the ALTER TABLE lock only for a - # short period of time. The key _is_ enforced for any newly created - # data. - execute <<-EOF.strip_heredoc - ALTER TABLE #{source} - ADD CONSTRAINT #{key_name} - FOREIGN KEY (#{column}) - REFERENCES #{target} (id) - #{on_delete ? "ON DELETE #{on_delete.upcase}" : ''} - NOT VALID; - EOF + unless foreign_key_exists?(source, target, column: column) + Rails.logger.warn "Foreign key not created because it exists already " \ + "(this may be due to an aborted migration or similar): " \ + "source: #{source}, target: #{target}, column: #{column}" + + # Using NOT VALID allows us to create a key without immediately + # validating it. This means we keep the ALTER TABLE lock only for a + # short period of time. The key _is_ enforced for any newly created + # data. + execute <<-EOF.strip_heredoc + ALTER TABLE #{source} + ADD CONSTRAINT #{key_name} + FOREIGN KEY (#{column}) + REFERENCES #{target} (id) + #{on_delete ? "ON DELETE #{on_delete.upcase}" : ''} + NOT VALID; + EOF + end # Validate the existing constraint. This can potentially take a very # long time to complete, but fortunately does not lock the source table # while running. + # + # Note this is a no-op in case the constraint is VALID already execute("ALTER TABLE #{source} VALIDATE CONSTRAINT #{key_name};") end + def foreign_key_exists?(source, target = nil, column: nil) + foreign_keys(source).any? do |key| + if column + key.options[:column].to_s == column.to_s + else + key.to_table.to_s == target.to_s + end + end + end + # Returns the name for a concurrent foreign key. # # PostgreSQL constraint names have a limit of 63 bytes. The logic used @@ -859,6 +899,13 @@ 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 + + # 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/diff_refs.rb b/lib/gitlab/diff/diff_refs.rb index 88e0db830f6..81df47964be 100644 --- a/lib/gitlab/diff/diff_refs.rb +++ b/lib/gitlab/diff/diff_refs.rb @@ -44,7 +44,11 @@ module Gitlab project.commit(head_sha) else straight = start_sha == base_sha - CompareService.new(project, head_sha).execute(project, start_sha, straight: straight) + + CompareService.new(project, head_sha).execute(project, + start_sha, + base_sha: base_sha, + straight: straight) 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 fcda1fe2233..c358ae428cf 100644 --- a/lib/gitlab/diff/file_collection/merge_request_diff.rb +++ b/lib/gitlab/diff/file_collection/merge_request_diff.rb @@ -13,22 +13,32 @@ module Gitlab end def diff_files - super.tap { |_| store_highlight_cache } + # Make sure to _not_ send any method call to Gitlab::Diff::File + # _before_ all of them were collected (`super`). Premature method calls will + # trigger N+1 RPCs to Gitaly through BatchLoader records (Blob.lazy). + # + diff_files = super + + diff_files.each { |diff_file| cache_highlight!(diff_file) if cacheable?(diff_file) } + store_highlight_cache + + diff_files end def real_size @merge_request_diff.real_size end - private + def clear_cache! + Rails.cache.delete(cache_key) + end - # Extracted method to highlight in the same iteration to the diff_collection. - def decorate_diff!(diff) - diff_file = super - cache_highlight!(diff_file) if cacheable?(diff_file) - diff_file + def cache_key + [@merge_request_diff, 'highlighted-diff-files', diff_options] end + private + def highlight_diff_file_from_cache!(diff_file, cache_diff_lines) diff_file.highlighted_diff_lines = cache_diff_lines.map do |line| Gitlab::Diff::Line.init_from_hash(line) @@ -62,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/exclusive_lease.rb b/lib/gitlab/exclusive_lease.rb index dbb8f317afe..12b5e240962 100644 --- a/lib/gitlab/exclusive_lease.rb +++ b/lib/gitlab/exclusive_lease.rb @@ -41,6 +41,16 @@ module Gitlab "gitlab:exclusive_lease:#{key}" end + # Removes any existing exclusive_lease from redis + # Don't run this in a live system without making sure no one is using the leases + def self.reset_all!(scope = '*') + Gitlab::Redis::SharedState.with do |redis| + redis.scan_each(match: redis_shared_state_key(scope)).each do |key| + redis.del(key) + end + end + end + def initialize(key, uuid: nil, timeout:) @redis_shared_state_key = self.class.redis_shared_state_key(key) @timeout = timeout diff --git a/lib/gitlab/git/gitlab_projects.rb b/lib/gitlab/git/gitlab_projects.rb index 5e1e22ae65c..a142ed6b2ef 100644 --- a/lib/gitlab/git/gitlab_projects.rb +++ b/lib/gitlab/git/gitlab_projects.rb @@ -67,7 +67,7 @@ module Gitlab tags_option = tags ? '--tags' : '--no-tags' logger.info "Fetching remote #{name} for repository #{repository_absolute_path}." - cmd = %W(git fetch #{name} --quiet) + cmd = %W(#{Gitlab.config.git.bin_path} fetch #{name} --quiet) cmd << '--prune' if prune cmd << '--force' if force cmd << tags_option @@ -85,7 +85,7 @@ module Gitlab def push_branches(remote_name, timeout, force, branch_names) logger.info "Pushing branches from #{repository_absolute_path} to remote #{remote_name}: #{branch_names}" - cmd = %w(git push) + cmd = %W(#{Gitlab.config.git.bin_path} push) cmd << '--force' if force cmd += %W(-- #{remote_name}).concat(branch_names) @@ -102,7 +102,7 @@ module Gitlab branches = branch_names.map { |branch_name| ":#{branch_name}" } logger.info "Pushing deleted branches from #{repository_absolute_path} to remote #{remote_name}: #{branch_names}" - cmd = %W(git push -- #{remote_name}).concat(branches) + cmd = %W(#{Gitlab.config.git.bin_path} push -- #{remote_name}).concat(branches) success = run(cmd, repository_absolute_path) @@ -143,7 +143,7 @@ module Gitlab end def remove_origin_in_repo - cmd = %w(git remote rm origin) + cmd = %W(#{Gitlab.config.git.bin_path} remote rm origin) run(cmd, repository_absolute_path) end @@ -223,7 +223,7 @@ module Gitlab masked_source = mask_password_in_url(source) logger.info "Importing project from <#{masked_source}> to <#{repository_absolute_path}>." - cmd = %W(git clone --bare -- #{source} #{repository_absolute_path}) + cmd = %W(#{Gitlab.config.git.bin_path} clone --bare -- #{source} #{repository_absolute_path}) success = run_with_timeout(cmd, timeout, nil) @@ -266,7 +266,7 @@ module Gitlab FileUtils.mkdir_p(File.dirname(to_path), mode: 0770) logger.info "Forking repository from <#{from_path}> to <#{to_path}>." - cmd = %W(git clone --bare --no-local -- #{from_path} #{to_path}) + cmd = %W(#{Gitlab.config.git.bin_path} clone --bare --no-local -- #{from_path} #{to_path}) run(cmd, nil) && Gitlab::Git::Repository.create_hooks(to_path, global_hooks_path) 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 656f3999756..208710b0935 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) @@ -1189,15 +1190,9 @@ module Gitlab end def fsck - gitaly_migrate(:git_fsck) do |is_enabled| - msg, status = if is_enabled - gitaly_fsck - else - shell_fsck - end + msg, status = gitaly_repository_client.fsck - raise GitError.new("Could not fsck repository: #{msg}") unless status.zero? - end + raise GitError.new("Could not fsck repository: #{msg}") unless status.zero? end def create_from_bundle(bundle_path) @@ -1395,7 +1390,7 @@ module Gitlab offset = 2 args = %W(grep -i -I -n -z --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref}) - run_git(args).first.scrub.split(/^--$/) + run_git(args).first.scrub.split(/^--\n/) end def can_be_merged?(source_sha, target_branch) @@ -1596,14 +1591,6 @@ module Gitlab File.write(File.join(worktree_info_path, 'sparse-checkout'), files) end - def gitaly_fsck - gitaly_repository_client.fsck - end - - def shell_fsck - run_git(%W[--git-dir=#{path} fsck], nice: true) - end - def rugged_fetch_source_branch(source_repository, source_branch, local_ref) with_repo_branch_commit(source_repository, source_branch) do |commit| if commit diff --git a/lib/gitlab/git/wiki.rb b/lib/gitlab/git/wiki.rb index 52b44b9b3c5..8d82820915d 100644 --- a/lib/gitlab/git/wiki.rb +++ b/lib/gitlab/git/wiki.rb @@ -29,7 +29,6 @@ module Gitlab @repository.gitaly_migrate(:wiki_write_page) do |is_enabled| if is_enabled gitaly_write_page(name, format, content, commit_details) - gollum_wiki.clear_cache else gollum_write_page(name, format, content, commit_details) end @@ -40,7 +39,6 @@ module Gitlab @repository.gitaly_migrate(:wiki_delete_page) do |is_enabled| if is_enabled gitaly_delete_page(page_path, commit_details) - gollum_wiki.clear_cache else gollum_delete_page(page_path, commit_details) end @@ -51,7 +49,6 @@ module Gitlab @repository.gitaly_migrate(:wiki_update_page) do |is_enabled| if is_enabled gitaly_update_page(page_path, title, format, content, commit_details) - gollum_wiki.clear_cache else gollum_update_page(page_path, title, format, content, commit_details) end diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb index 9cd76630484..8ca30ffc232 100644 --- a/lib/gitlab/gitaly_client.rb +++ b/lib/gitlab/gitaly_client.rb @@ -119,6 +119,9 @@ module Gitlab # def self.call(storage, service, rpc, request, remote_storage: nil, timeout: nil) start = Gitlab::Metrics::System.monotonic_time + request_hash = request.is_a?(Google::Protobuf::MessageExts) ? request.to_h : {} + @current_call_id ||= SecureRandom.uuid + enforce_gitaly_request_limits(:call) kwargs = request_kwargs(storage, timeout, remote_storage: remote_storage) @@ -135,6 +138,10 @@ module Gitlab gitaly_controller_action_duration_seconds.observe( current_transaction_labels.merge(gitaly_service: service.to_s, rpc: rpc.to_s), duration) + + add_call_details(id: @current_call_id, feature: service, duration: duration, request: request_hash) + + @current_call_id = nil end def self.handle_grpc_unavailable!(ex) @@ -252,12 +259,16 @@ module Gitlab feature_stack.unshift(feature) begin start = Gitlab::Metrics::System.monotonic_time + @current_call_id = SecureRandom.uuid + call_details = { id: @current_call_id } yield is_enabled ensure total_time = Gitlab::Metrics::System.monotonic_time - start gitaly_migrate_call_duration_seconds.observe({ gitaly_enabled: is_enabled, feature: feature }, total_time) feature_stack.shift Thread.current[:gitaly_feature_stack] = nil if feature_stack.empty? + + add_call_details(call_details.merge(feature: feature, duration: total_time)) end end end @@ -344,6 +355,22 @@ module Gitlab end end + def self.add_call_details(details) + id = details.delete(:id) + + return unless id && RequestStore.active? && RequestStore.store[:peek_enabled] + + RequestStore.store['gitaly_call_details'] ||= {} + RequestStore.store['gitaly_call_details'][id] ||= {} + RequestStore.store['gitaly_call_details'][id].merge!(details) + end + + def self.list_call_details + return {} unless RequestStore.active? && RequestStore.store[:peek_enabled] + + RequestStore.store['gitaly_call_details'] || {} + end + def self.expected_server_version path = Rails.root.join(SERVER_VERSION_FILE) path.read.chomp diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb index 4f160e4a447..a61beafae0d 100644 --- a/lib/gitlab/github_import/client.rb +++ b/lib/gitlab/github_import/client.rb @@ -197,10 +197,7 @@ module Gitlab end def github_omniauth_provider - @github_omniauth_provider ||= - Gitlab.config.omniauth.providers - .find { |provider| provider.name == 'github' } - .to_h + @github_omniauth_provider ||= Gitlab::Auth::OAuth::Provider.config_for('github').to_h end def rate_limit_counter diff --git a/lib/gitlab/gitlab_import/client.rb b/lib/gitlab/gitlab_import/client.rb index 075b3982608..5482504e72e 100644 --- a/lib/gitlab/gitlab_import/client.rb +++ b/lib/gitlab/gitlab_import/client.rb @@ -72,7 +72,7 @@ module Gitlab end def config - Gitlab.config.omniauth.providers.find {|provider| provider.name == "gitlab"} + Gitlab::Auth::OAuth::Provider.config_for('gitlab') end def gitlab_options 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/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb index cf6b7e306dd..791a54e1b69 100644 --- a/lib/gitlab/import_export/relation_factory.rb +++ b/lib/gitlab/import_export/relation_factory.rb @@ -70,6 +70,7 @@ module Gitlab update_user_references update_project_references + remove_duplicate_assignees reset_tokens! remove_encrypted_attributes! @@ -83,6 +84,14 @@ module Gitlab end end + def remove_duplicate_assignees + return unless @relation_hash['issue_assignees'] + + # When an assignee did not exist in the members mapper, the importer is + # assigned. We only need to assign each user once. + @relation_hash['issue_assignees'].uniq!(&:user_id) + end + def setup_note set_note_author # attachment is deprecated and note uploads are handled by Markdown uploader 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/legacy_github_import/client.rb b/lib/gitlab/legacy_github_import/client.rb index 53c910d44bd..d8ed0ebca9d 100644 --- a/lib/gitlab/legacy_github_import/client.rb +++ b/lib/gitlab/legacy_github_import/client.rb @@ -83,7 +83,7 @@ module Gitlab end def config - Gitlab.config.omniauth.providers.find { |provider| provider.name == "github" } + Gitlab::Auth::OAuth::Provider.config_for('github') end def github_options 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/middleware/read_only.rb b/lib/gitlab/middleware/read_only.rb index d9d5f90596f..7f63e39b3aa 100644 --- a/lib/gitlab/middleware/read_only.rb +++ b/lib/gitlab/middleware/read_only.rb @@ -13,7 +13,7 @@ module Gitlab end def call(env) - ReadOnly::Controller.new(@app, env).call + ::Gitlab::Middleware::ReadOnly::Controller.new(@app, env).call end end end diff --git a/lib/gitlab/omniauth_initializer.rb b/lib/gitlab/omniauth_initializer.rb new file mode 100644 index 00000000000..35ed3a5ac05 --- /dev/null +++ b/lib/gitlab/omniauth_initializer.rb @@ -0,0 +1,75 @@ +module Gitlab + class OmniauthInitializer + def initialize(devise_config) + @devise_config = devise_config + end + + def execute(providers) + providers.each do |provider| + add_provider(provider['name'].to_sym, *arguments_for(provider)) + end + end + + private + + def add_provider(*args) + @devise_config.omniauth(*args) + end + + def arguments_for(provider) + provider_arguments = [] + + %w[app_id app_secret].each do |argument| + provider_arguments << provider[argument] if provider[argument] + end + + case provider['args'] + when Array + # An Array from the configuration will be expanded. + provider_arguments.concat provider['args'] + when Hash + hash_arguments = provider['args'].merge(provider_defaults(provider)) + + # A Hash from the configuration will be passed as is. + provider_arguments << hash_arguments.symbolize_keys + end + + provider_arguments + end + + def provider_defaults(provider) + case provider['name'] + when 'cas3' + { on_single_sign_out: cas3_signout_handler } + when 'authentiq' + { remote_sign_out_handler: authentiq_signout_handler } + when 'shibboleth' + { fail_with_empty_uid: true } + else + {} + end + end + + def cas3_signout_handler + lambda do |request| + ticket = request.params[:session_index] + raise "Service Ticket not found." unless Gitlab::Auth::OAuth::Session.valid?(:cas3, ticket) + + Gitlab::Auth::OAuth::Session.destroy(:cas3, ticket) + true + end + end + + def authentiq_signout_handler + lambda do |request| + authentiq_session = request.params['sid'] + if Gitlab::Auth::OAuth::Session.valid?(:authentiq, authentiq_session) + Gitlab::Auth::OAuth::Session.destroy(:authentiq, authentiq_session) + true + else + false + end + end + end + end +end diff --git a/lib/gitlab/profiler.rb b/lib/gitlab/profiler.rb index 98a168b43bb..18540e64d4c 100644 --- a/lib/gitlab/profiler.rb +++ b/lib/gitlab/profiler.rb @@ -92,8 +92,8 @@ module Gitlab if type && time @load_times_by_model ||= {} - @load_times_by_model[type] ||= 0 - @load_times_by_model[type] += time.to_f + @load_times_by_model[type] ||= [] + @load_times_by_model[type] << time.to_f end super @@ -135,8 +135,12 @@ module Gitlab def self.log_load_times_by_model(logger) return unless logger.respond_to?(:load_times_by_model) - logger.load_times_by_model.to_a.sort_by(&:last).reverse.each do |(model, time)| - logger.info("#{model} total: #{time.round(2)}ms") + summarised_load_times = logger.load_times_by_model.to_a.map do |(model, times)| + [model, times.count, times.sum] + end + + summarised_load_times.sort_by(&:last).reverse.each do |(model, query_count, time)| + logger.info("#{model} total (#{query_count}): #{time.round(2)}ms") end end end diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index 29277ec6481..390efda326a 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -58,7 +58,7 @@ module Gitlab data = "" startline = 0 - result.strip.each_line.each_with_index do |line, index| + result.each_line.each_with_index do |line, index| prefix ||= line.match(/^(?<ref>[^:]*):(?<filename>.*)\x00(?<startline>\d+)\x00/)&.tap do |matches| ref = matches[:ref] filename = matches[:filename] diff --git a/lib/gitlab/project_transfer.rb b/lib/gitlab/project_transfer.rb index 1bba0b78e2f..690c38737c0 100644 --- a/lib/gitlab/project_transfer.rb +++ b/lib/gitlab/project_transfer.rb @@ -1,13 +1,19 @@ module Gitlab + # This class is used to move local, unhashed files owned by projects to their new location class ProjectTransfer - def move_project(project_path, namespace_path_was, namespace_path) - new_namespace_folder = File.join(root_dir, namespace_path) - FileUtils.mkdir_p(new_namespace_folder) unless Dir.exist?(new_namespace_folder) - from = File.join(root_dir, namespace_path_was, project_path) - to = File.join(root_dir, namespace_path, project_path) + # nil parent_path (or parent_path_was) represents a root namespace + def move_namespace(path, parent_path_was, parent_path) + parent_path_was ||= '' + parent_path ||= '' + new_parent_folder = File.join(root_dir, parent_path) + FileUtils.mkdir_p(new_parent_folder) + from = File.join(root_dir, parent_path_was, path) + to = File.join(root_dir, parent_path, path) move(from, to, "") end + alias_method :move_project, :move_namespace + def rename_project(path_was, path, namespace_path) base_dir = File.join(root_dir, namespace_path) move(path_was, path, base_dir) diff --git a/lib/gitlab/repository_cache.rb b/lib/gitlab/repository_cache.rb new file mode 100644 index 00000000000..b1bf3ca4143 --- /dev/null +++ b/lib/gitlab/repository_cache.rb @@ -0,0 +1,33 @@ +# Interface to the Redis-backed cache store +module Gitlab + class RepositoryCache + attr_reader :repository, :namespace, :backend + + def initialize(repository, extra_namespace: nil, backend: Rails.cache) + @repository = repository + @namespace = "#{repository.full_path}:#{repository.project.id}" + @namespace += ":#{extra_namespace}" if extra_namespace + @backend = backend + end + + def cache_key(type) + "#{type}:#{namespace}" + end + + def expire(key) + backend.delete(cache_key(key)) + end + + def fetch(key, &block) + backend.fetch(cache_key(key), &block) + end + + def exist?(key) + backend.exist?(cache_key(key)) + end + + def read(key) + backend.read(cache_key(key)) + end + end +end diff --git a/lib/gitlab/repository_cache_adapter.rb b/lib/gitlab/repository_cache_adapter.rb new file mode 100644 index 00000000000..7f64a8c9e46 --- /dev/null +++ b/lib/gitlab/repository_cache_adapter.rb @@ -0,0 +1,84 @@ +module Gitlab + module RepositoryCacheAdapter + extend ActiveSupport::Concern + + class_methods do + # Wraps around the given method and caches its output in Redis and an instance + # variable. + # + # This only works for methods that do not take any arguments. + def cache_method(name, fallback: nil, memoize_only: false) + original = :"_uncached_#{name}" + + alias_method(original, name) + + define_method(name) do + cache_method_output(name, fallback: fallback, memoize_only: memoize_only) do + __send__(original) # rubocop:disable GitlabSecurity/PublicSend + end + end + end + end + + # RepositoryCache to be used. Should be overridden by the including class + def cache + raise NotImplementedError + end + + # Caches the supplied block both in a cache and in an instance variable. + # + # The cache key and instance variable are named the same way as the value of + # the `key` argument. + # + # This method will return `nil` if the corresponding instance variable is also + # set to `nil`. This ensures we don't keep yielding the block when it returns + # `nil`. + # + # key - The name of the key to cache the data in. + # fallback - A value to fall back to in the event of a Git error. + def cache_method_output(key, fallback: nil, memoize_only: false, &block) + ivar = cache_instance_variable_name(key) + + if instance_variable_defined?(ivar) + instance_variable_get(ivar) + else + # If the repository doesn't exist and a fallback was specified we return + # that value inmediately. This saves us Rugged/gRPC invocations. + return fallback unless fallback.nil? || cache.repository.exists? + + begin + value = + if memoize_only + yield + else + cache.fetch(key, &block) + end + + instance_variable_set(ivar, value) + rescue Gitlab::Git::Repository::NoRepository + # Even if the above `#exists?` check passes these errors might still + # occur (for example because of a non-existing HEAD). We want to + # gracefully handle this and not cache anything + fallback + end + end + end + + # Expires the caches of a specific set of methods + def expire_method_caches(methods) + methods.each do |key| + cache.expire(key) + + ivar = cache_instance_variable_name(key) + + remove_instance_variable(ivar) if instance_variable_defined?(ivar) + end + end + + private + + def cache_instance_variable_name(key) + :"@#{key.to_s.tr('?!', '')}" + end + end +end diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb index 757ef71b95a..1e45d074e0a 100644 --- a/lib/gitlab/search_results.rb +++ b/lib/gitlab/search_results.rb @@ -7,8 +7,8 @@ module Gitlab def initialize(opts = {}) @id = opts.fetch(:id, nil) - @filename = opts.fetch(:filename, nil) - @basename = opts.fetch(:basename, nil) + @filename = encode_utf8(opts.fetch(:filename, nil)) + @basename = encode_utf8(opts.fetch(:basename, nil)) @ref = opts.fetch(:ref, nil) @startline = opts.fetch(:startline, nil) @data = encode_utf8(opts.fetch(:data, nil)) diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb index dda7afc0999..3a8f5826818 100644 --- a/lib/gitlab/shell.rb +++ b/lib/gitlab/shell.rb @@ -69,13 +69,14 @@ module Gitlab # name - project disk path # # Ex. - # add_repository("/path/to/storage", "gitlab/gitlab-ci") + # create_repository("/path/to/storage", "gitlab/gitlab-ci") # - def add_repository(storage, name) + def create_repository(storage, name) relative_path = name.dup relative_path << '.git' unless relative_path.end_with?('.git') - gitaly_migrate(:create_repository) do |is_enabled| + gitaly_migrate(:create_repository, + status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| if is_enabled repository = Gitlab::Git::Repository.new(storage, relative_path, '') repository.gitaly_repository_client.create_repository @@ -85,7 +86,7 @@ module Gitlab Gitlab::Git::Repository.create(repo_path, bare: true, symlink_hooks_to: gitlab_shell_hooks_path) end end - rescue => err + rescue => err # Once the Rugged codes gets removes this can be improved Rails.logger.error("Failed to add repository #{storage}/#{name}: #{err}") false end @@ -487,8 +488,8 @@ module Gitlab Gitlab.config.gitlab_shell.git_timeout end - def gitaly_migrate(method, &block) - Gitlab::GitalyClient.migrate(method, &block) + def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block) + Gitlab::GitalyClient.migrate(method, status: status, &block) rescue GRPC::NotFound, GRPC::BadStatus => e # Old Popen code returns [Error, output] to the caller, so we # need to do the same here... 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 diff --git a/lib/gitlab/verify/job_artifacts.rb b/lib/gitlab/verify/job_artifacts.rb new file mode 100644 index 00000000000..03500a61074 --- /dev/null +++ b/lib/gitlab/verify/job_artifacts.rb @@ -0,0 +1,27 @@ +module Gitlab + module Verify + class JobArtifacts < BatchVerifier + def name + 'Job artifacts' + end + + def describe(object) + "Job artifact: #{object.id}" + end + + private + + def relation + ::Ci::JobArtifact.all + end + + def expected_checksum(artifact) + artifact.file_sha256 + end + + def actual_checksum(artifact) + Digest::SHA256.file(artifact.file.path).hexdigest + end + end + end +end diff --git a/lib/google_api/auth.rb b/lib/google_api/auth.rb index 99a82c849e0..1aeaa387a49 100644 --- a/lib/google_api/auth.rb +++ b/lib/google_api/auth.rb @@ -32,7 +32,7 @@ module GoogleApi private def config - Gitlab.config.omniauth.providers.find { |provider| provider.name == "google_oauth2" } + Gitlab::Auth::OAuth::Provider.config_for('google_oauth2') end def client diff --git a/lib/peek/views/gitaly.rb b/lib/peek/views/gitaly.rb index d519d8e86fa..ab35f7a2258 100644 --- a/lib/peek/views/gitaly.rb +++ b/lib/peek/views/gitaly.rb @@ -10,11 +10,29 @@ module Peek end def results - { duration: formatted_duration, calls: calls } + { + duration: formatted_duration, + calls: calls, + details: details + } end private + def details + ::Gitlab::GitalyClient.list_call_details + .values + .sort { |a, b| b[:duration] <=> a[:duration] } + .map(&method(:format_call_details)) + end + + def format_call_details(call) + pretty_request = call[:request]&.reject { |k, v| v.blank? }.to_h.pretty_inspect + + call.merge(duration: (call[:duration] * 1000).round(3), + request: pretty_request || {}) + end + def formatted_duration ms = duration * 1000 if ms >= 1000 diff --git a/lib/peek/views/host.rb b/lib/peek/views/host.rb new file mode 100644 index 00000000000..43c8a35c7ea --- /dev/null +++ b/lib/peek/views/host.rb @@ -0,0 +1,9 @@ +module Peek + module Views + class Host < View + def results + { hostname: Gitlab::Environment.hostname } + end + end + end +end diff --git a/lib/repository_cache.rb b/lib/repository_cache.rb deleted file mode 100644 index 068a95790c0..00000000000 --- a/lib/repository_cache.rb +++ /dev/null @@ -1,30 +0,0 @@ -# Interface to the Redis-backed cache store used by the Repository model -class RepositoryCache - attr_reader :namespace, :backend, :project_id - - def initialize(namespace, project_id, backend = Rails.cache) - @namespace = namespace - @backend = backend - @project_id = project_id - end - - def cache_key(type) - "#{type}:#{namespace}:#{project_id}" - end - - def expire(key) - backend.delete(cache_key(key)) - end - - def fetch(key, &block) - backend.fetch(cache_key(key), &block) - end - - def exist?(key) - backend.exist?(cache_key(key)) - end - - def read(key) - backend.read(cache_key(key)) - end -end diff --git a/lib/tasks/gitlab/artifacts/check.rake b/lib/tasks/gitlab/artifacts/check.rake new file mode 100644 index 00000000000..a105261ed51 --- /dev/null +++ b/lib/tasks/gitlab/artifacts/check.rake @@ -0,0 +1,8 @@ +namespace :gitlab do + namespace :artifacts do + desc 'GitLab | Artifacts | Check integrity of uploaded job artifacts' + task check: :environment do + Gitlab::Verify::RakeTask.run!(Gitlab::Verify::JobArtifacts) + end + end +end diff --git a/lib/tasks/gitlab/exclusive_lease.rake b/lib/tasks/gitlab/exclusive_lease.rake new file mode 100644 index 00000000000..83722bf6d94 --- /dev/null +++ b/lib/tasks/gitlab/exclusive_lease.rake @@ -0,0 +1,9 @@ +namespace :gitlab do + namespace :exclusive_lease do + desc 'GitLab | Clear existing exclusive leases for specified scope (default: *)' + task :clear, [:scope] => [:environment] do |_, args| + args[:scope].nil? ? Gitlab::ExclusiveLease.reset_all! : Gitlab::ExclusiveLease.reset_all!(args[:scope]) + puts 'All exclusive lease entries were removed.' + end + end +end diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake index 844664b12d4..4fcbbbf8c9d 100644 --- a/lib/tasks/gitlab/shell.rake +++ b/lib/tasks/gitlab/shell.rake @@ -69,7 +69,7 @@ namespace :gitlab do if File.exist?(path_to_repo) print '-' else - if Gitlab::Shell.new.add_repository(project.repository_storage, + if Gitlab::Shell.new.create_repository(project.repository_storage, project.disk_path) print '.' else diff --git a/lib/tasks/haml-lint.rake b/lib/tasks/haml-lint.rake index 5c0cc4990fc..ad2d034b0b4 100644 --- a/lib/tasks/haml-lint.rake +++ b/lib/tasks/haml-lint.rake @@ -2,14 +2,5 @@ unless Rails.env.production? require 'haml_lint/rake_task' require 'haml_lint/inline_javascript' - # Workaround for warnings from parser/current - # TODO: Remove this after we update parser gem - task :haml_lint do - require 'parser' - def Parser.warn(*args) - puts(*args) # static-analysis ignores stdout if status is 0 - end - end - HamlLint::RakeTask.new end |
