Skip to content
代码片段 群组 项目
提交 09b3c07a 编辑于 作者: Prabakaran Murugesan's avatar Prabakaran Murugesan 提交者: Peter Leitzen
浏览文件

Cop to ensure BBM has the associated dictionary file

上级 e327568e
No related branches found
No related tags found
无相关合并请求
......@@ -440,6 +440,12 @@ BackgroundMigration/FeatureCategory:
Include:
- 'lib/gitlab/background_migration/*.rb'
BackgroundMigration/MissingDictionaryFile:
Enabled: true
EnforcedSince: 20230307160251
Include:
- 'db/post_migrate/*.rb'
# See https://gitlab.com/gitlab-org/gitlab/-/issues/373194
Gitlab/RSpec/AvoidSetup:
Enabled: true
......
---
# Grace period will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/395354
BackgroundMigration/MissingDictionaryFile:
Details: grace period
......@@ -64,5 +64,10 @@ def column_name
def feature_category
options[:feature_category]
end
def current_milestone
version = Gem::Version.new(File.read('VERSION'))
version.release.segments.first(2).join('.')
end
end
end
......@@ -3,4 +3,4 @@ migration_job_name: <%= class_name %>
description: # Please capture what <%= class_name %> does
feature_category: <%= feature_category %>
introduced_by_url: # URL of the MR (or issue/commit) that introduced the migration
milestone:
milestone: <%= current_milestone %>
......@@ -458,7 +458,7 @@ namespace :gitlab do
Rails.application.eager_load!
version = Gem::Version.new(File.read('VERSION'))
milestone = version.release.segments[0..1].join('.')
milestone = version.release.segments.first(2).join('.')
classes = {}
......
# frozen_string_literal: true
require_relative '../../migration_helpers'
module RuboCop
module Cop
module BackgroundMigration
# Checks the batched background migration has the corresponding dictionary file
class MissingDictionaryFile < RuboCop::Cop::Base
include MigrationHelpers
MSG = "Missing %{file_name}. " \
"Use the generator 'batched_background_migration' to create dictionary files automatically. " \
"For more details refer: https://docs.gitlab.com/ee/development/database/batched_background_migrations.html#generator"
DICTIONARY_DIR = "db/docs/batched_background_migrations"
def_node_matcher :batched_background_migration_name_node, <<~PATTERN
`(send nil? :queue_batched_background_migration $_ ...)
PATTERN
def_node_matcher :migration_constant_value, <<~PATTERN
`(casgn nil? %const_name ({sym|str} $_))
PATTERN
def on_class(node)
return unless time_enforced?(node) && in_post_deployment_migration?(node)
migration_name_node = batched_background_migration_name_node(node)
return unless migration_name_node
migration_name = if migration_name_node.const_name.present?
migration_constant_value(node, const_name: migration_name_node.const_name.to_sym)
else
migration_name_node.value
end
return if dictionary_file?(migration_name)
add_offense(node, message: format(MSG, file_name: dictionary_file_path(migration_name)))
end
private
def dictionary_file?(migration_class_name)
File.exist?(dictionary_file_path(migration_class_name))
end
def dictionary_file_path(migration_class_name)
File.join(rails_root, DICTIONARY_DIR, "#{migration_class_name.underscore}.yml")
end
def rails_root
@rails_root ||= File.expand_path('../../..', __dir__)
end
end
end
end
end
......@@ -25,7 +25,7 @@
let(:expected_migration_spec_file) { load_expected_file('queue_my_batched_migration_spec.txt') }
let(:expected_migration_job_file) { load_expected_file('my_batched_migration.txt') }
let(:expected_migration_job_spec_file) { load_expected_file('my_batched_migration_spec_matcher.txt') }
let(:expected_migration_dictionary) { load_expected_file('my_batched_migration_dictionary.txt') }
let(:expected_migration_dictionary) { load_expected_file('my_batched_migration_dictionary_matcher.txt') }
it 'generates expected files' do
run_generator %w[my_batched_migration --table_name=projects --column_name=id --feature_category=database]
......@@ -48,7 +48,8 @@
end
assert_file('db/docs/batched_background_migrations/my_batched_migration.yml') do |migration_dictionary|
expect(migration_dictionary).to eq(expected_migration_dictionary)
# Regex is used to match the dynamically generated 'milestone' in the dictionary
expect(migration_dictionary).to match(/#{expected_migration_dictionary}/)
end
end
end
......
......@@ -2,5 +2,5 @@
migration_job_name: MyBatchedMigration
description: # Please capture what MyBatchedMigration does
feature_category: database
introduced_by_url: # URL of the MR (or issue/commit) that introduced the migration
milestone:
introduced_by_url: # URL of the MR \(or issue/commit\) that introduced the migration
milestone: [0-9\.]+
# frozen_string_literal: true
require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/background_migration/missing_dictionary_file'
RSpec.describe RuboCop::Cop::BackgroundMigration::MissingDictionaryFile, feature_category: :database do
let(:config) do
RuboCop::Config.new(
'BackgroundMigration/MissingDictionaryFile' => {
'EnforcedSince' => 20230307160251
}
)
end
context 'for non post migrations' do
before do
allow(cop).to receive(:in_post_deployment_migration?).and_return(false)
end
it 'does not throw any offense' do
expect_no_offenses(<<~RUBY)
class QueueMyMigration < Gitlab::Database::Migration[2.1]
MIGRATION = 'MyMigration'
def up
queue_batched_background_migration(
MIGRATION,
:users,
:id
)
end
end
RUBY
end
end
context 'for post migrations' do
before do
allow(cop).to receive(:in_post_deployment_migration?).and_return(true)
end
context 'without enqueuing batched migrations' do
it 'does not throw any offense' do
expect_no_offenses(<<~RUBY)
class CreateTestTable < Gitlab::Database::Migration[2.1]
def change
create_table(:tests)
end
end
RUBY
end
end
context 'with enqueuing batched migration' do
let(:rails_root) { File.expand_path('../../../..', __dir__) }
let(:dictionary_file_path) { File.join(rails_root, 'db/docs/batched_background_migrations/my_migration.yml') }
context 'for migrations before enforced time' do
before do
allow(cop).to receive(:version).and_return(20230307160250)
end
it 'does not throw any offenses' do
expect_no_offenses(<<~RUBY)
class QueueMyMigration < Gitlab::Database::Migration[2.1]
MIGRATION = 'MyMigration'
def up
queue_batched_background_migration(
MIGRATION,
:users,
:id
)
end
end
RUBY
end
end
context 'for migrations after enforced time' do
before do
allow(cop).to receive(:version).and_return(20230307160252)
end
it 'throws offense on not having the appropriate dictionary file with migration name as a constant' do
expect_offense(<<~RUBY)
class QueueMyMigration < Gitlab::Database::Migration[2.1]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{format("Missing %{file_name}. Use the generator 'batched_background_migration' to create dictionary files automatically. For more details refer: https://docs.gitlab.com/ee/development/database/batched_background_migrations.html#generator", file_name: dictionary_file_path)}
MIGRATION = 'MyMigration'
def up
queue_batched_background_migration(
MIGRATION,
:users,
:id
)
end
end
RUBY
end
it 'throws offense on not having the appropriate dictionary file with migration name as a variable' do
expect_offense(<<~RUBY)
class QueueMyMigration < Gitlab::Database::Migration[2.1]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{format("Missing %{file_name}. Use the generator 'batched_background_migration' to create dictionary files automatically. For more details refer: https://docs.gitlab.com/ee/development/database/batched_background_migrations.html#generator", file_name: dictionary_file_path)}
def up
queue_batched_background_migration(
'MyMigration',
:users,
:id
)
end
end
RUBY
end
it 'does not throw offense with appropriate dictionary file' do
expect(File).to receive(:exist?).with(dictionary_file_path).and_return(true)
expect_no_offenses(<<~RUBY)
class QueueMyMigration < Gitlab::Database::Migration[2.1]
MIGRATION = 'MyMigration'
def up
queue_batched_background_migration(
MIGRATION,
:users,
:id
)
end
end
RUBY
end
end
end
end
end
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册