mirror of
https://gitlab.com/gitlab-org/gitlab-foss.git
synced 2025-07-23 00:47:51 +00:00
211 lines
8.0 KiB
Ruby
211 lines
8.0 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'rubocop'
|
|
|
|
module Keeps
|
|
# This is an implementation of a ::Gitlab::Housekeeper::Keep. This keep will not make any changes unless there are
|
|
# Advanced search migrations to be marked as obsolete. This keep locates advanced search migrations that were added
|
|
# before the cutoff milestone and adds a prepend to the bottom of the migration file which marks the migration as
|
|
# obsolete in the code. It also updates the corresponding `ee/elastic/migrate/docs/` file with
|
|
# a `marked_obsolete_in_milestone`. The cutoff milestone is defined as one milestone before the last required stop.
|
|
# This is to prevent long running migrations from being marked as obsolete.
|
|
#
|
|
# You can run it manually with:
|
|
#
|
|
# ```
|
|
# bundle exec gitlab-housekeeper -d -k Keeps::MarkOldAdvancedSearchMigrationsAsObsolete
|
|
# ```
|
|
class MarkOldAdvancedSearchMigrationsAsObsolete < ::Gitlab::Housekeeper::Keep
|
|
MIGRATIONS_PATH = 'ee/elastic/migrate'
|
|
MIGRATION_REGEXP = /\A([0-9]+)_([_a-z0-9]*)\.rb\z/
|
|
MIGRATIONS_SPECS_PATH = 'ee/spec/elastic/migrate/'
|
|
MIGRATION_DOCS_PATH = 'ee/elastic/docs'
|
|
|
|
GROUP_LABEL = 'group::global search'
|
|
|
|
def initialize(...)
|
|
@migrations_to_be_marked_obsolete = {}
|
|
@search_team_map = group_data['backend_engineers'].index_with(0)
|
|
|
|
load_migrations_to_process
|
|
|
|
super
|
|
end
|
|
|
|
def each_change
|
|
return if migrations_to_be_marked_obsolete.empty?
|
|
|
|
migrations_to_be_marked_obsolete.each do |version, migration_data|
|
|
mark_obsolete_change = create_mark_obsolete_change(version, migration_data)
|
|
next unless mark_obsolete_change
|
|
|
|
search_team_map[mark_obsolete_change.assignees.first] += 1
|
|
yield(mark_obsolete_change)
|
|
end
|
|
|
|
nil
|
|
end
|
|
|
|
private
|
|
|
|
attr_reader :migrations_to_be_marked_obsolete, :search_team_map
|
|
|
|
def load_migrations_to_process
|
|
each_advanced_search_migration do |migration_filename, spec_filename, yaml_filename, yaml_content|
|
|
version = yaml_content['version']
|
|
next unless before_cutoff_milestone?(yaml_content['milestone'])
|
|
next if yaml_content['obsolete']
|
|
next unless before_cutoff_milestone?(yaml_content['milestone'])
|
|
|
|
migrations_to_be_marked_obsolete[version] =
|
|
{
|
|
file: migration_filename,
|
|
spec_file: spec_filename,
|
|
yaml_filename: yaml_filename,
|
|
yaml_content: yaml_content
|
|
}
|
|
end
|
|
end
|
|
|
|
def get_migration_versions_from_codebase
|
|
migration_files = Dir[File.join(MIGRATIONS_PATH, '**', '[0-9]*_*.rb')]
|
|
migration_versions_hash = {}
|
|
migration_files.each do |migration_file|
|
|
version, filename = File.basename(migration_file).scan(MIGRATION_REGEXP).first
|
|
migration_versions_hash[version] = { version: version, filename: filename }
|
|
end
|
|
migration_versions_hash
|
|
end
|
|
|
|
def before_cutoff_milestone?(milestone)
|
|
Gem::Version.new(milestone) < Gem::Version.new(cutoff_milestone)
|
|
end
|
|
|
|
def each_advanced_search_migration
|
|
all_advanced_search_migration_files.map do |f|
|
|
version, filename = File.basename(f).scan(MIGRATION_REGEXP).first
|
|
yaml_file = "#{MIGRATION_DOCS_PATH}/#{version}_#{filename}.yml"
|
|
spec_file = "#{MIGRATIONS_SPECS_PATH}/#{version}_#{filename}_spec.rb"
|
|
|
|
yield(f, spec_file, yaml_file, YAML.load_file(yaml_file))
|
|
end
|
|
end
|
|
|
|
def all_advanced_search_migration_files
|
|
Dir.glob("#{MIGRATIONS_PATH}/*.rb")
|
|
end
|
|
|
|
def create_mark_obsolete_change(version, migration_data)
|
|
migration_name = migration_data[:yaml_content]['name']
|
|
change = ::Gitlab::Housekeeper::Change.new
|
|
change.title = "Mark #{version} as obsolete"
|
|
change.identifiers = ['mark_obsolete', version, migration_name]
|
|
change.labels = [
|
|
'maintenance::refactor',
|
|
GROUP_LABEL
|
|
]
|
|
assignee = search_team_map.min_by { |_k, v| v }.first
|
|
change.assignees = assignee
|
|
change.changelog_ee = true
|
|
|
|
# rubocop:disable Gitlab/DocumentationLinks/HardcodedUrl -- Not running inside rails application
|
|
change.description = <<~MARKDOWN
|
|
This migration marks the #{version} #{migration_name} Advanced search migration as obsolete.
|
|
|
|
[Search for references to `#{migration_name.underscore}` in code](https://gitlab.com/search?project_id=278964&scope=blobs&search=#{migration_name.underscore}®ex=false))
|
|
|
|
At the moment, the `gitlab-housekeeper` is not always capable of removing all references so
|
|
you must check the diff and pipeline failures to confirm if there are any issues.
|
|
It is the responsibility of the assignee (picked from ~"group::global search") to push those changes to this branch.
|
|
|
|
[Read more](https://docs.gitlab.com/ee/development/search/advanced_search_migration_styleguide.html#cleaning-up-advanced-search-migrations)
|
|
about the process for marking Advanced search migrations as obsolete.
|
|
|
|
All Advanced search migrations must have had at least one
|
|
[required stop](https : // docs.gitlab.com / ee / development / database / required_stops.html)
|
|
to process the migration. Therefore we mark any Advanced search migrations added before the
|
|
last required stop as obsolete.
|
|
MARKDOWN
|
|
# rubocop:enable Gitlab/DocumentationLinks/HardcodedUrl
|
|
|
|
change.changed_files = []
|
|
add_obsolete_to_yaml(migration_data[:yaml_filename], migration_data[:yaml_content])
|
|
change.changed_files << migration_data[:yaml_filename]
|
|
|
|
add_obsolete_to_migration(migration_data[:file])
|
|
change.changed_files << migration_data[:file]
|
|
|
|
if File.exist?(migration_data[:spec_file])
|
|
add_obsolete_to_migration_spec(version, migration_data[:spec_file], migration_data[:yaml_content]['name'])
|
|
change.changed_files << migration_data[:spec_file]
|
|
end
|
|
|
|
change.push_options.ci_skip = true
|
|
|
|
change
|
|
end
|
|
|
|
def add_obsolete_to_yaml(file, content)
|
|
content['obsolete'] = true
|
|
content['marked_obsolete_in_milestone'] = migration_marked_as_obsolete_milestone.to_s
|
|
File.open(file, 'w') { |f| f.write(YAML.dump(content)) }
|
|
end
|
|
|
|
def add_obsolete_to_migration(file)
|
|
source = RuboCop::ProcessedSource.new(File.read(file), RuboCop::ConfigStore.new.for_file('.').target_ruby_version)
|
|
ast = source.ast
|
|
klass_name = ast.children[0].source
|
|
|
|
File.open(file, 'a') { |f| f.write("\n#{klass_name}.prepend ::Search::Elastic::MigrationObsolete\n") }
|
|
end
|
|
|
|
def add_obsolete_to_migration_spec(version, file, name)
|
|
source = RuboCop::ProcessedSource.new(File.read(file), RuboCop::ConfigStore.new.for_file('.').target_ruby_version)
|
|
rewriter = Parser::Source::TreeRewriter.new(source.buffer)
|
|
describe_block = source.ast.each_node(:block).first
|
|
content = <<~RUBY.strip
|
|
RSpec.describe #{name}, feature_category: :global_search do
|
|
it_behaves_like 'a deprecated Advanced Search migration', #{version}
|
|
end
|
|
RUBY
|
|
|
|
rewriter.replace(describe_block.loc.expression, content)
|
|
process = rewriter.process.lstrip.gsub(/\n{3,}/, "\n\n")
|
|
|
|
File.write(file, process)
|
|
end
|
|
|
|
def migration_marked_as_obsolete_milestone
|
|
@migration_marked_as_obsolete_milestone ||= read_milestone
|
|
end
|
|
|
|
def read_milestone
|
|
milestone = File.read('VERSION')
|
|
milestone.gsub(/^(\d+\.\d+).*$/, '\1').chomp
|
|
end
|
|
|
|
def groups_helper
|
|
@groups_helper ||= ::Keeps::Helpers::Groups.new
|
|
end
|
|
|
|
def group_data
|
|
@group_data ||= groups_helper.group_for_group_label(GROUP_LABEL)
|
|
end
|
|
|
|
def cutoff_milestone
|
|
@cutoff_milestone ||= calculate_cutoff_milestone
|
|
end
|
|
|
|
def calculate_cutoff_milestone
|
|
# Only mark migrations added in the milestone before the last required stop as obsolete
|
|
last_required_stop = Gitlab::Database.min_schema_gitlab_version
|
|
|
|
if last_required_stop.minor > 0
|
|
Gitlab::VersionInfo.new(last_required_stop.major, last_required_stop.minor - 1, 0)
|
|
else
|
|
Gitlab::VersionInfo.new(last_required_stop.major - 1, 11, 0)
|
|
end
|
|
end
|
|
end
|
|
end
|