Skip to content
代码片段 群组 项目
代码所有者
将用户和群组指定为特定文件更改的核准人。 了解更多。
elastic-migration 4.11 KiB
#!/usr/bin/env ruby
# frozen_string_literal: true

# Generate an Elastic migration file, spec and dictionary record with the current timestamp.

require 'yaml'
require 'fileutils'
require 'uri'
require 'readline'
require 'active_support'
require 'active_support/core_ext/string'

class ElasticMigrationCreator
  attr_reader :options

  Options = Struct.new(
    :name,
    :description,
    :group,
    :introduced_by_url,
    :milestone,
    :obsolete,
    :marked_obsolete_by_url,
    :marked_obsolete_in_milestone
  )

  def initialize
    @options = Options.new
  end

  def execute
    options.name = read_name
    options.description = read_description
    options.group = read_group
    options.introduced_by_url = read_introduced_by_url
    options.milestone = read_milestone

    $stdout.puts "\e[32mcreated\e[0m #{file_path}"
    $stdout.puts "\e[32mcreated\e[0m #{spec_file_path}"
    $stdout.puts "\e[32mcreated\e[0m #{dictionary_file_path}"

    write
    $stdout.puts "\n=> Please consult the documentation for Advanced Search Migrations: #{documentation_reference}"
  end

  private

  def read_name
    read_variable('name', 'Name of the migration in CamelCase').camelize
  end

  def read_description
    read_variable('description', 'Description of what the migration does')
  end

  def read_group
    read_variable('group', 'The group introducing a feature flag, like: `global search`')
  end

  def read_milestone
    milestone = File.read('VERSION')
    milestone.gsub(/^(\d+\.\d+).*$/, '\1').chomp
  end

  def read_variable(name, description)
    $stdout.puts "\n>> #{description}:"

    loop do
      variable = Readline.readline('?> ', false)&.strip
      return variable unless variable.empty?

      warn "Error: #{name} is required."
    end
  end

  def read_introduced_by_url
    $stdout.puts
    $stdout.puts ">> URL of the MR introducing the migration (enter to skip):"

    loop do
      introduced_by_url = Readline.readline('?> ', false)&.strip
      introduced_by_url = nil if introduced_by_url.empty?
      return introduced_by_url if introduced_by_url.nil? || introduced_by_url.start_with?('https://')

      warn 'Error: URL needs to start with https://'
    end
  end

  def write
    # create migration file
    FileUtils.mkdir_p(File.dirname(file_path))
    File.write(file_path, file_contents)

    # create spec
    FileUtils.mkdir_p(File.dirname(spec_file_path))
    File.write(spec_file_path, spec_contents)

    # create dictionary file
    FileUtils.mkdir_p(File.dirname(dictionary_file_path))
    File.write(dictionary_file_path, dictionary_contents)
  end

  def timestamp
    @timestamp ||= Time.now.strftime('%Y%m%d%H%M%S')
  end

  def file_name
    @file_name ||= "#{timestamp}_#{options.name.dup.underscore}"
  end

  def file_path
    "ee/elastic/migrate/#{file_name}.rb"
  end

  def spec_file_path
    "ee/spec/elastic/migrate/#{file_name}_spec.rb"
  end

  def dictionary_file_path
    "ee/elastic/docs/#{file_name}.yml"
  end

  def file_contents
    "# frozen_string_literal: true

class #{options.name} < Elastic::Migration
end
"
  end

  def spec_contents
    "# frozen_string_literal: true

require 'spec_helper'
require File.expand_path('#{file_path}')

# See https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#elasticsearch-specs
# for more information on how to write search migration specs for GitLab.
RSpec.describe #{options.name}, feature_category: :#{options.group.parameterize.underscore} do
  let(:version) { #{timestamp} }
end
"
  end

  def dictionary_contents
    dictionary_config_hash.to_yaml
  end

  def dictionary_config_hash
    {
      'name' => options.name,
      'version' => timestamp,
      'description' => options.description,
      'group' => "group::#{options.group}",
      'milestone' => options.milestone.to_s,
      'introduced_by_url' => options.introduced_by_url,
      'obsolete' => false,
      'marked_obsolete_by_url' => nil,
      'marked_obsolete_in_milestone' => nil
    }
  end

  def documentation_reference
    'https://docs.gitlab.com/ee/development/search/advanced_search_migration_styleguide.html'
  end
end

ElasticMigrationCreator.new.execute if $PROGRAM_NAME == __FILE__