diff options
author | Patrick Steinhardt <psteinhardt@gitlab.com> | 2022-11-02 16:01:28 +0300 |
---|---|---|
committer | Patrick Steinhardt <psteinhardt@gitlab.com> | 2022-11-02 16:01:28 +0300 |
commit | 04667f8e0c885d87da491e179cd0283bc3b966c9 (patch) | |
tree | 24cd9fdb3ea804f9c64f955ee37efecb2436d378 /ruby | |
parent | bd8c3c94e2d9eb61ffedd91ea34902a9e89e9448 (diff) | |
parent | 0d2baa826945678103c49f76645e276e3cd1633b (diff) |
Merge branch 'qmnguyen0711/remove-rugged-in-gitaly' into 'master'
Remove rugged wrappers in Gitaly
See merge request https://gitlab.com/gitlab-org/gitaly/-/merge_requests/4992
Merged-by: Patrick Steinhardt <psteinhardt@gitlab.com>
Approved-by: Patrick Steinhardt <psteinhardt@gitlab.com>
Approved-by: Toon Claes <toon@gitlab.com>
Co-authored-by: Quang-Minh Nguyen <qmnguyen@gitlab.com>
Diffstat (limited to 'ruby')
38 files changed, 16 insertions, 2446 deletions
diff --git a/ruby/.rubocop_todo.yml b/ruby/.rubocop_todo.yml index 87453d9e6..b0e943dcb 100644 --- a/ruby/.rubocop_todo.yml +++ b/ruby/.rubocop_todo.yml @@ -219,13 +219,6 @@ Style/RaiseArgs: - 'lib/gitlab/git/operation_service.rb' - 'lib/gitlab/git/remote_repository.rb' -# Offense count: 5 -# Cop supports --auto-correct. -Style/RedundantSelf: - Exclude: - - 'lib/gitlab/git.rb' - - 'lib/gitlab/git/blob.rb' - # Offense count: 14 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. diff --git a/ruby/Gemfile b/ruby/Gemfile index 91c724113..89bafb90f 100644 --- a/ruby/Gemfile +++ b/ruby/Gemfile @@ -4,10 +4,8 @@ gem 'rugged', '~> 1.2' gem 'github-linguist', '~> 7.20.0', require: 'linguist' gem 'gitlab-markup', '~> 1.7.1' gem 'activesupport', '~> 6.1.6.1' -gem 'rdoc', '~> 6.0' gem 'grpc', '~> 1.42.0' # keep in lock-step with grpc-tools gem 'sentry-raven', '~> 3.0', require: false -gem 'faraday', '~> 1.0' gem 'rbtrace', require: false # The Gitaly Gem contains the Protobuf and gRPC definitions required by the @@ -23,11 +21,6 @@ gem 'licensee', '~> 9.15' gem 'google-protobuf', '~> 3.21.9' -# Rails is currently blocked on the upgrade to the new major version for Redis, -# so we don't upgrade either until the issue is resolved. This is an indirect -# dependency and can thus be removed when the version constraint is gone. -gem 'redis', '~> 4.7.1' - group :development, :test do gem 'rubocop', '~> 0.69', require: false gem 'rspec', require: false diff --git a/ruby/Gemfile.lock b/ruby/Gemfile.lock index ccd124f08..9c80b436f 100644 --- a/ruby/Gemfile.lock +++ b/ruby/Gemfile.lock @@ -134,7 +134,6 @@ GEM ffi (>= 1.0.6) msgpack (>= 0.4.3) optimist (>= 3.0.0) - rdoc (6.3.2) redis (4.7.1) regexp_parser (1.8.1) reverse_markdown (1.4.0) @@ -205,7 +204,6 @@ PLATFORMS DEPENDENCIES activesupport (~> 6.1.6.1) factory_bot - faraday (~> 1.0) gitaly (~> 15.5.0) github-linguist (~> 7.20.0) gitlab-labkit (~> 0.28) @@ -216,8 +214,6 @@ DEPENDENCIES licensee (~> 9.15) pry (~> 0.12.2) rbtrace - rdoc (~> 6.0) - redis (~> 4.7.1) rspec rspec-parameterized rubocop (~> 0.69) diff --git a/ruby/bin/gitaly-ruby b/ruby/bin/gitaly-ruby index 5b5f537a6..11cfa231b 100755 --- a/ruby/bin/gitaly-ruby +++ b/ruby/bin/gitaly-ruby @@ -20,7 +20,6 @@ require 'rugged' require_relative '../lib/gitaly_server.rb' require_relative '../lib/gitaly_server/sentry_interceptor.rb' require_relative '../lib/gitaly_server/exception_sanitizer_interceptor.rb' -require_relative '../lib/gitaly_server/rugged_interceptor.rb' SHUTDOWN_TIMEOUT = 600 @@ -132,7 +131,6 @@ def build_server_interceptor_chain chain << GitalyServer::SentryInterceptor.new chain << Labkit::Tracing::GRPC::ServerInterceptor.new if Labkit::Tracing.enabled? chain << GitalyServer::ExceptionSanitizerInterceptor.new - chain << GitalyServer::RuggedInterceptor.new chain end diff --git a/ruby/lib/gitaly_server.rb b/ruby/lib/gitaly_server.rb index dcd54dfd8..17f303ac0 100644 --- a/ruby/lib/gitaly_server.rb +++ b/ruby/lib/gitaly_server.rb @@ -1,45 +1,32 @@ $:.unshift(File.expand_path('../proto', __dir__)) require 'gitaly' -require_relative 'gitlab/git.rb' +require 'linguist/blob_helper' +require 'securerandom' +require 'gitlab-labkit' +require 'rugged' + +require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/numeric/bytes' +require 'active_support/core_ext/numeric/time' +require 'active_support/core_ext/integer/time' +require 'active_support/core_ext/module/delegation' +require 'active_support/core_ext/enumerable' + +require_relative './gitlab/config.rb' -require_relative 'gitaly_server/client.rb' require_relative 'gitaly_server/utils.rb' require_relative 'gitaly_server/repository_service.rb' require_relative 'gitaly_server/health_service.rb' -require_relative 'gitaly_server/feature_flags.rb' module GitalyServer - STORAGE_PATH_HEADER = 'gitaly-storage-path'.freeze REPO_PATH_HEADER = 'gitaly-repo-path'.freeze - GL_REPOSITORY_HEADER = 'gitaly-gl-repository'.freeze - REPO_ALT_DIRS_HEADER = 'gitaly-repo-alt-dirs'.freeze GITALY_SERVERS_HEADER = 'gitaly-servers'.freeze - def self.storage_path(call) - call.metadata.fetch(STORAGE_PATH_HEADER) - end - def self.repo_path(call) call.metadata.fetch(REPO_PATH_HEADER) end - def self.gl_repository(call) - call.metadata.fetch(GL_REPOSITORY_HEADER) - end - - def self.repo_alt_dirs(call) - call.metadata.fetch(REPO_ALT_DIRS_HEADER) - end - - def self.feature_flags(call) - FeatureFlags.new(call.metadata) - end - - def self.client(call) - Client.new(call.metadata[GITALY_SERVERS_HEADER]) - end - def self.register_handlers(server) server.handle(RepositoryService.new) server.handle(HealthService.new) diff --git a/ruby/lib/gitaly_server/client.rb b/ruby/lib/gitaly_server/client.rb deleted file mode 100644 index 49fab89ce..000000000 --- a/ruby/lib/gitaly_server/client.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'base64' -require 'json' - -module GitalyServer - class Client - ServerLookupError = Class.new(StandardError) - - def initialize(encoded_servers) - @servers = encoded_servers.present? ? JSON.parse(Base64.strict_decode64(encoded_servers)) : {} - end - - def shared_secret(storage) - server(storage)['token'] - end - - def address(storage) - server(storage)['address'] - end - - private - - def server(storage) - raise ServerLookupError.new("cannot find gitaly address for storage #{storage.inspect}") unless @servers.has_key?(storage) - - @servers[storage] - end - end -end diff --git a/ruby/lib/gitaly_server/feature_flags.rb b/ruby/lib/gitaly_server/feature_flags.rb deleted file mode 100644 index da6718a84..000000000 --- a/ruby/lib/gitaly_server/feature_flags.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -module GitalyServer - # Interface to Ruby-specific feature flags passed to the Gitaly Ruby server - # via headers. - class FeatureFlags - # Only headers prefixed with this String will be made available - HEADER_PREFIX = 'gitaly-feature-ruby-' - - def initialize(metadata) - @flags = metadata.select do |key, _| - key.start_with?(HEADER_PREFIX) - end - end - - # Check if a given flag is enabled - # - # The `gitaly-feature-ruby-` prefix is optional, and underscores are - # translated to hyphens automatically. - # - # Examples - # - # enabled?('gitaly-feature-ruby-my-flag') - # => true - # - # enabled?(:my_flag) - # => true - # - # enabled?('my-flag') - # => true - # - # enabled?(:unknown_flag) - # => false - def enabled?(flag, on_by_default: false) - flag = normalize_flag(flag) - - @flags.fetch(flag, on_by_default.to_s) == 'true' - end - - def disabled?(flag, on_by_default: false) - !enabled?(flag, on_by_default: on_by_default) - end - - def inspect - pairs = @flags.map { |name, value| "#{name}=#{value}" } - pairs.unshift(self.class.name) - - "#<#{pairs.join(' ')}>" - end - - private - - def normalize_flag(flag) - flag = flag.to_s.delete_prefix(HEADER_PREFIX).tr('_', '-') - - "#{HEADER_PREFIX}#{flag}" - end - end -end diff --git a/ruby/lib/gitaly_server/repository_service.rb b/ruby/lib/gitaly_server/repository_service.rb index 98cb8b748..9cb39fd5d 100644 --- a/ruby/lib/gitaly_server/repository_service.rb +++ b/ruby/lib/gitaly_server/repository_service.rb @@ -4,11 +4,11 @@ module GitalyServer class RepositoryService < Gitaly::RepositoryService::Service include Utils - def find_license(request, call) - repo = Gitlab::Git::Repository.from_gitaly(request.repository, call) + def find_license(_request, call) + path = GitalyServer.repo_path(call) begin - project = ::Licensee.project(repo.path) + project = ::Licensee.project(path) return Gitaly::FindLicenseResponse.new(license_short_name: "") unless project&.license license = project.license diff --git a/ruby/lib/gitaly_server/rugged_interceptor.rb b/ruby/lib/gitaly_server/rugged_interceptor.rb deleted file mode 100644 index 72af71f11..000000000 --- a/ruby/lib/gitaly_server/rugged_interceptor.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -require 'grpc' - -module GitalyServer - class RuggedInterceptor < GRPC::ServerInterceptor - # Intercept a unary request response call - %i[request_response server_streamer client_streamer bidi_streamer].each do |meth| - define_method(meth) do |**, &blk| - init_rugged_reference_list - - blk.call - - cleanup_rugged_references - end - end - - def init_rugged_reference_list - Thread.current[::Gitlab::Git::Repository::RUGGED_KEY] = [] - end - - def cleanup_rugged_references - repos = Thread.current[::Gitlab::Git::Repository::RUGGED_KEY] - repos.compact.map(&:close) - end - end -end diff --git a/ruby/lib/gitlab/encoding_helper.rb b/ruby/lib/gitlab/encoding_helper.rb deleted file mode 100644 index 039b0cda4..000000000 --- a/ruby/lib/gitlab/encoding_helper.rb +++ /dev/null @@ -1,98 +0,0 @@ -module Gitlab - module EncodingHelper - extend self - - # This threshold is carefully tweaked to prevent usage of encodings detected - # by CharlockHolmes with low confidence. If CharlockHolmes confidence is low, - # we're better off sticking with utf8 encoding. - # Reason: git diff can return strings with invalid utf8 byte sequences if it - # truncates a diff in the middle of a multibyte character. In this case - # CharlockHolmes will try to guess the encoding and will likely suggest an - # obscure encoding with low confidence. - # There is a lot more info with this merge request: - # https://gitlab.com/gitlab-org/gitlab_git/merge_requests/77#note_4754193 - ENCODING_CONFIDENCE_THRESHOLD = 50 - - def encode!(message) - message = force_encode_utf8(message) - return message if message.valid_encoding? - - # return message if message type is binary - detect = CharlockHolmes::EncodingDetector.detect(message) - return message.force_encoding("BINARY") if detect_binary?(message, detect) - - if detect && detect[:encoding] && detect[:confidence] > ENCODING_CONFIDENCE_THRESHOLD - # force detected encoding if we have sufficient confidence. - message.force_encoding(detect[:encoding]) - end - - # encode and clean the bad chars - message.replace clean(message) - rescue ArgumentError => e - return unless e.message.include?('unknown encoding name') - - encoding = detect ? detect[:encoding] : "unknown" - "--broken encoding: #{encoding}" - end - - def detect_binary?(data, detect = nil) - detect ||= CharlockHolmes::EncodingDetector.detect(data) - detect && detect[:type] == :binary && detect[:confidence] == 100 - end - - def detect_libgit2_binary?(data) - # EncodingDetector checks the first 1024 * 1024 bytes for NUL byte, libgit2 checks - # only the first 8000 (https://github.com/libgit2/libgit2/blob/2ed855a9e8f9af211e7274021c2264e600c0f86b/src/filter.h#L15), - # which is what we use below to keep a consistent behavior. - detect = CharlockHolmes::EncodingDetector.new(8000).detect(data) - detect && detect[:type] == :binary - end - - def encode_utf8(message) - message = force_encode_utf8(message) - return message if message.valid_encoding? - - detect = CharlockHolmes::EncodingDetector.detect(message) - if detect && detect[:encoding] - begin - CharlockHolmes::Converter.convert(message, detect[:encoding], 'UTF-8') - rescue ArgumentError => e - Rails.logger.warn("Ignoring error converting #{detect[:encoding]} into UTF8: #{e.message}") - - '' - end - else - clean(message) - end - rescue ArgumentError - nil - end - - def encode_binary(str) - return "" if str.nil? - - str.dup.force_encoding(Encoding::ASCII_8BIT) - end - - def binary_stringio(str) - StringIO.new(str.freeze || '').tap { |io| io.set_encoding(Encoding::ASCII_8BIT) } - end - - private - - def force_encode_utf8(message) - raise ArgumentError unless message.respond_to?(:force_encoding) - return message if message.encoding == Encoding::UTF_8 && message.valid_encoding? - - message = message.dup if message.respond_to?(:frozen?) && message.frozen? - - message.force_encoding("UTF-8") - end - - def clean(message) - message.encode("UTF-16BE", undef: :replace, invalid: :replace, replace: "".encode("UTF-16BE")) - .encode("UTF-8") - .gsub("\0".encode("UTF-8"), "") - end - end -end diff --git a/ruby/lib/gitlab/git.rb b/ruby/lib/gitlab/git.rb deleted file mode 100644 index 1f99646ee..000000000 --- a/ruby/lib/gitlab/git.rb +++ /dev/null @@ -1,99 +0,0 @@ -# External dependencies of Gitlab::Git -require 'rugged' -require 'linguist/blob_helper' -require 'securerandom' -require 'gitlab-labkit' - -# Ruby on Rails mix-ins that GitLab::Git code relies on -require 'active_support/core_ext/object/blank' -require 'active_support/core_ext/numeric/bytes' -require 'active_support/core_ext/numeric/time' -require 'active_support/core_ext/integer/time' -require 'active_support/core_ext/module/delegation' -require 'active_support/core_ext/enumerable' - -require_relative 'git_logger.rb' -require_relative 'rails_logger.rb' -require_relative 'config.rb' - -dir = __dir__ - -# Some later requires are order-sensitive. Manually require whatever we need. -require_relative "#{dir}/encoding_helper.rb" -require_relative "#{dir}/utils/strong_memoize.rb" -require_relative "#{dir}/git/popen.rb" - -# Require all .rb files we can find in the gitlab lib directory -Dir["#{dir}/**/*.rb"].sort.each do |ruby_file| - require File.expand_path(ruby_file) -end - -class String - # Because we are not rendering HTML, this is a no-op in gitaly-ruby. - def html_safe - self - end -end - -module Gitlab - module Git - # The ID of empty tree. - # See http://stackoverflow.com/a/40884093/1856239 and - # https://github.com/git/git/blob/3ad8b5bf26362ac67c9020bf8c30eee54a84f56d/cache.h#L1011-L1012 - EMPTY_TREE_ID = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'.freeze - BLANK_SHA = ('0' * 40).freeze - TAG_REF_PREFIX = "refs/tags/".freeze - BRANCH_REF_PREFIX = "refs/heads/".freeze - - BaseError = Class.new(StandardError) - CommandError = Class.new(BaseError) - CommitError = Class.new(BaseError) - PreReceiveError = Class.new(BaseError) - - class << self - include Gitlab::EncodingHelper - - def ref_name(ref) - encode!(ref).sub(%r{\Arefs/(tags|heads|remotes)/}, '') - end - - def branch_name(ref) - ref = ref.to_s - self.ref_name(ref) if self.branch_ref?(ref) - end - - def tag_name(ref) - ref = ref.to_s - self.ref_name(ref) if self.tag_ref?(ref) - end - - def tag_ref?(ref) - ref.start_with?(TAG_REF_PREFIX) - end - - def branch_ref?(ref) - ref.start_with?(BRANCH_REF_PREFIX) - end - - def blank_ref?(ref) - ref == BLANK_SHA - end - - def version - Gitlab::Git::Version.git_version - end - end - end -end - -module Gitlab - module GlId - def self.gl_id(user) - user.gl_id - end - - def self.gl_id_from_id_value(id) - "user-#{id}" - end - end -end diff --git a/ruby/lib/gitlab/git/branch.rb b/ruby/lib/gitlab/git/branch.rb deleted file mode 100644 index 9618b5fef..000000000 --- a/ruby/lib/gitlab/git/branch.rb +++ /dev/null @@ -1,11 +0,0 @@ -require_relative 'ref' - -module Gitlab - module Git - class Branch < Ref - def initialize(repository, name, target, target_commit) - super(repository, name, target, target_commit) - end - end - end -end diff --git a/ruby/lib/gitlab/git/commit.rb b/ruby/lib/gitlab/git/commit.rb deleted file mode 100644 index 5235b1c46..000000000 --- a/ruby/lib/gitlab/git/commit.rb +++ /dev/null @@ -1,250 +0,0 @@ -module Gitlab - module Git - class Commit - include Gitlab::EncodingHelper - - attr_accessor :raw_commit, :head - - MAX_COMMIT_MESSAGE_DISPLAY_SIZE = 10.megabytes - MIN_SHA_LENGTH = 7 - SERIALIZE_KEYS = %i[ - id message parent_ids - authored_date author_name author_email - committed_date committer_name committer_email trailers - ].freeze - - attr_accessor *SERIALIZE_KEYS # rubocop:disable Lint/AmbiguousOperator - - def ==(other) - return false unless other.is_a?(Gitlab::Git::Commit) - - id && id == other.id - end - - class << self - # Get single commit - # - # Ex. - # Commit.find(repo, '29eda46b') - # - # Commit.find(repo, 'master') - # - def find(repo, commit_id = "HEAD") - # Already a commit? - return commit_id if commit_id.is_a?(Gitlab::Git::Commit) - - # A rugged reference? - commit_id = Gitlab::Git::Ref.dereference_object(commit_id) - return decorate(repo, commit_id) if commit_id.is_a?(Rugged::Commit) - - # Some weird thing? - return nil unless commit_id.is_a?(String) - - # This saves us an RPC round trip. - return nil if commit_id.include?(':') - - commit = rugged_find(repo, commit_id) - - decorate(repo, commit) if commit - rescue Rugged::ReferenceError, Rugged::InvalidError, Rugged::ObjectError, - Gitlab::Git::CommandError, Gitlab::Git::Repository::NoRepository, - Rugged::OdbError, Rugged::TreeError, ArgumentError - nil - end - - def rugged_find(repo, commit_id) - obj = repo.rev_parse_target(commit_id) - - obj.is_a?(Rugged::Commit) ? obj : nil - end - - def decorate(repository, commit, ref = nil) - Gitlab::Git::Commit.new(repository, commit, ref) - end - - def shas_with_signatures(repository, shas) - shas.select do |sha| - begin - Rugged::Commit.extract_signature(repository.rugged, sha) - rescue Rugged::OdbError - false - end - end - end - end - - def initialize(repository, raw_commit, head = nil) - raise "Nil as raw commit passed" unless raw_commit - - @repository = repository - @head = head - - case raw_commit - when Hash - init_from_hash(raw_commit) - when Rugged::Commit - init_from_rugged(raw_commit) - when Gitaly::GitCommit - init_from_gitaly(raw_commit) - else - raise "Invalid raw commit type: #{raw_commit.class}" - end - end - - def sha - id - end - - def short_id(length = 10) - id.to_s[0..length] - end - - def safe_message - @safe_message ||= message - end - - def no_commit_message - "--no commit message" - end - - def to_hash - serialize_keys.map.with_object({}) do |key, hash| - hash[key] = send(key) - end - end - - def date - committed_date - end - - def parents - parent_ids.map { |oid| self.class.find(@repository, oid) }.compact - end - - def message - encode! @message - end - - def author_name - encode! @author_name - end - - def author_email - encode! @author_email - end - - def committer_name - encode! @committer_name - end - - def committer_email - encode! @committer_email - end - - def rugged_commit - @rugged_commit ||= if raw_commit.is_a?(Rugged::Commit) - raw_commit - else - @repository.rev_parse_target(id) - end - end - - def merge_commit? - parent_ids.size > 1 - end - - def to_gitaly_commit - return raw_commit if raw_commit.is_a?(Gitaly::GitCommit) - - message_split = raw_commit.message.split("\n", 2) - Gitaly::GitCommit.new( - id: raw_commit.oid, - subject: message_split[0] ? message_split[0].chomp.b : "", - body: raw_commit.message.b, - parent_ids: raw_commit.parent_ids, - author: gitaly_commit_author_from_rugged(raw_commit.author), - committer: gitaly_commit_author_from_rugged(raw_commit.committer), - trailers: gitaly_trailers_from_rugged(raw_commit) - ) - end - - private - - def init_from_hash(hash) - raw_commit = hash.symbolize_keys - - serialize_keys.each do |key| - send("#{key}=", raw_commit[key]) - end - end - - def init_from_rugged(commit) - author = commit.author - committer = commit.committer - - @raw_commit = commit - @id = commit.oid - @message = commit.message - @authored_date = author[:time] - @committed_date = committer[:time] - @author_name = author[:name] - @author_email = author[:email] - @committer_name = committer[:name] - @committer_email = committer[:email] - @parent_ids = commit.parents.map(&:oid) - @trailers = Hash[commit.trailers] - end - - def init_from_gitaly(commit) - @raw_commit = commit - @id = commit.id - # TODO: Once gitaly "takes over" Rugged consider separating the - # subject from the message to make it clearer when there's one - # available but not the other. - @message = message_from_gitaly_body - @authored_date = init_date_from_gitaly(commit.author) - @author_name = commit.author.name.dup - @author_email = commit.author.email.dup - @committed_date = init_date_from_gitaly(commit.committer) - @committer_name = commit.committer.name.dup - @committer_email = commit.committer.email.dup - @parent_ids = Array(commit.parent_ids) - @trailers = Hash[commit.trailers.map { |t| [t.key, t.value] }] - end - - # Gitaly provides a UNIX timestamp in author.date.seconds, and a timezone - # offset in author.timezone. If the latter isn't present, assume UTC. - def init_date_from_gitaly(author) - if author.timezone.present? - Time.strptime("#{author.date.seconds} #{author.timezone}", '%s %z') - else - Time.at(author.date.seconds).utc - end - end - - def serialize_keys - SERIALIZE_KEYS - end - - def gitaly_commit_author_from_rugged(author_or_committer) - Gitaly::CommitAuthor.new( - name: author_or_committer[:name].b, - email: author_or_committer[:email].b, - date: Google::Protobuf::Timestamp.new(seconds: author_or_committer[:time].to_i) - ) - end - - def gitaly_trailers_from_rugged(rugged_commit) - rugged_commit.trailers.map do |(key, value)| - Gitaly::CommitTrailer.new(key: key, value: value) - end - end - - def message_from_gitaly_body - return @raw_commit.subject.dup if @raw_commit.body_size.zero? - - @raw_commit.body.dup - end - end - end -end diff --git a/ruby/lib/gitlab/git/hook.rb b/ruby/lib/gitlab/git/hook.rb deleted file mode 100644 index 8084cd74c..000000000 --- a/ruby/lib/gitlab/git/hook.rb +++ /dev/null @@ -1,141 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Git - class Hook - def self.directory - Gitlab.config.git.hooks_directory - end - - GL_PROTOCOL = 'web' - attr_reader :name, :path, :repository - - def initialize(name, repository) - @name = name - @repository = repository - @path = File.join(self.class.directory, name) - end - - def repo_path - repository.path - end - - def exists? - File.exist?(path) - end - - def trigger(gl_id, gl_username, oldrev, newrev, ref, push_options: nil, transaction: nil) - return [true, nil] unless exists? - - Bundler.with_unbundled_env do - case name - when "pre-receive", "post-receive" - call_receive_hook(gl_id, gl_username, oldrev, newrev, ref, push_options, transaction) - when "reference-transaction" - call_reference_transaction_hook(gl_id, gl_username, oldrev, newrev, ref, transaction) - when "update" - call_update_hook(gl_id, gl_username, oldrev, newrev, ref, transaction) - end - end - end - - private - - def call_stdin_hook(args, input, env) - exit_status = false - exit_message = nil - - options = { - chdir: repo_path - } - - Open3.popen3(env, path, *args, options) do |stdin, stdout, stderr, wait_thr| - exit_status = true - stdin.sync = true - - # in git, hooks may just exit without reading stdin. We catch the - # exception to avoid a broken pipe warning - begin - input.lines do |line| - stdin.puts line - end - rescue Errno::EPIPE - end - - stdin.close - - unless wait_thr.value == 0 - exit_status = false - exit_message = retrieve_error_message(stderr, stdout) - end - end - - [exit_status, exit_message] - end - - def call_receive_hook(gl_id, gl_username, oldrev, newrev, ref, push_options, transaction) - changes = [oldrev, newrev, ref].join(" ") - - vars = env_base_vars(gl_id, gl_username, transaction) - vars.merge!(push_options.env_data) if push_options - - call_stdin_hook([], changes, vars) - end - - def call_reference_transaction_hook(gl_id, gl_username, oldrev, newrev, ref, transaction) - changes = [oldrev, newrev, ref].join(" ") - - vars = env_base_vars(gl_id, gl_username, transaction) - - call_stdin_hook(["prepared"], changes, vars) - end - - def call_update_hook(gl_id, gl_username, oldrev, newrev, ref, transaction) - options = { - chdir: repo_path - } - - args = [ref, oldrev, newrev] - - vars = env_base_vars(gl_id, gl_username, transaction) - - stdout, stderr, status = Open3.capture3(vars, path, *args, options) - [status.success?, stderr.presence || stdout] - end - - def retrieve_error_message(stderr, stdout) - err_message = stderr.read - err_message = err_message.blank? ? stdout.read : err_message - err_message - end - - def hooks_payload(gl_id, gl_username, transaction) - payload = { - repository: repository.gitaly_repository.to_json, - binary_directory: Gitlab.config.gitaly.bin_dir, - git_path: Gitlab.config.git.bin_path, - internal_socket: Gitlab.config.gitaly.internal_socket, - internal_socket_token: ENV['GITALY_TOKEN'], - user_details: { - userid: gl_id, - username: gl_username, - protocol: GL_PROTOCOL - } - } - - payload.merge!(transaction.payload) if transaction - - Base64.strict_encode64(payload.to_json) - end - - def env_base_vars(gl_id, gl_username, transaction = nil) - { - 'GITALY_HOOKS_PAYLOAD' => hooks_payload(gl_id, gl_username, transaction), - 'GITALY_LOG_DIR' => Gitlab.config.logging.dir, - 'PWD' => repo_path, - 'GIT_DIR' => repo_path - } - end - end - end -end diff --git a/ruby/lib/gitlab/git/hooks_service.rb b/ruby/lib/gitlab/git/hooks_service.rb deleted file mode 100644 index baccaa5c0..000000000 --- a/ruby/lib/gitlab/git/hooks_service.rb +++ /dev/null @@ -1,40 +0,0 @@ -module Gitlab - module Git - class HooksService - attr_accessor :oldrev, :newrev, :ref - - def execute(pusher, repository, oldrev, newrev, ref, push_options:, transaction: nil) - @repository = repository - @gl_id = pusher.gl_id - @gl_username = pusher.username - @oldrev = oldrev - @newrev = newrev - @ref = ref - @push_options = push_options - @transaction = transaction - - %w[pre-receive update reference-transaction].each do |hook_name| - status, message = run_hook(hook_name) - - raise PreReceiveError, message unless status - end - - yield(self).tap do - status, message = run_hook('reference-transaction') - Gitlab::GitLogger.error("reference-transaction committed hook: #{message}") unless status - - status, message = run_hook('post-receive') - - Gitlab::GitLogger.error("post-receive hook: #{message}") unless status - end - end - - private - - def run_hook(name) - hook = Gitlab::Git::Hook.new(name, @repository) - hook.trigger(@gl_id, @gl_username, oldrev, newrev, ref, push_options: @push_options, transaction: @transaction) - end - end - end -end diff --git a/ruby/lib/gitlab/git/operation_service.rb b/ruby/lib/gitlab/git/operation_service.rb deleted file mode 100644 index 902a707ad..000000000 --- a/ruby/lib/gitlab/git/operation_service.rb +++ /dev/null @@ -1,121 +0,0 @@ -module Gitlab - module Git - class OperationService - include Gitlab::Git::Popen - - BranchUpdate = Struct.new(:newrev, :repo_created, :branch_created) do - alias_method :repo_created?, :repo_created - alias_method :branch_created?, :branch_created - - def self.from_gitaly(branch_update) - return if branch_update.nil? - - new( - branch_update.commit_id, - branch_update.repo_created, - branch_update.branch_created - ) - end - end - - attr_reader :user, :repository - - def initialize(user, new_repository) - @user = user - @repository = new_repository - end - - # Execute the block with the tip commit referenced by the given branch. - # The branch must exist before calling this function. - def with_branch(branch_name, &block) - if !repository.empty? && !repository.branch_exists?(branch_name) - raise ArgumentError, "Cannot find branch '#{branch_name}'" - end - - update_branch_with_hooks(branch_name) do - repository.with_repo_branch_commit(branch_name, &block) - end - end - - private - - # Returns [newrev, should_run_after_create, should_run_after_create_branch] - def update_branch_with_hooks(branch_name) - was_empty = repository.empty? - - # Make commit - newrev = yield - - raise Gitlab::Git::CommitError.new('Failed to create commit') unless newrev - - branch = repository.find_branch(branch_name) - oldrev = find_oldrev_from_branch(newrev, branch) - - ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name - update_ref_in_hooks(ref, newrev, oldrev) - - BranchUpdate.new(newrev, was_empty, was_empty || Gitlab::Git.blank_ref?(oldrev)) - end - - def find_oldrev_from_branch(newrev, branch) - return Gitlab::Git::BLANK_SHA unless branch - - oldrev = branch.target - - merge_base = repository.merge_base(newrev, branch.target) - raise Gitlab::Git::Repository::InvalidRef unless merge_base - - if oldrev == merge_base - oldrev - else - raise Gitlab::Git::CommitError.new('Branch diverged') - end - end - - def update_ref_in_hooks(ref, newrev, oldrev, push_options: nil, transaction: nil) - with_hooks(ref, newrev, oldrev, push_options: push_options, transaction: transaction) do - update_ref(ref, newrev, oldrev) - end - end - - def with_hooks(ref, newrev, oldrev, push_options: nil, transaction: nil) - Gitlab::Git::HooksService.new.execute( - user, - repository, - oldrev, - newrev, - ref, - push_options: push_options, - transaction: transaction - ) do |service| - yield(service) - end - end - - def update_ref(ref, newrev, oldrev) - # We use 'git update-ref' because libgit2/rugged currently does not - # offer 'compare and swap' ref updates. Without compare-and-swap we can - # (and have!) accidentally reset the ref to an earlier state, clobbering - # commits. See also https://github.com/libgit2/libgit2/issues/1534. - command = %W[#{Gitlab.config.git.bin_path} update-ref --stdin -z] - - output, status = popen( - command, - repository.path - ) do |stdin| - stdin.write("update #{ref}\x00#{newrev}\x00#{oldrev}\x00") - end - - unless status.zero? - Gitlab::GitLogger.error("'git update-ref' in #{repository.path}: #{output}") - ref_name = Gitlab::Git.branch_name(ref) || ref - - raise Gitlab::Git::CommitError.new( - "Could not update #{ref_name}." \ - " Please refresh and try again." - ) - end - end - end - end -end diff --git a/ruby/lib/gitlab/git/popen.rb b/ruby/lib/gitlab/git/popen.rb deleted file mode 100644 index 5d8218eb1..000000000 --- a/ruby/lib/gitlab/git/popen.rb +++ /dev/null @@ -1,51 +0,0 @@ -require 'open3' - -module Gitlab - module Git - module Popen - FAST_GIT_PROCESS_TIMEOUT = 15.seconds - - def popen(cmd, path, vars = {}, include_stderr: true, lazy_block: nil) - raise "System commands must be given as an array of strings" unless cmd.is_a?(Array) - - path ||= Dir.pwd - vars['PWD'] = path - options = { chdir: path } - - cmd_output = "" - cmd_status = 0 - Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr| - stdout.set_encoding(Encoding::ASCII_8BIT) - stderr.set_encoding(Encoding::ASCII_8BIT) - - # stderr and stdout pipes can block if stderr/stdout aren't drained: https://bugs.ruby-lang.org/issues/9082 - # Mimic what Ruby does with capture3: https://github.com/ruby/ruby/blob/1ec544695fa02d714180ef9c34e755027b6a2103/lib/open3.rb#L257-L273 - err_reader = Thread.new { stderr.read } - - begin - yield(stdin) if block_given? - stdin.close - - if lazy_block - cmd_output = lazy_block.call(stdout.lazy) - cmd_status = 0 - break - else - cmd_output << stdout.read - end - - cmd_output << err_reader.value if include_stderr - cmd_status = wait_thr.value.exitstatus - ensure - # When Popen3.open3 returns, the stderr reader gets closed, which causes - # an exception in the err_reader thread. Kill the thread before - # returning from Popen3.open3. - err_reader.kill - end - end - - [cmd_output, cmd_status] - end - end - end -end diff --git a/ruby/lib/gitlab/git/push_options.rb b/ruby/lib/gitlab/git/push_options.rb deleted file mode 100644 index a3829cc9d..000000000 --- a/ruby/lib/gitlab/git/push_options.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Git - class PushOptions - attr_accessor :options - - def initialize(options) - @options = options - end - - def env_data - return {} if options.empty? - - data = { - 'GIT_PUSH_OPTION_COUNT' => options.count.to_s - } - - options.each_with_index do |opt, index| - data["GIT_PUSH_OPTION_#{index}"] = opt - end - - data - end - end - end -end diff --git a/ruby/lib/gitlab/git/ref.rb b/ruby/lib/gitlab/git/ref.rb deleted file mode 100644 index e45bec404..000000000 --- a/ruby/lib/gitlab/git/ref.rb +++ /dev/null @@ -1,53 +0,0 @@ -# Gitaly note: JV: probably no RPC's here (just one interaction with Rugged). - -module Gitlab - module Git - class Ref - include Gitlab::EncodingHelper - - # Canonical refname, including `refs/heads|tags/` prefix - attr_reader :refname - - # Branch or tag name - # without "refs/tags|heads" prefix - attr_reader :name - - # Target sha. - # Usually it is commit sha but in case - # when tag reference on other tag it can be tag sha - attr_reader :target - - # Dereferenced target - # Commit object to which the Ref points to - attr_reader :dereferenced_target - - # Extract branch name from full ref path - # - # Ex. - # Ref.extract_branch_name('refs/heads/master') #=> 'master' - def self.extract_branch_name(str) - str.gsub(%r{\Arefs/heads/}, '') - end - - # Gitaly: this method will probably be migrated indirectly via its call sites. - def self.dereference_object(object) - object = object.target while object.is_a?(Rugged::Tag::Annotation) - - object - end - - def initialize(_repository, name, target, dereferenced_target) - @refname = name - @name = Gitlab::Git.ref_name(name) - @dereferenced_target = dereferenced_target - @target = if target.respond_to?(:oid) - target.oid - elsif target.respond_to?(:name) - target.name - elsif target.is_a? String - target - end - end - end - end -end diff --git a/ruby/lib/gitlab/git/repository.rb b/ruby/lib/gitlab/git/repository.rb deleted file mode 100644 index cbe5bde19..000000000 --- a/ruby/lib/gitlab/git/repository.rb +++ /dev/null @@ -1,282 +0,0 @@ -require 'securerandom' - -module Gitlab - module Git - # These are monkey patches on top of the vendored version of Repository. - class Repository - include Gitlab::Git::Popen - include Gitlab::EncodingHelper - include Gitlab::Utils::StrongMemoize - - GITALY_INTERNAL_URL = 'ssh://gitaly/internal.git'.freeze - RUGGED_KEY = :rugged_list - GIT_ALLOW_SHA_UPLOAD = 'uploadpack.allowAnySHA1InWant=true'.freeze - - NoRepository = Class.new(StandardError) - InvalidRef = Class.new(StandardError) - GitError = Class.new(StandardError) - - class << self - def from_gitaly(gitaly_repository, call) - new( - gitaly_repository, - GitalyServer.repo_path(call), - GitalyServer.gl_repository(call), - GitalyServer.repo_alt_dirs(call), - GitalyServer.feature_flags(call) - ) - end - - def from_gitaly_with_block(gitaly_repository, call) - repository = from_gitaly(gitaly_repository, call) - - result = yield repository - - repository.cleanup - - result - end - end - - attr_reader :path - - # Directory name of repo - attr_reader :name - - attr_reader :storage, :gl_repository, :gl_project_path, :relative_path - - def initialize(gitaly_repository, path, gl_repository, combined_alt_dirs = "", feature_flags = GitalyServer::FeatureFlags.new({})) - @gitaly_repository = gitaly_repository - - @alternate_object_directories = combined_alt_dirs - .split(File::PATH_SEPARATOR) - .map { |d| File.join(path, d) } - - @storage = gitaly_repository.storage_name - @relative_path = gitaly_repository.relative_path - @path = path - @gl_repository = gl_repository - @gl_project_path = gitaly_repository.gl_project_path - @feature_flags = feature_flags - end - - def ==(other) - [storage, relative_path] == [other.storage, other.relative_path] - end - - attr_reader :gitaly_repository - - attr_reader :alternate_object_directories - - def sort_branches(branches, sort_by) - case sort_by - when 'name' - branches.sort_by(&:name) - when 'updated_desc' - branches.sort do |a, b| - b.dereferenced_target.committed_date <=> a.dereferenced_target.committed_date - end - when 'updated_asc' - branches.sort do |a, b| - a.dereferenced_target.committed_date <=> b.dereferenced_target.committed_date - end - else - branches - end - end - - def exists? - File.exist?(File.join(path, 'refs')) - end - - def root_ref - @root_ref ||= discover_default_branch - end - - def rugged - @rugged ||= begin - # Open in bare mode, for a slight performance gain - # https://github.com/libgit2/rugged/blob/654ff2fe12041e09707ba0647307abcb6348a7fb/ext/rugged/rugged_repo.c#L276-L278 - Rugged::Repository.bare(path, alternates: alternate_object_directories).tap do |repo| - Thread.current[RUGGED_KEY] << repo if Thread.current[RUGGED_KEY] - end - end - rescue Rugged::RepositoryError, Rugged::OSError - raise NoRepository, 'no repository for such path' - end - - def branch_names - branches.map(&:name) - end - - def branches - branches_filter - end - - # Git repository can contains some hidden refs like: - # /refs/notes/* - # /refs/git-as-svn/* - # /refs/pulls/* - # This refs by default not visible in project page and not cloned to client side. - def has_visible_content? - strong_memoize(:has_visible_content) do - branches_filter(filter: :local).any? do |ref| - begin - ref.name && ref.target # ensures the branch is valid - - true - rescue Rugged::ReferenceError - false - end - end - end - end - - def tags - rugged.references.each("refs/tags/*").map do |ref| - message = nil - - if ref.target.is_a?(Rugged::Tag::Annotation) - tag_message = ref.target.message - - message = tag_message.chomp if tag_message.respond_to?(:chomp) - end - - target_commit = Gitlab::Git::Commit.find(self, ref.target) - Gitlab::Git::Tag.new(self, - name: ref.canonical_name, - target: ref.target, - target_commit: target_commit, - message: message) - end.sort_by(&:name) - end - - # Discovers the default branch based on the repository's available branches - # - # - If no branches are present, returns nil - # - If one branch is present, returns its name - # - If two or more branches are present, returns current HEAD or master or first branch - def discover_default_branch - names = branch_names - - return if names.empty? - - return names[0] if names.length == 1 - - if rugged_head - extracted_name = Ref.extract_branch_name(rugged_head.name) - - return extracted_name if names.include?(extracted_name) - end - - if names.include?('master') - 'master' - else - names[0] - end - end - - def with_repo_branch_commit(start_ref) - if empty? - yield nil - else - # Directly return the commit from this repository - yield commit(start_ref) - end - end - - # Directly find a branch with a simple name (e.g. master) - # - # force_reload causes a new Rugged repository to be instantiated - # - # This is to work around a bug in libgit2 that causes in-memory refs to - # be stale/invalid when packed-refs is changed. - # See https://gitlab.com/gitlab-org/gitlab-ce/issues/15392#note_14538333 - def find_branch(name, force_reload = false) - reload_rugged if force_reload - - rugged_ref = rugged.ref("refs/heads/" + name) - if rugged_ref - target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target) - Gitlab::Git::Branch.new(self, rugged_ref.canonical_name, rugged_ref.target, target_commit) - end - end - - # Returns true if the given branch exists - # - # name - The name of the branch as a String. - def branch_exists?(name) - rugged.branches.exists?(name) - - # If the branch name is invalid (e.g. ".foo") Rugged will raise an error. - # Whatever code calls this method shouldn't have to deal with that so - # instead we just return `false` (which is true since a branch doesn't - # exist when it has an invalid name). - rescue Rugged::ReferenceError - false - end - - def merge_base(from, to) - rugged.merge_base(from, to) - rescue Rugged::ReferenceError - nil - end - - # Lookup for rugged object by oid or ref name - def lookup(oid_or_ref_name) - rugged.rev_parse(oid_or_ref_name) - end - - # Return the object that +revspec+ points to. If +revspec+ is an - # annotated tag, then return the tag's target instead. - def rev_parse_target(revspec) - obj = rugged.rev_parse(revspec) - Ref.dereference_object(obj) - end - - def commit(ref = nil) - ref ||= root_ref - Gitlab::Git::Commit.find(self, ref) - end - - def empty? - !has_visible_content? - end - - def cleanup - # Opening a repository may be expensive, and we only need to close it - # if it's been open. - rugged&.close if defined?(@rugged) - end - - def head_symbolic_ref - head = rugged.ref('HEAD') - - return 'main' if head.type != :symbolic - - Ref.extract_branch_name(head.target_id) - end - - private - - def branches_filter(filter: nil, sort_by: nil) - branches = rugged.branches.each(filter).map do |rugged_ref| - begin - target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target) - Gitlab::Git::Branch.new(self, rugged_ref.canonical_name, rugged_ref.target, target_commit) - rescue Rugged::ReferenceError - # Omit invalid branch - end - end.compact - - sort_branches(branches, sort_by) - end - - def rugged_head - rugged.head - rescue Rugged::ReferenceError - nil - end - end - end -end diff --git a/ruby/lib/gitlab/git/tag.rb b/ruby/lib/gitlab/git/tag.rb deleted file mode 100644 index 1d7c8e5d2..000000000 --- a/ruby/lib/gitlab/git/tag.rb +++ /dev/null @@ -1,49 +0,0 @@ -require_relative 'ref' - -module Gitlab - module Git - class Tag < Ref - extend Gitlab::EncodingHelper - - attr_reader :object_sha, :repository - - SERIALIZE_KEYS = %i[name target target_commit message].freeze - - attr_accessor *SERIALIZE_KEYS # rubocop:disable Lint/AmbiguousOperator - - def initialize(repository, raw_tag) - @repository = repository - @raw_tag = raw_tag - - case raw_tag - when Hash - init_from_hash - when Gitaly::Tag - init_from_gitaly - end - - super(repository, name, target, target_commit) - end - - def init_from_hash - raw_tag = @raw_tag.symbolize_keys - - SERIALIZE_KEYS.each do |key| - send("#{key}=", raw_tag[key]) - end - end - - def init_from_gitaly - @name = encode!(@raw_tag.name.dup) - @target = @raw_tag.id - @message = @raw_tag.message.dup - - @target_commit = Gitlab::Git::Commit.decorate(repository, @raw_tag.target_commit) if @raw_tag.target_commit.present? - end - - def message - encode! @message - end - end - end -end diff --git a/ruby/lib/gitlab/git/user.rb b/ruby/lib/gitlab/git/user.rb deleted file mode 100644 index ebb317430..000000000 --- a/ruby/lib/gitlab/git/user.rb +++ /dev/null @@ -1,36 +0,0 @@ -module Gitlab - module Git - class User - attr_reader :username, :name, :email, :gl_id - - def self.from_gitaly(gitaly_user) - new( - gitaly_user.gl_username, - Gitlab::EncodingHelper.encode!(gitaly_user.name), - Gitlab::EncodingHelper.encode!(gitaly_user.email), - gitaly_user.gl_id - ) - end - - def initialize(username, name, email, gl_id) - @username = username - @name = name - @email = email - @gl_id = gl_id - end - - def ==(other) - [username, name, email, gl_id] == [other.username, other.name, other.email, other.gl_id] - end - - def git_env(timestamp = nil) - { - 'GIT_COMMITTER_NAME' => name, - 'GIT_COMMITTER_EMAIL' => email, - 'GIT_COMMITTER_DATE' => timestamp ? "#{timestamp.seconds} +0000" : nil, - 'GL_ID' => Gitlab::GlId.gl_id(self) - }.reject { |_, v| v.nil? } - end - end - end -end diff --git a/ruby/lib/gitlab/git_logger.rb b/ruby/lib/gitlab/git_logger.rb deleted file mode 100644 index 23a163ba6..000000000 --- a/ruby/lib/gitlab/git_logger.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'logger' - -module Gitlab - GitLogger = Logger.new(STDOUT) - GitLogger.progname = 'githost.log' - GitLogger.level = 'info' -end diff --git a/ruby/lib/gitlab/rails_logger.rb b/ruby/lib/gitlab/rails_logger.rb deleted file mode 100644 index 917bbb487..000000000 --- a/ruby/lib/gitlab/rails_logger.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'logger' - -module Rails - LOGGER = Logger.new(STDOUT) - LOGGER.level = 'info' - - def self.logger - LOGGER - end -end diff --git a/ruby/lib/gitlab/utils/strong_memoize.rb b/ruby/lib/gitlab/utils/strong_memoize.rb deleted file mode 100644 index fe091f461..000000000 --- a/ruby/lib/gitlab/utils/strong_memoize.rb +++ /dev/null @@ -1,41 +0,0 @@ -module Gitlab - module Utils - module StrongMemoize - # Instead of writing patterns like this: - # - # def trigger_from_token - # return @trigger if defined?(@trigger) - # - # @trigger = Ci::Trigger.find_by_token(params[:token].to_s) - # end - # - # We could write it like: - # - # include Gitlab::Utils::StrongMemoize - # - # def trigger_from_token - # strong_memoize(:trigger) do - # Ci::Trigger.find_by_token(params[:token].to_s) - # end - # end - # - def strong_memoize(name) - if instance_variable_defined?(ivar(name)) - instance_variable_get(ivar(name)) - else - instance_variable_set(ivar(name), yield) - end - end - - def clear_memoization(name) - remove_instance_variable(ivar(name)) if instance_variable_defined?(ivar(name)) - end - - private - - def ivar(name) - "@#{name}" - end - end - end -end diff --git a/ruby/spec/factories/gitaly/commit_author.rb b/ruby/spec/factories/gitaly/commit_author.rb deleted file mode 100644 index aaf634ce0..000000000 --- a/ruby/spec/factories/gitaly/commit_author.rb +++ /dev/null @@ -1,9 +0,0 @@ -FactoryBot.define do - factory :gitaly_commit_author, class: Gitaly::CommitAuthor do - skip_create - - name { generate(:name) } - email { generate(:email) } - date { Google::Protobuf::Timestamp.new(seconds: Time.now.to_i) } - end -end diff --git a/ruby/spec/factories/sequences.rb b/ruby/spec/factories/sequences.rb deleted file mode 100644 index 16a05b1ca..000000000 --- a/ruby/spec/factories/sequences.rb +++ /dev/null @@ -1,4 +0,0 @@ -FactoryBot.define do - sequence(:name) { |n| "John Doe#{n}" } - sequence(:email) { |n| "user#{n}@example.org" } -end diff --git a/ruby/spec/lib/gitaly_server/feature_flags_spec.rb b/ruby/spec/lib/gitaly_server/feature_flags_spec.rb deleted file mode 100644 index 40e24a024..000000000 --- a/ruby/spec/lib/gitaly_server/feature_flags_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe GitalyServer::FeatureFlags do - describe '#enabled?' do - let(:metadata) do - { - "#{described_class::HEADER_PREFIX}some-feature" => 'true', - 'gitaly-storage-path' => 'foo', - 'gitaly-repo-path' => 'bar' - } - end - - subject { described_class.new(metadata) } - - it 'returns true for an enabled flag' do - expect(subject.enabled?(:some_feature)).to eq(true) - end - - it 'returns true for a missing flag that is on by default' do - expect(subject.enabled?(:feature_default_on, on_by_default: true)).to eq(true) - end - - it 'returns false for an unknown flag' do - expect(subject.enabled?(:missing_feature)).to eq(false) - end - - it 'removes the prefix if provided' do - expect(subject.enabled?(metadata.keys.first)).to eq(true) - end - - it 'translates underscores' do - expect(subject.enabled?('some-feature')).to eq(true) - end - end - - describe '#disabled?' do - it 'is the inverse of `enabled?`' do - instance = described_class.new({}) - - expect(instance).to receive(:enabled?) - .with(:some_feature, on_by_default: false) - .and_return(false) - - expect(instance.disabled?(:some_feature)).to eq(true) - end - end -end diff --git a/ruby/spec/lib/gitaly_server/rugged_interceptor_spec.rb b/ruby/spec/lib/gitaly_server/rugged_interceptor_spec.rb deleted file mode 100644 index 879ba1bae..000000000 --- a/ruby/spec/lib/gitaly_server/rugged_interceptor_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'spec_helper' - -require_relative '../../../lib/gitaly_server/rugged_interceptor.rb' - -describe GitalyServer::RuggedInterceptor do - include TestRepo - - let(:meth) { GitalyServer::RepositoryService.instance_method(:find_license) } - let(:call) { double(metadata: {}) } - - subject do - described_class.new.server_streamer(call: call, method: meth) {} - end - - context 'no Rugged repositories initialized' do - it 'does not clean up any repositories' do - expect(Rugged::Repository).not_to receive(:new) - - subject - end - end - - context 'Rugged repository initialized' do - let(:rugged) { rugged_from_gitaly(test_repo_read_only) } - - let(:streamer) do - described_class.new.server_streamer(call: call, method: meth) do - Thread.current[Gitlab::Git::Repository::RUGGED_KEY] = [rugged] - end - end - - it 'cleans up repositories' do - expect(rugged).to receive(:close).and_call_original - - streamer - end - end -end diff --git a/ruby/spec/lib/gitlab/git/branch_spec.rb b/ruby/spec/lib/gitlab/git/branch_spec.rb deleted file mode 100644 index 3381fc230..000000000 --- a/ruby/spec/lib/gitlab/git/branch_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require "spec_helper" - -describe Gitlab::Git::Branch do - include TestRepo - - let(:repository) { gitlab_git_from_gitaly(git_test_repo_read_only) } - - subject { repository.branches } - - it { is_expected.to be_an(Array) } - - describe '#size' do - subject { super().size } - - it { is_expected.to eq(SeedRepo::Repo::BRANCHES.size) } - end - - it { expect(repository.branches.size).to eq(SeedRepo::Repo::BRANCHES.size) } -end diff --git a/ruby/spec/lib/gitlab/git/commit_spec.rb b/ruby/spec/lib/gitlab/git/commit_spec.rb deleted file mode 100644 index bd2b5aeeb..000000000 --- a/ruby/spec/lib/gitlab/git/commit_spec.rb +++ /dev/null @@ -1,207 +0,0 @@ -require "spec_helper" - -describe Gitlab::Git::Commit do - include TestRepo - - let(:repository) { gitlab_git_from_gitaly(new_mutable_test_repo) } - let(:rugged_repo) { Rugged::Repository.new(repository.path) } - let(:commit) { described_class.find(repository, SeedRepo::Commit::ID) } - let(:rugged_commit) { rugged_repo.lookup(SeedRepo::Commit::ID) } - - describe "Commit info" do - let(:committer) do - { - email: 'mike@smith.com', - name: "Mike Smith", - time: Time.new(2000, 1, 1, 0, 0, 0, "+08:00") - } - end - let(:author) do - { - email: 'john@smith.com', - name: "John Smith", - time: Time.new(2000, 1, 1, 0, 0, 0, "-08:00") - } - end - let(:parents) { [rugged_repo.head.target] } - let(:gitlab_parents) do - parents.map { |c| described_class.find(repository, c.oid) } - end - let(:tree) { parents.first.tree } - let(:sha) do - Rugged::Commit.create( - rugged_repo, - author: author, - committer: committer, - tree: tree, - parents: parents, - message: "Refactoring specs", - update_ref: "HEAD" - ) - end - let(:rugged_commit) { rugged_repo.lookup(sha) } - let(:commit) { described_class.find(repository, sha) } - - it { expect(commit.short_id).to eq(rugged_commit.oid[0..10]) } - it { expect(commit.id).to eq(rugged_commit.oid) } - it { expect(commit.sha).to eq(rugged_commit.oid) } - it { expect(commit.safe_message).to eq(rugged_commit.message) } - it { expect(commit.date).to eq(rugged_commit.committer[:time]) } - it { expect(commit.author_email).to eq(author[:email]) } - it { expect(commit.author_name).to eq(author[:name]) } - it { expect(commit.committer_name).to eq(committer[:name]) } - it { expect(commit.committer_email).to eq(committer[:email]) } - it { expect(commit.parents).to eq(gitlab_parents) } - it { expect(commit.no_commit_message).to eq("--no commit message") } - end - - describe "Commit info from gitaly commit" do - let(:subject) { "My commit".b } - let(:body) { subject + "My body".b } - let(:body_size) { body.length } - let(:gitaly_commit) { build(:gitaly_commit, subject: subject, body: body, body_size: body_size) } - let(:id) { gitaly_commit.id } - let(:committer) { gitaly_commit.committer } - let(:author) { gitaly_commit.author } - let(:commit) { described_class.new(repository, gitaly_commit) } - - it { expect(commit.short_id).to eq(id[0..10]) } - it { expect(commit.id).to eq(id) } - it { expect(commit.sha).to eq(id) } - it { expect(commit.safe_message).to eq(body) } - it { expect(commit.author_email).to eq(author.email) } - it { expect(commit.author_name).to eq(author.name) } - it { expect(commit.committer_name).to eq(committer.name) } - it { expect(commit.committer_email).to eq(committer.email) } - it { expect(commit.parent_ids).to eq(gitaly_commit.parent_ids) } - - context 'non-UTC dates' do - let(:seconds) { Time.now.to_i } - - it 'sets timezones correctly' do - gitaly_commit.author.date.seconds = seconds - gitaly_commit.author.timezone = '-0800' - gitaly_commit.committer.date.seconds = seconds - gitaly_commit.committer.timezone = '+0800' - - expect(commit.authored_date).to eq(Time.at(seconds, in: '-08:00')) - expect(commit.committed_date).to eq(Time.at(seconds, in: '+08:00')) - end - end - - context 'body_size != body.size' do - let(:body) { "".b } - - context 'zero body_size' do - it { expect(commit.safe_message).to eq(subject) } - end - end - end - - context 'Class methods' do - describe '.find' do - it "returns an array of parent ids" do - expect(described_class.find(repository, SeedRepo::Commit::ID).parent_ids).to be_an(Array) - end - - it "should return valid commit for tag" do - expect(described_class.find(repository, 'v1.0.0').id).to eq('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') - end - - it "should return nil for non-commit ids" do - blob_id = repository.lookup("#{SeedRepo::Commit::ID}:files/ruby/popen.rb") - expect(described_class.find(repository, blob_id)).to be_nil - end - - it "should return nil for parent of non-commit object" do - blob_id = repository.lookup("#{SeedRepo::Commit::ID}:files/ruby/popen.rb") - expect(described_class.find(repository, "#{blob_id}^")).to be_nil - end - - it "should return nil for nonexisting ids" do - expect(described_class.find(repository, "+123_4532530XYZ")).to be_nil - end - - context 'with broken repo' do - let(:repository) { gitlab_git_from_gitaly(new_broken_test_repo) } - - it 'returns nil' do - expect(described_class.find(repository, SeedRepo::Commit::ID)).to be_nil - end - end - end - - describe '.shas_with_signatures' do - let(:signed_shas) { %w[5937ac0a7beb003549fc5fd26fc247adbce4a52e 570e7b2abdd848b95f2f578043fc23bd6f6fd24d] } - let(:unsigned_shas) { %w[19e2e9b4ef76b422ce1154af39a91323ccc57434 c642fe9b8b9f28f9225d7ea953fe14e74748d53b] } - let(:first_signed_shas) { %w[5937ac0a7beb003549fc5fd26fc247adbce4a52e c642fe9b8b9f28f9225d7ea953fe14e74748d53b] } - - it 'has 2 signed shas' do - ret = described_class.shas_with_signatures(repository, signed_shas) - expect(ret).to eq(signed_shas) - end - - it 'has 0 signed shas' do - ret = described_class.shas_with_signatures(repository, unsigned_shas) - expect(ret).to eq([]) - end - - it 'has 1 signed sha' do - ret = described_class.shas_with_signatures(repository, first_signed_shas) - expect(ret).to contain_exactly(first_signed_shas.first) - end - end - end - - describe '#init_from_rugged' do - let(:gitlab_commit) { described_class.new(repository, rugged_commit) } - subject { gitlab_commit } - - describe '#id' do - subject { super().id } - it { is_expected.to eq(SeedRepo::Commit::ID) } - end - end - - describe '#init_from_hash' do - let(:commit) { described_class.new(repository, sample_commit_hash) } - subject { commit } - - describe '#id' do - subject { super().id } - it { is_expected.to eq(sample_commit_hash[:id]) } - end - - describe '#message' do - subject { super().message } - it { is_expected.to eq(sample_commit_hash[:message]) } - end - end - - describe '#to_hash' do - let(:hash) { commit.to_hash } - subject { hash } - - it { is_expected.to be_kind_of Hash } - - describe '#keys' do - subject { super().keys.sort } - it { is_expected.to match(sample_commit_hash.keys.sort) } - end - end - - def sample_commit_hash - { - author_email: "dmitriy.zaporozhets@gmail.com", - author_name: "Dmitriy Zaporozhets", - authored_date: "2012-02-27 20:51:12 +0200", - committed_date: "2012-02-27 20:51:12 +0200", - committer_email: "dmitriy.zaporozhets@gmail.com", - committer_name: "Dmitriy Zaporozhets", - id: SeedRepo::Commit::ID, - message: "tree css fixes", - parent_ids: ["874797c3a73b60d2187ed6e2fcabd289ff75171e"], - trailers: [] - } - end -end diff --git a/ruby/spec/lib/gitlab/git/hook_spec.rb b/ruby/spec/lib/gitlab/git/hook_spec.rb deleted file mode 100644 index 1d5454728..000000000 --- a/ruby/spec/lib/gitlab/git/hook_spec.rb +++ /dev/null @@ -1,144 +0,0 @@ -require 'spec_helper' - -describe Gitlab::Git::Hook do - include TestRepo - - describe '.directory' do - it 'does not raise an KeyError' do - expect { described_class.directory }.not_to raise_error - end - end - - describe '#trigger' do - let(:tmp_dir) { Dir.mktmpdir } - let(:hook_names) { %w[pre-receive post-receive update] } - let(:repo) { gitlab_git_from_gitaly(test_repo_read_only) } - let(:push_options) do - Gitlab::Git::PushOptions.new(['ci.skip']) - end - - def trigger_with_stub_data(hook, push_options) - hook.trigger('user-1', 'admin', '0' * 40, 'a' * 40, 'master', push_options: push_options) - end - - before do - hook_names.each do |f| - path = File.join(tmp_dir, f) - File.write(path, script) - FileUtils.chmod("u+x", path) - end - - allow(Gitlab.config.git).to receive(:hooks_directory).and_return(tmp_dir) - end - - after do - FileUtils.remove_entry(tmp_dir) - end - - context 'when the hooks require environment variables' do - let(:vars) do - { - 'GITALY_HOOKS_PAYLOAD' => Base64.strict_encode64({ - repository: repo.gitaly_repository.to_json, - binary_directory: Gitlab.config.gitaly.bin_dir, - git_path: Gitlab.config.git.bin_path, - internal_socket: Gitlab.config.gitaly.internal_socket, - internal_socket_token: nil, - user_details: { - userid: 'user-123', - username: 'janedoe', - protocol: 'web' - } - }.to_json), - 'PWD' => repo.path, - 'GIT_DIR' => repo.path - } - end - - let(:script) do - [ - "#!/bin/sh", - vars.map do |key, value| - <<-SCRIPT - if [ x$#{key} != x#{value} ]; then - echo "unexpected value: #{key}=$#{key}" - exit 1 - fi - SCRIPT - end.join, - "exit 0" - ].join("\n") - end - - it 'returns true' do - hook_names.each do |hook| - trigger_result = described_class.new(hook, repo) - .trigger('user-123', 'janedoe', '0' * 40, 'a' * 40, 'master', push_options: push_options) - - expect(trigger_result.first).to be(true), "#{hook} failed: #{trigger_result.last}" - end - end - end - - context 'when the hooks are successful' do - let(:script) { "#!/bin/sh\nexit 0\n" } - - it 'returns true' do - hook_names.each do |hook| - trigger_result = described_class.new(hook, repo) - .trigger('user-456', 'admin', '0' * 40, 'a' * 40, 'master', push_options: push_options) - - expect(trigger_result.first).to be(true) - end - end - end - - context 'when the hooks fail' do - let(:script) { "#!/bin/sh\nexit 1\n" } - - it 'returns false' do - hook_names.each do |name| - hook = described_class.new(name, repo) - trigger_result = trigger_with_stub_data(hook, push_options) - - expect(trigger_result.first).to be(false) - end - end - end - - context 'when push options are passed' do - let(:script) do - <<~HOOK - #!/usr/bin/env ruby - unless ENV['GIT_PUSH_OPTION_COUNT'] == '1' && ENV['GIT_PUSH_OPTION_0'] == 'ci.skip' - abort 'missing GIT_PUSH_OPTION env vars' - end - HOOK - end - - context 'for pre-receive and post-receive hooks' do - let(:hooks) do - %w[pre-receive post-receive].map { |name| described_class.new(name, repo) } - end - - it 'sets the push options environment variables' do - hooks.each do |hook| - trigger_result = trigger_with_stub_data(hook, push_options) - - expect(trigger_result.first).to be(true) - end - end - end - - context 'for update hook' do - let(:hook) { described_class.new('update', repo) } - - it 'does not set the push options environment variables' do - trigger_result = trigger_with_stub_data(hook, push_options) - - expect(trigger_result.first).to be(false) - end - end - end - end -end diff --git a/ruby/spec/lib/gitlab/git/popen_spec.rb b/ruby/spec/lib/gitlab/git/popen_spec.rb deleted file mode 100644 index 6272b7c17..000000000 --- a/ruby/spec/lib/gitlab/git/popen_spec.rb +++ /dev/null @@ -1,108 +0,0 @@ -require 'spec_helper' - -describe 'Gitlab::Git::Popen' do - let(:path) { Dir.mktmpdir } - - let(:klass) do - Class.new(Object) do - include Gitlab::Git::Popen - end - end - - after do - FileUtils.remove_entry path - end - - context 'popen' do - context 'zero status' do - let(:result) { klass.new.popen(%w(ls), path) } - let(:status) { result.last } - - it { expect(status).to be_zero } - end - - context 'non-zero status' do - let(:result) { klass.new.popen(%w(cat NOTHING), path) } - let(:output) { result.first } - let(:status) { result.last } - - it { expect(status).to eq(1) } - it { expect(output).to include('No such file or directory') } - end - - context 'when stderr is not included' do - let(:result) { klass.new.popen(%w(cat NOTHING), path, include_stderr: false) } - let(:output) { result.first } - let(:status) { result.last } - - it { expect(status).to eq(1) } - it { expect(output).to eq('') } - end - - context 'when stderr is included' do - let(:result) { klass.new.popen(['ruby', '-e', 'warn "hello world"'], path, include_stderr: true) } - let(:output) { result.first } - let(:status) { result.last } - - it { expect(status).to eq(0) } - it { expect(output).to eq("hello world\n") } - end - - context 'unsafe string command' do - it 'raises an error when it gets called with a string argument' do - expect { klass.new.popen('ls', path) }.to raise_error(RuntimeError) - end - end - - context 'with custom options' do - let(:vars) { { 'foobar' => 123, 'PWD' => path } } - let(:options) { { chdir: path } } - - it 'calls popen3 with the provided environment variables' do - expect(Open3).to receive(:popen3).with(vars, 'ls', options) - - klass.new.popen(%w(ls), path, { 'foobar' => 123 }) # rubocop:disable Style/BracesAroundHashParameters: - end - end - - context 'use stdin' do - let(:result) { klass.new.popen(%w[cat], path) { |stdin| stdin.write 'hello' } } - let(:output) { result.first } - let(:status) { result.last } - - it { expect(status).to be_zero } - it { expect(output).to eq('hello') } - end - - context 'with lazy block' do - it 'yields a lazy io' do - expect_lazy_io = lambda do |io| - expect(io).to be_a Enumerator::Lazy - expect(io.inspect).to include('#<IO:fd') - end - - klass.new.popen(%w[ls], path, lazy_block: expect_lazy_io) - end - - it "doesn't wait for process exit" do - Timeout.timeout(2) do - klass.new.popen(%w[yes], path, lazy_block: ->(io) {}) - end - end - end - - context 'with non ASCII output' do - let(:stdin) { StringIO.new } - let(:stdout) { StringIO.new("Preparando \xC3\xA1rbol de trabajo") } - let(:stderr) { StringIO.new("UTF-8 error é").set_encoding('UTF-8') } - let(:process_status) { double('Process::Status', exitstatus: 0) } - let(:wait_thr) { double('Process::Waiter', value: process_status) } - - it "handles the output correctly" do - expect(Open3).to receive(:popen3).and_yield(stdin, stdout, stderr, wait_thr) - - klass.new.popen(%w[ls], path) - end - end - end -end diff --git a/ruby/spec/lib/gitlab/git/push_options_spec.rb b/ruby/spec/lib/gitlab/git/push_options_spec.rb deleted file mode 100644 index d2c74f399..000000000 --- a/ruby/spec/lib/gitlab/git/push_options_spec.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::Git::PushOptions do - subject { described_class.new(options) } - - describe '#env_data' do - context 'when push options are set' do - let(:options) { ['ci.skip', 'test=value'] } - - it 'sets GIT_PUSH_OPTION environment variables' do - env_data = subject.env_data - - expect(env_data.count).to eq(3) - expect(env_data['GIT_PUSH_OPTION_COUNT']).to eq('2') - expect(env_data['GIT_PUSH_OPTION_0']).to eq('ci.skip') - expect(env_data['GIT_PUSH_OPTION_1']).to eq('test=value') - end - end - - context 'when push options are not set' do - let(:options) { [] } - - it 'does not set any variable' do - env_data = subject.env_data - - expect(env_data).to eq({}) - end - end - end -end diff --git a/ruby/spec/lib/gitlab/git/repository_spec.rb b/ruby/spec/lib/gitlab/git/repository_spec.rb deleted file mode 100644 index 573058b25..000000000 --- a/ruby/spec/lib/gitlab/git/repository_spec.rb +++ /dev/null @@ -1,249 +0,0 @@ -require 'spec_helper' - -describe Gitlab::Git::Repository do # rubocop:disable Metrics/BlockLength - include TestRepo - include Gitlab::EncodingHelper - using RSpec::Parameterized::TableSyntax - - let(:mutable_repository) { gitlab_git_from_gitaly(new_mutable_git_test_repo) } - let(:repository) { gitlab_git_from_gitaly(git_test_repo_read_only) } - let(:repository_path) { repository.path } - let(:repository_rugged) { Rugged::Repository.new(repository_path) } - let(:storage_path) { DEFAULT_STORAGE_DIR } - let(:user) { Gitlab::Git::User.new('johndone', 'John Doe', 'johndoe@mail.com', 'user-1') } - - describe '.from_gitaly_with_block' do - let(:call_metadata) do - { - 'user-agent' => 'grpc-go/1.9.1', - 'gitaly-storage-path' => DEFAULT_STORAGE_DIR, - 'gitaly-repo-path' => TEST_REPO_PATH, - 'gitaly-gl-repository' => 'project-52', - 'gitaly-repo-alt-dirs' => '' - } - end - let(:call) { double(metadata: call_metadata) } - - it 'cleans up the repository' do - described_class.from_gitaly_with_block(test_repo_read_only, call) do |repository| - expect(repository.rugged).to receive(:close) - end - end - - it 'returns the passed result of the block passed' do - result = described_class.from_gitaly_with_block(test_repo_read_only, call) { 'Hello world' } - - expect(result).to eq('Hello world') - end - end - - describe "Respond to" do - subject { repository } - - it { is_expected.to respond_to(:root_ref) } - it { is_expected.to respond_to(:tags) } - end - - describe '#root_ref' do - it 'calls #discover_default_branch' do - expect(repository).to receive(:discover_default_branch) - repository.root_ref - end - end - - describe '#branch_names' do - subject { repository.branch_names } - - it 'has SeedRepo::Repo::BRANCHES.size elements' do - expect(subject.size).to eq(SeedRepo::Repo::BRANCHES.size) - end - - it { is_expected.to include("master") } - it { is_expected.not_to include("branch-from-space") } - end - - describe '#tags' do - describe 'first tag' do - let(:tag) { repository.tags.first } - - it { expect(tag.name).to eq("v1.0.0") } - it { expect(tag.target).to eq("f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8") } - it { expect(tag.dereferenced_target.sha).to eq("6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9") } - it { expect(tag.message).to eq("Release") } - end - - describe 'last tag' do - let(:tag) { repository.tags.last } - - it { expect(tag.name).to eq("v1.2.1") } - it { expect(tag.target).to eq("2ac1f24e253e08135507d0830508febaaccf02ee") } - it { expect(tag.dereferenced_target.sha).to eq("fa1b1e6c004a68b7d8763b86455da9e6b23e36d6") } - it { expect(tag.message).to eq("Version 1.2.1") } - end - - it { expect(repository.tags.size).to eq(SeedRepo::Repo::TAGS.size) } - end - - describe '#empty?' do - it { expect(repository).not_to be_empty } - end - - describe '#merge_base' do - where(:from, :to, :result) do - '570e7b2abdd848b95f2f578043fc23bd6f6fd24d' | '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | '570e7b2abdd848b95f2f578043fc23bd6f6fd24d' - '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | '570e7b2abdd848b95f2f578043fc23bd6f6fd24d' | '570e7b2abdd848b95f2f578043fc23bd6f6fd24d' - '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | 'foobar' | nil - 'foobar' | '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | nil - end - - with_them do - it { expect(repository.merge_base(from, to)).to eq(result) } - end - end - - describe '#find_branch' do - it 'should return a Branch for master' do - branch = repository.find_branch('master') - - expect(branch).to be_a_kind_of(Gitlab::Git::Branch) - expect(branch.name).to eq('master') - end - - it 'should handle non-existent branch' do - branch = repository.find_branch('this-is-garbage') - - expect(branch).to eq(nil) - end - end - - describe '#branches' do - subject { repository.branches } - - context 'with local and remote branches' do - let(:repository) { mutable_repository } - - before do - create_remote_branch('joe', 'remote_branch', 'master') - create_branch(repository, 'local_branch', 'master') - end - - it 'returns the local and remote branches' do - expect(subject.any? { |b| b.name == 'joe/remote_branch' }).to eq(true) - expect(subject.any? { |b| b.name == 'local_branch' }).to eq(true) - end - end - end - - describe '#branch_exists?' do - it 'returns true for an existing branch' do - expect(repository.branch_exists?('master')).to eq(true) - end - - it 'returns false for a non-existing branch' do - expect(repository.branch_exists?('kittens')).to eq(false) - end - - it 'returns false when using an invalid branch name' do - expect(repository.branch_exists?('.bla')).to eq(false) - end - end - - describe '#with_repo_branch_commit' do - context 'when repository is empty' do - let(:repository) { gitlab_git_from_gitaly(new_empty_test_repo) } - - it 'yields nil' do - expect do |block| - repository.with_repo_branch_commit('master', &block) - end.to yield_with_args(nil) - end - end - - context 'when repository is not empty' do - let(:start_commit) { repository.commit } - - it 'yields the commit for the SHA' do - expect do |block| - repository.with_repo_branch_commit(start_commit.sha, &block) - end.to yield_with_args(start_commit) - end - - it 'yields the commit for the branch' do - expect do |block| - repository.with_repo_branch_commit('master', &block) - end.to yield_with_args(start_commit) - end - end - end - - describe '#cleanup' do - context 'when Rugged has been called' do - it 'calls close on Rugged::Repository' do - rugged = repository.rugged - - expect(rugged).to receive(:close).and_call_original - - repository.cleanup - end - end - - context 'when Rugged has not been called' do - it 'does not call close on Rugged::Repository' do - expect(repository).not_to receive(:rugged) - - repository.cleanup - end - end - end - - describe '#rugged' do - after do - Thread.current[described_class::RUGGED_KEY] = nil - end - - it 'stores reference in Thread.current' do - Thread.current[described_class::RUGGED_KEY] = [] - - 2.times do - rugged = repository.rugged - - expect(rugged).to be_a(Rugged::Repository) - expect(Thread.current[described_class::RUGGED_KEY]).to eq([rugged]) - end - end - - it 'does not store reference if Thread.current is not set up' do - rugged = repository.rugged - - expect(rugged).to be_a(Rugged::Repository) - expect(Thread.current[described_class::RUGGED_KEY]).to be_nil - end - end - - describe '#head_symbolic_ref' do - subject { repository.head_symbolic_ref } - - it 'returns the symbolic ref in HEAD' do - expect(subject).to eq('master') - end - - context 'when repo is empty' do - let(:repository) { gitlab_git_from_gitaly(new_empty_test_repo) } - - it 'returns the symbolic ref in HEAD' do - repository.rugged.head = 'refs/heads/foo' - - expect(subject).to eq('foo') - end - end - end - - def create_remote_branch(remote_name, branch_name, source_branch_name) - source_branch = repository.branches.find { |branch| branch.name == source_branch_name } - repository_rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", source_branch.dereferenced_target.sha) - end - - def create_branch(repository, branch_name, start_point = 'HEAD') - repository.rugged.branches.create(branch_name, start_point) - end -end diff --git a/ruby/spec/lib/gitlab/git/user_spec.rb b/ruby/spec/lib/gitlab/git/user_spec.rb deleted file mode 100644 index 778fc3c80..000000000 --- a/ruby/spec/lib/gitlab/git/user_spec.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'spec_helper' - -describe Gitlab::Git::User do - let(:username) { 'janedoe' } - let(:name) { 'Jane Doé' } - let(:email) { 'janedoé@example.com' } - let(:gl_id) { 'user-123' } - let(:user) do - described_class.new(username, name, email, gl_id) - end - - subject { described_class.new(username, name, email, gl_id) } - - describe '.from_gitaly' do - let(:gitaly_user) do - Gitaly::User.new(gl_username: username, name: name.b, email: email.b, gl_id: gl_id) - end - - subject { described_class.from_gitaly(gitaly_user) } - - it { expect(subject).to eq(user) } - end - - describe '#==' do - def eq_other(username, name, email, gl_id) - eq(described_class.new(username, name, email, gl_id)) - end - - it { expect(subject).to eq_other(username, name, email, gl_id) } - - it { expect(subject).not_to eq_other(nil, nil, nil, nil) } - it { expect(subject).not_to eq_other(username + 'x', name, email, gl_id) } - it { expect(subject).not_to eq_other(username, name + 'x', email, gl_id) } - it { expect(subject).not_to eq_other(username, name, email + 'x', gl_id) } - it { expect(subject).not_to eq_other(username, name, email, gl_id + 'x') } - end - - describe '#git_env' do - let(:git_env) { subject.git_env } - - it 'returns the user environment variables' do - expect(git_env['GIT_COMMITTER_NAME']).to eq(name) - expect(git_env['GIT_COMMITTER_EMAIL']).to eq(email) - expect(git_env['GL_ID']).to eq(gl_id) - end - end -end diff --git a/ruby/spec/spec_helper.rb b/ruby/spec/spec_helper.rb index dcd20550e..d65ae3b22 100644 --- a/ruby/spec/spec_helper.rb +++ b/ruby/spec/spec_helper.rb @@ -1,5 +1,4 @@ require_relative '../lib/gitaly_server.rb' -require_relative '../lib/gitlab/git.rb' require_relative 'support/sentry.rb' require 'timecop' require 'rspec-parameterized' diff --git a/ruby/spec/test_repo_helper.rb b/ruby/spec/test_repo_helper.rb index e8fd142a2..a71c763f2 100644 --- a/ruby/spec/test_repo_helper.rb +++ b/ruby/spec/test_repo_helper.rb @@ -48,43 +48,10 @@ module TestRepo end end - def git_test_repo_read_only - Gitaly::Repository.new(storage_name: DEFAULT_STORAGE_NAME, relative_path: File.basename(GIT_TEST_REPO_PATH)) - end - def test_repo_read_only Gitaly::Repository.new(storage_name: DEFAULT_STORAGE_NAME, relative_path: File.basename(TEST_REPO_PATH)) end - def new_mutable_test_repo - relative_path = random_repository_relative_path(:mutable) - TestRepo.clone_new_repo!(TEST_REPO_ORIGIN, File.join(DEFAULT_STORAGE_DIR, relative_path)) - Gitaly::Repository.new(storage_name: DEFAULT_STORAGE_NAME, relative_path: relative_path) - end - - def new_mutable_git_test_repo - relative_path = random_repository_relative_path(:mutable) - TestRepo.clone_new_repo!(GIT_TEST_REPO_ORIGIN, File.join(DEFAULT_STORAGE_DIR, relative_path)) - Gitaly::Repository.new(storage_name: DEFAULT_STORAGE_NAME, relative_path: relative_path) - end - - def new_broken_test_repo - relative_path = random_repository_relative_path(:broken) - repo_path = File.join(DEFAULT_STORAGE_DIR, relative_path) - TestRepo.clone_new_repo!(TEST_REPO_ORIGIN, repo_path) - - refs_path = File.join(repo_path, 'refs') - FileUtils.rm_r(refs_path) - - Gitaly::Repository.new(storage_name: DEFAULT_STORAGE_NAME, relative_path: relative_path) - end - - def new_empty_test_repo - relative_path = random_repository_relative_path(:mutable) - TestRepo.init_new_repo!(File.join(DEFAULT_STORAGE_DIR, relative_path)) - Gitaly::Repository.new(storage_name: DEFAULT_STORAGE_NAME, relative_path: relative_path) - end - def rugged_from_gitaly(gitaly_repo) Rugged::Repository.new(repo_path_from_gitaly(gitaly_repo)) end @@ -96,21 +63,6 @@ module TestRepo File.join(DEFAULT_STORAGE_DIR, gitaly_repo.relative_path) end - def gitlab_git_from_gitaly(gitaly_repo) - Gitlab::Git::Repository.new( - gitaly_repo, - repo_path_from_gitaly(gitaly_repo), - 'project-123', - '' - ) - end - - def repository_from_relative_path(relative_path) - gitlab_git_from_gitaly( - Gitaly::Repository.new(storage_name: DEFAULT_STORAGE_NAME, relative_path: relative_path) - ) - end - def self.clone_new_repo!(origin, destination) env = {} env['GIT_EXEC_PATH'] = GIT_EXEC_PATH if defined?(GIT_EXEC_PATH) @@ -119,18 +71,6 @@ module TestRepo abort "Failed to clone test repo. Try running 'make prepare-tests' and try again." end - - def self.init_new_repo!(destination) - return if system(Gitlab.config.git.bin_path, "-c", "init.templateDir=", "init", "--quiet", "--bare", destination.to_s) - - abort "Failed to init test repo." - end - - private - - def random_repository_relative_path(prefix) - "#{prefix}-#{SecureRandom.hex(6)}.git" - end end TestRepo.prepare_test_repository |