diff options
Diffstat (limited to 'gems')
28 files changed, 245 insertions, 88 deletions
diff --git a/gems/activerecord-gitlab/lib/active_record/gitlab_patches/partitioning.rb b/gems/activerecord-gitlab/lib/active_record/gitlab_patches/partitioning.rb index cf0bd4849e2..2f3dde2a871 100644 --- a/gems/activerecord-gitlab/lib/active_record/gitlab_patches/partitioning.rb +++ b/gems/activerecord-gitlab/lib/active_record/gitlab_patches/partitioning.rb @@ -34,9 +34,12 @@ module ActiveRecord ::ActiveRecord::Reflection::MacroReflection.prepend( ActiveRecord::GitlabPatches::Partitioning::Reflection::MacroReflection ) - ::ActiveRecord::Base.prepend( + ::ActiveRecord::Persistence.prepend( ActiveRecord::GitlabPatches::Partitioning::Base ) + ::ActiveRecord::Persistence::ClassMethods.prepend( + ActiveRecord::GitlabPatches::Partitioning::Base::ClassMethods + ) end end end diff --git a/gems/activerecord-gitlab/lib/active_record/gitlab_patches/partitioning/base.rb b/gems/activerecord-gitlab/lib/active_record/gitlab_patches/partitioning/base.rb index 0c8a248b984..c4b3528a526 100644 --- a/gems/activerecord-gitlab/lib/active_record/gitlab_patches/partitioning/base.rb +++ b/gems/activerecord-gitlab/lib/active_record/gitlab_patches/partitioning/base.rb @@ -4,19 +4,18 @@ if ::ActiveRecord::VERSION::STRING >= "7.1" raise 'New version of active-record detected, please remove or update this patch' end +# rubocop:disable Gitlab/ModuleWithInstanceVariables module ActiveRecord module GitlabPatches module Partitioning module Base - extend ActiveSupport::Concern - def _query_constraints_hash constraints_hash = super return constraints_hash unless self.class.use_partition_id_filter? if self.class.query_constraints_list.nil? - { @primary_key => id_in_database } # rubocop:disable Gitlab/ModuleWithInstanceVariables + { @primary_key => id_in_database } else self.class.query_constraints_list.index_with do |column_name| attribute_in_database(column_name) @@ -24,7 +23,7 @@ module ActiveRecord end end - class_methods do + module ClassMethods def use_partition_id_filter? false end @@ -47,3 +46,4 @@ module ActiveRecord end end end +# rubocop:enable Gitlab/ModuleWithInstanceVariables diff --git a/gems/activerecord-gitlab/lib/active_record/gitlab_patches/rescue_from.rb b/gems/activerecord-gitlab/lib/active_record/gitlab_patches/rescue_from.rb index faabddd2686..eaaa90ec52b 100644 --- a/gems/activerecord-gitlab/lib/active_record/gitlab_patches/rescue_from.rb +++ b/gems/activerecord-gitlab/lib/active_record/gitlab_patches/rescue_from.rb @@ -7,6 +7,7 @@ module ActiveRecord # # - `ActiveRecord::Relation#load`, and other methods that call # `ActiveRecord::Relation#exec_queries`. + # - `ActiveModel::UnknownAttributeError` as a result of `ActiveRecord::Base#assign_attributes` # # class ApplicationRecord < ActiveRecord::Base # rescue_from MyException, with: :my_handler @@ -29,13 +30,23 @@ module ActiveRecord def exec_queries super rescue StandardError => e + # Method klass is defined in ActiveRecord gem lib/active_record/relation.rb klass.rescue_with_handler(e) || raise end end + + module AssignAttributesRescueWithHandler + def _assign_attributes(...) + super(...) + rescue StandardError => e + rescue_with_handler(e) || raise + end + end end end ActiveSupport.on_load(:active_record) do ActiveRecord::Relation.prepend(ActiveRecord::GitlabPatches::ExecQueriesRescueWithHandler) + ActiveRecord::Base.prepend(ActiveRecord::GitlabPatches::AssignAttributesRescueWithHandler) ActiveRecord::Base.prepend(ActiveRecord::GitlabPatches::RescueFrom) end diff --git a/gems/activerecord-gitlab/spec/active_record/gitlab_patches/partitioning/locking_spec.rb b/gems/activerecord-gitlab/spec/active_record/gitlab_patches/partitioning/locking_spec.rb new file mode 100644 index 00000000000..df9241e0983 --- /dev/null +++ b/gems/activerecord-gitlab/spec/active_record/gitlab_patches/partitioning/locking_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +RSpec.describe 'ActiveRecord::GitlabPatches::Partitioning::Associations::Locking', :partitioning do + let!(:job) { LockingJob.create!(partition_id: 100) } + + describe 'optimistic locking' do + it 'does not use lock version on unrelated updates' do + update_statement = <<~SQL.squish + UPDATE "locking_jobs" SET "name" = 'test' + WHERE "locking_jobs"."id" = #{job.id} AND "locking_jobs"."partition_id" = #{job.partition_id} + SQL + + result = QueryRecorder.log do + job.update!(name: 'test') + end + + expect(result).to include(update_statement) + end + + it 'uses lock version when status changes' do + update_statement = <<~SQL.squish + UPDATE "locking_jobs" + SET "status" = 1, "name" = 'test', "lock_version" = 1 + WHERE "locking_jobs"."id" = 1 AND "locking_jobs"."partition_id" = 100 AND "locking_jobs"."lock_version" = 0 + SQL + + result = QueryRecorder.log do + job.update!(name: 'test', status: :completed) + end + + expect(result).to include(update_statement) + end + end +end diff --git a/gems/activerecord-gitlab/spec/active_record/gitlab_patches/rescue_from_spec.rb b/gems/activerecord-gitlab/spec/active_record/gitlab_patches/rescue_from_spec.rb index 22729edb014..0a70c4dddaf 100644 --- a/gems/activerecord-gitlab/spec/active_record/gitlab_patches/rescue_from_spec.rb +++ b/gems/activerecord-gitlab/spec/active_record/gitlab_patches/rescue_from_spec.rb @@ -4,10 +4,13 @@ RSpec.describe ActiveRecord::GitlabPatches::RescueFrom do let(:model_with_rescue_from) do Class.new(Project) do rescue_from ActiveRecord::StatementInvalid, with: :handle_exception + rescue_from ActiveRecord::UnknownAttributeError, with: :handle_attr_exception class << self def handle_exception(exception); end end + + def handle_attr_exception(exc); end end end @@ -26,4 +29,19 @@ RSpec.describe ActiveRecord::GitlabPatches::RescueFrom do expect { model_without_rescue_from.where('BADQUERY').load }.to raise_error(ActiveRecord::StatementInvalid) end end + + context 'for errors from ActiveRecord::Base.assign_attributes' do + it 'triggers rescue_from' do + model_instance = model_with_rescue_from.new + + expect(model_instance).to receive(:handle_attr_exception) + + expect { model_instance.assign_attributes(nonexistent_column: "some value") }.not_to raise_error + end + + it 'does not trigger rescue_from' do + expect { model_without_rescue_from.new.assign_attributes(nonexistent_column: "some value") } + .to raise_error(ActiveRecord::UnknownAttributeError) + end + end end diff --git a/gems/activerecord-gitlab/spec/support/database.rb b/gems/activerecord-gitlab/spec/support/database.rb index 998d945c311..7b4de82312d 100644 --- a/gems/activerecord-gitlab/spec/support/database.rb +++ b/gems/activerecord-gitlab/spec/support/database.rb @@ -24,6 +24,14 @@ RSpec.configure do |config| t.integer :partition_id t.boolean :test_flag, default: false end + + create_table :locking_jobs, force: true do |t| + t.integer :pipeline_id + t.integer :partition_id + t.integer :lock_version, default: 0, null: false + t.integer :status, default: 0, null: false + t.string :name + end end end end diff --git a/gems/activerecord-gitlab/spec/support/models.rb b/gems/activerecord-gitlab/spec/support/models.rb index c0017656ea8..445dc8b2ac2 100644 --- a/gems/activerecord-gitlab/spec/support/models.rb +++ b/gems/activerecord-gitlab/spec/support/models.rb @@ -48,3 +48,14 @@ class Metadata < PartitionedRecord belongs_to :job, ->(metadata) { where(partition_id: metadata.partition_id) } end + +class LockingJob < PartitionedRecord + self.table_name = :locking_jobs + query_constraints :id, :partition_id + + enum status: { created: 0, completed: 1 } + + def locking_enabled? + will_save_change_to_status? + end +end diff --git a/gems/click_house-client/lib/click_house/client/formatter.rb b/gems/click_house-client/lib/click_house/client/formatter.rb index de7ae72bdf8..a19c55e3e3d 100644 --- a/gems/click_house-client/lib/click_house/client/formatter.rb +++ b/gems/click_house-client/lib/click_house/client/formatter.rb @@ -8,7 +8,8 @@ module ClickHouse TYPE_CASTERS = { 'UInt64' => ->(value) { Integer(value) }, "DateTime64(6, 'UTC')" => ->(value) { ActiveSupport::TimeZone['UTC'].parse(value) }, - "IntervalSecond" => ->(value) { ActiveSupport::Duration.build(value.to_i) } + "IntervalSecond" => ->(value) { ActiveSupport::Duration.build(value.to_i) }, + "IntervalMillisecond" => ->(value) { ActiveSupport::Duration.build(value.to_i / 1000.0) } }.freeze def self.format(result) diff --git a/gems/click_house-client/spec/click_house/client/formatter_spec.rb b/gems/click_house-client/spec/click_house/client/formatter_spec.rb index 0af3aa0bdbc..ca57187b098 100644 --- a/gems/click_house-client/spec/click_house/client/formatter_spec.rb +++ b/gems/click_house-client/spec/click_house/client/formatter_spec.rb @@ -8,7 +8,8 @@ RSpec.describe ClickHouse::Client::Formatter do _query = <<~SQL.squish SELECT toUInt64(1) as uint64, toDateTime64('2016-06-15 23:00:00', 6, 'UTC') as datetime64_6, - INTERVAL 1 second as interval_second + INTERVAL 1 second as interval_second, + INTERVAL 1 millisecond as interval_millisecond SQL response_json = <<~JSON @@ -26,6 +27,10 @@ RSpec.describe ClickHouse::Client::Formatter do { "name": "interval_second", "type": "IntervalSecond" + }, + { + "name": "interval_millisecond", + "type": "IntervalMillisecond" } ], @@ -34,7 +39,8 @@ RSpec.describe ClickHouse::Client::Formatter do { "uint64": "1", "datetime64_6": "2016-06-15 23:00:00.000000", - "interval_second": "1" + "interval_second": "1", + "interval_millisecond": "1" } ], @@ -56,7 +62,8 @@ RSpec.describe ClickHouse::Client::Formatter do eq( [{ "uint64" => 1, "datetime64_6" => ActiveSupport::TimeZone["UTC"].parse("2016-06-15 23:00:00"), - "interval_second" => 1.second }] + "interval_second" => 1.second, + "interval_millisecond" => 0.001.seconds }] ) ) end diff --git a/gems/config/rubocop.yml b/gems/config/rubocop.yml index 72b37aa60b5..d6139bef1b5 100644 --- a/gems/config/rubocop.yml +++ b/gems/config/rubocop.yml @@ -93,7 +93,7 @@ RSpec/ContextWording: - 'if' # This cop doesn't make sense in the context of gems -RSpec/MissingFeatureCategory: +RSpec/FeatureCategory: Enabled: false # Enable once we drop 3.0 support diff --git a/gems/csv_builder/lib/csv_builder/gzip.rb b/gems/csv_builder/lib/csv_builder/gzip.rb index f97c066705a..83a83970acd 100644 --- a/gems/csv_builder/lib/csv_builder/gzip.rb +++ b/gems/csv_builder/lib/csv_builder/gzip.rb @@ -12,12 +12,15 @@ module CsvBuilder # > puts rows # > end def render - Tempfile.open(['csv_builder_gzip', '.csv.gz']) do |tempfile| - csv = CSV.new(Zlib::GzipWriter.open(tempfile.path)) + Tempfile.create(['csv_builder_gzip', '.csv.gz']) do |tempfile| + Zlib::GzipWriter.open(tempfile.path) do |gz| + csv = CSV.new(gz) - write_csv csv, until_condition: -> {} # truncation must be handled outside of the CsvBuilder + write_csv csv, until_condition: -> {} # truncation must be handled outside of the CsvBuilder + + csv.close + end - csv.close yield tempfile, @rows_written end end diff --git a/gems/gitlab-http/.rubocop.yml b/gems/gitlab-http/.rubocop.yml index 73ea5f610b3..8bc6b6a4cfb 100644 --- a/gems/gitlab-http/.rubocop.yml +++ b/gems/gitlab-http/.rubocop.yml @@ -1,13 +1,13 @@ inherit_from: - ../config/rubocop.yml +Gemfile/MissingFeatureCategory: + Enabled: false + Naming/ClassAndModuleCamelCase: AllowedNames: - HTTP_V2 -Performance/RegexpMatch: - Enabled: false - Style/SpecialGlobalVars: Enabled: false diff --git a/gems/gitlab-http/Gemfile.lock b/gems/gitlab-http/Gemfile.lock index 4afa39ef750..c15bcd7cc18 100644 --- a/gems/gitlab-http/Gemfile.lock +++ b/gems/gitlab-http/Gemfile.lock @@ -19,11 +19,11 @@ PATH remote: . specs: gitlab-http (0.1.0) - activesupport (~> 7.0.6) + activesupport (~> 7) httparty (~> 0.21.0) ipaddress (~> 0.8.3) nokogiri (~> 1.15.4) - railties (~> 7.0.6) + railties (~> 7) GEM remote: https://rubygems.org/ diff --git a/gems/gitlab-http/gitlab-http.gemspec b/gems/gitlab-http/gitlab-http.gemspec index 2653d4d4fb7..6146ba7f78b 100644 --- a/gems/gitlab-http/gitlab-http.gemspec +++ b/gems/gitlab-http/gitlab-http.gemspec @@ -19,11 +19,11 @@ Gem::Specification.new do |spec| spec.test_files = Dir['spec/**/*'] spec.require_paths = ["lib"] - spec.add_runtime_dependency 'activesupport', '~> 7.0.6' + spec.add_runtime_dependency 'activesupport', '~> 7' spec.add_runtime_dependency 'httparty', '~> 0.21.0' spec.add_runtime_dependency 'ipaddress', '~> 0.8.3' spec.add_runtime_dependency 'nokogiri', '~> 1.15.4' - spec.add_runtime_dependency "railties", "~> 7.0.6" + spec.add_runtime_dependency "railties", "~> 7" spec.add_development_dependency 'gitlab-styles', '~> 10.1.0' spec.add_development_dependency 'rspec-rails', '~> 6.0.3' diff --git a/gems/gitlab-http/lib/gitlab/http_v2/client.rb b/gems/gitlab-http/lib/gitlab/http_v2/client.rb index 8daf19d7351..c10197e0385 100644 --- a/gems/gitlab-http/lib/gitlab/http_v2/client.rb +++ b/gems/gitlab-http/lib/gitlab/http_v2/client.rb @@ -25,70 +25,74 @@ module Gitlab include HTTParty # rubocop:disable Gitlab/HTTParty - class << self - alias_method :httparty_perform_request, :perform_request - end - connection_adapter NewConnectionAdapter - def self.perform_request(http_method, path, options, &block) - raise_if_blocked_by_silent_mode(http_method) if options.delete(:silent_mode_enabled) + class << self + def try_get(path, options = {}, &block) + self.get(path, options, &block) # rubocop:disable Style/RedundantSelf + rescue *HTTP_ERRORS + nil + end - log_info = options.delete(:extra_log_info) - options_with_timeouts = - if !options.has_key?(:timeout) - options.with_defaults(DEFAULT_TIMEOUT_OPTIONS) - else - options - end + def configuration + Gitlab::HTTP_V2.configuration + end - if options[:stream_body] - httparty_perform_request(http_method, path, options_with_timeouts, &block) - else - begin - start_time = nil - read_total_timeout = options.fetch(:timeout, DEFAULT_READ_TOTAL_TIMEOUT) + private - httparty_perform_request(http_method, path, options_with_timeouts) do |fragment| - start_time ||= system_monotonic_time - elapsed = system_monotonic_time - start_time + alias_method :httparty_perform_request, :perform_request - raise ReadTotalTimeout, "Request timed out after #{elapsed} seconds" if elapsed > read_total_timeout + # TODO: This overwrites a method implemented by `HTTPParty` + # The calls to `get/...` will call this method instead of `httparty_perform_request` + def perform_request(http_method, path, options, &block) + raise_if_blocked_by_silent_mode(http_method) if options.delete(:silent_mode_enabled) + + log_info = options.delete(:extra_log_info) + options_with_timeouts = + if !options.has_key?(:timeout) + options.with_defaults(DEFAULT_TIMEOUT_OPTIONS) + else + options + end - yield fragment if block + if options[:stream_body] + httparty_perform_request(http_method, path, options_with_timeouts, &block) + else + begin + start_time = nil + read_total_timeout = options.fetch(:timeout, DEFAULT_READ_TOTAL_TIMEOUT) + + httparty_perform_request(http_method, path, options_with_timeouts) do |fragment| + start_time ||= system_monotonic_time + elapsed = system_monotonic_time - start_time + + raise ReadTotalTimeout, "Request timed out after #{elapsed} seconds" if elapsed > read_total_timeout + + yield fragment if block + end + rescue HTTParty::RedirectionTooDeep + raise RedirectionTooDeep + rescue *HTTP_ERRORS => e + extra_info = log_info || {} + extra_info = log_info.call(e, path, options) if log_info.respond_to?(:call) + configuration.log_exception(e, extra_info) + + raise e end - rescue HTTParty::RedirectionTooDeep - raise RedirectionTooDeep - rescue *HTTP_ERRORS => e - extra_info = log_info || {} - extra_info = log_info.call(e, path, options) if log_info.respond_to?(:call) - configuration.log_exception(e, extra_info) - - raise e end end - end - - def self.try_get(path, options = {}, &block) - self.get(path, options, &block) # rubocop:disable Style/RedundantSelf - rescue *HTTP_ERRORS - nil - end - def self.raise_if_blocked_by_silent_mode(http_method) - return if SILENT_MODE_ALLOWED_METHODS.include?(http_method) + def raise_if_blocked_by_silent_mode(http_method) + return if SILENT_MODE_ALLOWED_METHODS.include?(http_method) - configuration.silent_mode_log_info('Outbound HTTP request blocked', http_method.to_s) + configuration.silent_mode_log_info('Outbound HTTP request blocked', http_method.to_s) - raise SilentModeBlockedError, 'only get, head, options, and trace methods are allowed in silent mode' - end - - def self.system_monotonic_time - Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second) - end + raise SilentModeBlockedError, 'only get, head, options, and trace methods are allowed in silent mode' + end - def self.configuration - Gitlab::HTTP_V2.configuration + def system_monotonic_time + Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second) + end end end end diff --git a/gems/gitlab-http/lib/gitlab/http_v2/url_blocker.rb b/gems/gitlab-http/lib/gitlab/http_v2/url_blocker.rb index a794ab2f443..878daf42d8a 100644 --- a/gems/gitlab-http/lib/gitlab/http_v2/url_blocker.rb +++ b/gems/gitlab-http/lib/gitlab/http_v2/url_blocker.rb @@ -188,7 +188,7 @@ module Gitlab # # @param uri [Addressable::URI] # - # @raise [Gitlab::UrlBlocker::BlockedUrlError, ArgumentError] - BlockedUrlError raised if host is too long. + # @raise [Gitlab::HTTP_V2::UrlBlocker::BlockedUrlError, ArgumentError] raised if host is too long. # # @return [Array<Addrinfo>] def get_address_info(uri) @@ -271,7 +271,7 @@ module Gitlab def multiline_blocked?(parsed_url) url = parsed_url.to_s - return true if url =~ /\n|\r/ + return true if /\n|\r/.match?(url) # Google Cloud Storage uses a multi-line, encoded Signature query string return false if %w[http https].include?(parsed_url.scheme&.downcase) @@ -295,7 +295,7 @@ module Gitlab def validate_user(value) return if value.blank? - return if value =~ /\A\p{Alnum}/ + return if /\A\p{Alnum}/.match?(value) raise BlockedUrlError, "Username needs to start with an alphanumeric character" end @@ -303,7 +303,7 @@ module Gitlab def validate_hostname(value) return if value.blank? return if IPAddress.valid?(value) - return if value =~ /\A\p{Alnum}/ + return if /\A\p{Alnum}/.match?(value) raise BlockedUrlError, "Hostname or IP address invalid" end diff --git a/gems/gitlab-http/lib/net_http/response_patch.rb b/gems/gitlab-http/lib/net_http/response_patch.rb index e5477a31318..303d629b32e 100644 --- a/gems/gitlab-http/lib/net_http/response_patch.rb +++ b/gems/gitlab-http/lib/net_http/response_patch.rb @@ -20,11 +20,12 @@ module Net start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) key = value = nil while true - line = if sock.is_a?(Gitlab::HTTP_V2::BufferedIo) - sock.readuntil("\n", true, start_time) - else - sock.readuntil("\n", true) - end + uses_buffered_io = sock.is_a?(Gitlab::HTTP_V2::BufferedIo) + + # TODO: Gitlab::BufferedIo is temporarily used for an easy migration. + uses_buffered_io ||= sock.is_a?(Gitlab::BufferedIo) if defined?(Gitlab::BufferedIo) + + line = uses_buffered_io ? sock.readuntil("\n", true, start_time) : sock.readuntil("\n", true) line = line.sub(/\s{0,10}\z/, '') break if line.empty? if line[0] == ?\s or line[0] == ?\t and value diff --git a/gems/gitlab-http/spec/gitlab/http_v2/net_http_patch_spec.rb b/gems/gitlab-http/spec/gitlab/http_v2/net_http_patch_spec.rb index b82646fb365..f34b0d98403 100644 --- a/gems/gitlab-http/spec/gitlab/http_v2/net_http_patch_spec.rb +++ b/gems/gitlab-http/spec/gitlab/http_v2/net_http_patch_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' require 'net/http' -RSpec.describe 'Net::HTTP patch proxy user and password encoding' do +RSpec.describe 'Net::HTTP patch proxy user and password encoding', feature_category: :shared do let(:net_http) { Net::HTTP.new('hostname.example') } before do diff --git a/gems/gitlab-schema-validation/lib/gitlab/schema/validation/adapters/column_structure_sql_adapter.rb b/gems/gitlab-schema-validation/lib/gitlab/schema/validation/adapters/column_structure_sql_adapter.rb index 62e501bf16b..5b6026a1f73 100644 --- a/gems/gitlab-schema-validation/lib/gitlab/schema/validation/adapters/column_structure_sql_adapter.rb +++ b/gems/gitlab-schema-validation/lib/gitlab/schema/validation/adapters/column_structure_sql_adapter.rb @@ -36,7 +36,7 @@ module Gitlab value = parse_node(constraints.find { |node| node.constraint.contype == DEFAULT_CONSTR }) - return unless value + return if value.nil? "DEFAULT #{value}" end diff --git a/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/database.rb b/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/database.rb index 45ce4d8ebfe..3f62396c156 100644 --- a/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/database.rb +++ b/gems/gitlab-schema-validation/lib/gitlab/schema/validation/sources/database.rb @@ -143,8 +143,10 @@ module Gitlab # rubocop:disable Rails/SquishedSQLHeredocs sql = <<~SQL SELECT indexname, indexdef - FROM pg_indexes - WHERE indexname NOT LIKE '%_pkey' AND schemaname IN ($1, $2); + FROM pg_indexes i + LEFT JOIN pg_constraint AS c ON i.indexname = c.conname + WHERE i.indexname NOT LIKE '%_pkey' AND schemaname IN ($1, $2) + AND c.conname IS NULL; SQL # rubocop:enable Rails/SquishedSQLHeredocs diff --git a/gems/gitlab-schema-validation/spec/fixtures/structure.sql b/gems/gitlab-schema-validation/spec/fixtures/structure.sql index 421fb6c3593..7bb68734c72 100644 --- a/gems/gitlab-schema-validation/spec/fixtures/structure.sql +++ b/gems/gitlab-schema-validation/spec/fixtures/structure.sql @@ -19,7 +19,8 @@ CREATE TABLE test_table ( numeric_column numeric NOT NULL, numeric_with_default_column numeric DEFAULT 1.0 NOT NULL, boolean_colum boolean, - boolean_with_default_colum boolean DEFAULT true NOT NULL, + boolean_with_default_column_true boolean DEFAULT true NOT NULL, + boolean_with_default_column_false boolean DEFAULT false NOT NULL, double_precision_column double precision, double_precision_with_default_column double precision DEFAULT 1.0, varying_column character varying, diff --git a/gems/gitlab-schema-validation/spec/lib/gitlab/schema/validation/adapters/column_structure_sql_adapter_spec.rb b/gems/gitlab-schema-validation/spec/lib/gitlab/schema/validation/adapters/column_structure_sql_adapter_spec.rb index ae0d635e8ca..1c44db5286a 100644 --- a/gems/gitlab-schema-validation/spec/lib/gitlab/schema/validation/adapters/column_structure_sql_adapter_spec.rb +++ b/gems/gitlab-schema-validation/spec/lib/gitlab/schema/validation/adapters/column_structure_sql_adapter_spec.rb @@ -21,7 +21,8 @@ RSpec.describe Gitlab::Schema::Validation::Adapters::ColumnStructureSqlAdapter, ['smallint_with_default_column', 'smallint', 'DEFAULT 0', 'NOT NULL', false], ['double_precision_with_default_column', 'double precision', 'DEFAULT 1.0', nil, false], ['numeric_with_default_column', 'numeric', 'DEFAULT 1.0', 'NOT NULL', false], - ['boolean_with_default_colum', 'boolean', 'DEFAULT true', 'NOT NULL', false], + ['boolean_with_default_column_true', 'boolean', 'DEFAULT true', 'NOT NULL', false], + ['boolean_with_default_column_false', 'boolean', 'DEFAULT false', 'NOT NULL', false], ['varying_with_default_column', 'character varying', "DEFAULT 'DEFAULT'::character varying", 'NOT NULL', false], ['varying_with_limit_and_default_column', 'character varying(255)', "DEFAULT 'DEFAULT'::character varying", nil, false], diff --git a/gems/gitlab-utils/.rubocop.yml b/gems/gitlab-utils/.rubocop.yml index eeafd850c9b..bea4262fa2b 100644 --- a/gems/gitlab-utils/.rubocop.yml +++ b/gems/gitlab-utils/.rubocop.yml @@ -1,6 +1,9 @@ inherit_from: - ../config/rubocop.yml +Gemfile/MissingFeatureCategory: + Enabled: false + RSpec/InstanceVariable: Exclude: - spec/**/*.rb diff --git a/gems/gitlab-utils/lib/gitlab/version_info.rb b/gems/gitlab-utils/lib/gitlab/version_info.rb index 00a9b4ddc6e..21478c46259 100644 --- a/gems/gitlab-utils/lib/gitlab/version_info.rb +++ b/gems/gitlab-utils/lib/gitlab/version_info.rb @@ -7,10 +7,22 @@ module Gitlab attr_reader :major, :minor, :patch VERSION_REGEX = /(\d+)\.(\d+)\.(\d+)/ + MILESTONE_REGEX = /\A(\d+)\.(\d+)\z/ # To mitigate ReDoS, limit the length of the version string we're # willing to check MAX_VERSION_LENGTH = 128 + InvalidMilestoneError = Class.new(StandardError) + + def self.parse_from_milestone(str) + raise InvalidMilestoneError if str.length > MAX_VERSION_LENGTH + + m = MILESTONE_REGEX.match(str) + raise InvalidMilestoneError if m.nil? + + VersionInfo.new(m[1].to_i, m[2].to_i) + end + def self.parse(str, parse_suffix: false) return str if str.is_a?(self) diff --git a/gems/gitlab-utils/spec/gitlab/version_info_spec.rb b/gems/gitlab-utils/spec/gitlab/version_info_spec.rb index 2b5f6bcb4c1..ca13a06b92c 100644 --- a/gems/gitlab-utils/spec/gitlab/version_info_spec.rb +++ b/gems/gitlab-utils/spec/gitlab/version_info_spec.rb @@ -130,6 +130,40 @@ RSpec.describe Gitlab::VersionInfo, feature_category: :shared do end end + describe '.parse_from_milestone' do + subject(:milestone) { described_class.parse_from_milestone(milestone_str) } + + context 'when the milestone string is valid' do + let(:milestone_str) { '14.7' } + + it "creates a #{described_class.class} with patch version zero" do + expect(milestone.major).to eq 14 + expect(milestone.minor).to eq 7 + expect(milestone.patch).to eq 0 + end + end + + context 'when the milestone string is not valid' do + let(:milestone_str) { 'foo' } + + it 'raises InvalidMilestoneError' do + expect do + milestone + end.to raise_error "#{described_class}::InvalidMilestoneError".constantize + end + end + + context 'when the milestone string is too long' do + let(:milestone_str) { 'a' * 129 } + + it 'raises InvalidMilestoneError' do + expect do + milestone + end.to raise_error "#{described_class}::InvalidMilestoneError".constantize + end + end + end + describe '.to_s' do it { expect(@v1_0_0.to_s).to eq("1.0.0") } it { expect(@v1_0_1_rc1.to_s).to eq("1.0.1-rc1") } diff --git a/gems/rspec_flaky/.rubocop.yml b/gems/rspec_flaky/.rubocop.yml index 62cb8a982c5..66f8eb6e21c 100644 --- a/gems/rspec_flaky/.rubocop.yml +++ b/gems/rspec_flaky/.rubocop.yml @@ -1,6 +1,9 @@ inherit_from: - ../config/rubocop.yml +Gemfile/MissingFeatureCategory: + Enabled: false + # FIXME once Gitlab::Json is in a gem Gitlab/Json: Enabled: false diff --git a/gems/rspec_flaky/Gemfile.lock b/gems/rspec_flaky/Gemfile.lock index 547dc24e375..3f40a41483e 100644 --- a/gems/rspec_flaky/Gemfile.lock +++ b/gems/rspec_flaky/Gemfile.lock @@ -9,7 +9,7 @@ PATH remote: . specs: rspec_flaky (0.1.0) - activesupport (>= 6.1, < 7.1) + activesupport (>= 6.1, < 8) rspec (~> 3.0) GEM diff --git a/gems/rspec_flaky/rspec_flaky.gemspec b/gems/rspec_flaky/rspec_flaky.gemspec index 5c0a434218f..6ddbe4afd1e 100644 --- a/gems/rspec_flaky/rspec_flaky.gemspec +++ b/gems/rspec_flaky/rspec_flaky.gemspec @@ -20,7 +20,7 @@ Gem::Specification.new do |spec| spec.files = Dir["lib/**/*.rb"] spec.require_paths = ["lib"] - spec.add_runtime_dependency "activesupport", ">= 6.1", "< 7.1" + spec.add_runtime_dependency "activesupport", ">= 6.1", "< 8" spec.add_runtime_dependency "rspec", "~> 3.0" spec.add_development_dependency "gitlab-styles", "~> 10.1.0" |