From a21091270d45530468f8ac2f4f926fe1b9840b67 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 1 Feb 2023 12:10:48 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- tooling/bin/js_to_system_specs_mappings | 10 +++ tooling/bin/view_to_js_mappings | 4 +- tooling/lib/tooling/mappings/base.rb | 30 +++++++++ .../mappings/js_to_system_specs_mappings.rb | 61 +++++++++++++++++ .../lib/tooling/mappings/view_to_js_mappings.rb | 74 +++++++++++++++++++++ tooling/lib/tooling/view_to_js_mappings.rb | 77 ---------------------- 6 files changed, 177 insertions(+), 79 deletions(-) create mode 100755 tooling/bin/js_to_system_specs_mappings create mode 100644 tooling/lib/tooling/mappings/base.rb create mode 100644 tooling/lib/tooling/mappings/js_to_system_specs_mappings.rb create mode 100644 tooling/lib/tooling/mappings/view_to_js_mappings.rb delete mode 100644 tooling/lib/tooling/view_to_js_mappings.rb (limited to 'tooling') diff --git a/tooling/bin/js_to_system_specs_mappings b/tooling/bin/js_to_system_specs_mappings new file mode 100755 index 00000000000..59a66ab0691 --- /dev/null +++ b/tooling/bin/js_to_system_specs_mappings @@ -0,0 +1,10 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require_relative '../lib/tooling/mappings/js_to_system_specs_mappings' + +changes = ARGV.shift +output_file = ARGV.shift +changed_files = File.read(changes).split(' ') + +File.write(output_file, Tooling::Mappings::JsToSystemSpecsMappings.new.execute(changed_files).join(' ')) diff --git a/tooling/bin/view_to_js_mappings b/tooling/bin/view_to_js_mappings index 2cebb91892e..483003aac5e 100755 --- a/tooling/bin/view_to_js_mappings +++ b/tooling/bin/view_to_js_mappings @@ -1,10 +1,10 @@ #!/usr/bin/env ruby # frozen_string_literal: true -require_relative '../lib/tooling/view_to_js_mappings' +require_relative '../lib/tooling/mappings/view_to_js_mappings' changes = ARGV.shift output_file = ARGV.shift changed_files = File.read(changes).split(' ') -File.write(output_file, Tooling::ViewToJsMappings.new.execute(changed_files).join(' ')) +File.write(output_file, Tooling::Mappings::ViewToJsMappings.new.execute(changed_files).join(' ')) diff --git a/tooling/lib/tooling/mappings/base.rb b/tooling/lib/tooling/mappings/base.rb new file mode 100644 index 00000000000..93d3a967114 --- /dev/null +++ b/tooling/lib/tooling/mappings/base.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require_relative '../../../../lib/gitlab_edition' + +# Returns system specs files that are related to the JS files that were changed in the MR. +module Tooling + module Mappings + class Base + # Input: A list of space-separated files + # Output: A list of space-separated specs files (JS, Ruby, ...) + def execute(changed_files) + raise "Not Implemented" + end + + # Input: A list of space-separated files + # Output: array/hash of files + def filter_files(changed_files) + raise "Not Implemented" + end + + # Input: A folder + # Output: An array of folders, each prefixed with a GitLab edition + def folders_for_available_editions(base_folder) + foss_prefix = base_folder + extension_prefixes = ::GitlabEdition.extensions.map { |prefix| "#{prefix}/#{foss_prefix}" } + [foss_prefix, *extension_prefixes] + end + end + end +end diff --git a/tooling/lib/tooling/mappings/js_to_system_specs_mappings.rb b/tooling/lib/tooling/mappings/js_to_system_specs_mappings.rb new file mode 100644 index 00000000000..365e466011b --- /dev/null +++ b/tooling/lib/tooling/mappings/js_to_system_specs_mappings.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'active_support/inflector' + +require_relative 'base' +require_relative '../../../../lib/gitlab_edition' + +# Returns system specs files that are related to the JS files that were changed in the MR. +module Tooling + module Mappings + class JsToSystemSpecsMappings < Base + def initialize(js_base_folder: 'app/assets/javascripts', system_specs_base_folder: 'spec/features') + @js_base_folder = js_base_folder + @js_base_folders = folders_for_available_editions(js_base_folder) + @system_specs_base_folder = system_specs_base_folder + + # Cannot be extracted to a constant, as it depends on a variable + @first_js_folder_extract_regexp = %r{ + (?:.*/)? # Skips the GitLab edition (e.g. ee/, jh/) + #{@js_base_folder}/ # Most likely app/assets/javascripts/ + ([\w-]*) # Captures the first folder + }x + end + + def execute(changed_files) + filter_files(changed_files).flat_map do |edition, js_files| + js_keywords_regexp = Regexp.union(construct_js_keywords(js_files)) + + system_specs_for_edition(edition).select do |system_spec_file| + system_spec_file if js_keywords_regexp.match?(system_spec_file) + end + end + end + + # Keep the files that are in the @js_base_folders folders + # + # Returns a hash, where the key is the GitLab edition, and the values the JS specs + def filter_files(changed_files) + selected_files = changed_files.select do |filename| + filename.start_with?(*@js_base_folders) && File.exist?(filename) + end + + selected_files.group_by { |filename| filename[/^#{Regexp.union(::GitlabEdition.extensions)}/] } + end + + # Extract keywords in the JS filenames to be used for searching matching system specs + def construct_js_keywords(js_files) + js_files.map do |js_file| + filename = js_file.scan(@first_js_folder_extract_regexp).flatten.first + filename.singularize + end.uniq + end + + def system_specs_for_edition(edition) + all_files_in_folders_glob = File.join(@system_specs_base_folder, '**', '*') + all_files_in_folders_glob = File.join(edition, all_files_in_folders_glob) if edition + Dir[all_files_in_folders_glob].select { |f| File.file?(f) } + end + end + end +end diff --git a/tooling/lib/tooling/mappings/view_to_js_mappings.rb b/tooling/lib/tooling/mappings/view_to_js_mappings.rb new file mode 100644 index 00000000000..db80eb9bfe8 --- /dev/null +++ b/tooling/lib/tooling/mappings/view_to_js_mappings.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require_relative 'base' +require_relative '../../../../lib/gitlab_edition' + +# Returns JS files that are related to the Rails views files that were changed in the MR. +module Tooling + module Mappings + class ViewToJsMappings < Base + # The HTML attribute value pattern we're looking for to match an HTML file to a JS file. + HTML_ATTRIBUTE_VALUE_REGEXP = /js-[-\w]+/.freeze + + # Search for Rails partials included in an HTML file + RAILS_PARTIAL_INVOCATION_REGEXP = %r{(?:render|render_if_exist)(?: |\()(?:partial: ?)?['"]([\w/-]+)['"]}.freeze + + def initialize(view_base_folder: 'app/views', js_base_folder: 'app/assets/javascripts') + @view_base_folders = folders_for_available_editions(view_base_folder) + @js_base_folders = folders_for_available_editions(js_base_folder) + end + + def execute(changed_files) + changed_view_files = filter_files(changed_files) + + partials = changed_view_files.flat_map do |file| + find_partials(file) + end + + files_to_scan = changed_view_files + partials + js_tags = files_to_scan.flat_map do |file| + find_pattern_in_file(file, HTML_ATTRIBUTE_VALUE_REGEXP) + end + js_tags_regexp = Regexp.union(js_tags) + + @js_base_folders.flat_map do |js_base_folder| + Dir["#{js_base_folder}/**/*.{js,vue}"].select do |js_file| + file_content = File.read(js_file) + js_tags_regexp.match?(file_content) + end + end + end + + # Keep the files that are in the @view_base_folders folder + def filter_files(changed_files) + changed_files.select do |filename| + filename.start_with?(*@view_base_folders) && + File.exist?(filename) + end + end + + # Note: We only search for partials with depth 1. We don't do recursive search, as + # it is probably not necessary for a first iteration. + def find_partials(file) + partial_paths = find_pattern_in_file(file, RAILS_PARTIAL_INVOCATION_REGEXP) + partial_paths.flat_map do |partial_path| + view_file_folder = File.dirname(file) + partial_relative_folder = File.dirname(partial_path) + + dirname = + if partial_relative_folder == '.' # The partial is in the same folder as the HTML file + view_file_folder + else + File.join(view_file_folder, partial_relative_folder) + end + + Dir["#{dirname}/_#{File.basename(partial_path)}.*"] + end + end + + def find_pattern_in_file(file, pattern) + File.read(file).scan(pattern).flatten.uniq + end + end + end +end diff --git a/tooling/lib/tooling/view_to_js_mappings.rb b/tooling/lib/tooling/view_to_js_mappings.rb deleted file mode 100644 index 76704a04469..00000000000 --- a/tooling/lib/tooling/view_to_js_mappings.rb +++ /dev/null @@ -1,77 +0,0 @@ -# frozen_string_literal: true - -require_relative '../../../lib/gitlab_edition' - -# Returns JS files that are related to the Rails views files that were changed in the MR. -module Tooling - class ViewToJsMappings - # The HTML attribute value pattern we're looking for to match an HTML file to a JS file. - HTML_ATTRIBUTE_VALUE_REGEXP = /js-[-\w]+/.freeze - - # Search for Rails partials included in an HTML file - RAILS_PARTIAL_INVOCATION_REGEXP = %r{(?:render|render_if_exist)(?: |\()(?:partial: ?)?['"]([\w/-]+)['"]}.freeze - - def initialize(view_base_folder: 'app/views', js_base_folder: 'app/assets/javascripts') - @view_base_folders = folders_for_available_editions(view_base_folder) - @js_base_folders = folders_for_available_editions(js_base_folder) - end - - def execute(changed_files) - changed_view_files = view_files(changed_files) - - partials = changed_view_files.flat_map do |file| - find_partials(file) - end - - files_to_scan = changed_view_files + partials - js_tags = files_to_scan.flat_map do |file| - find_pattern_in_file(file, HTML_ATTRIBUTE_VALUE_REGEXP) - end - js_tags_regexp = Regexp.union(js_tags) - - @js_base_folders.flat_map do |js_base_folder| - Dir["#{js_base_folder}/**/*.{js,vue}"].select do |js_file| - file_content = File.read(js_file) - js_tags_regexp.match?(file_content) - end - end - end - - # Keep the files that are in the @view_base_folders folder - def view_files(changed_files) - changed_files.select do |filename| - filename.start_with?(*@view_base_folders) && - File.exist?(filename) - end - end - - def folders_for_available_editions(base_folder) - foss_prefix = base_folder - extension_prefixes = ::GitlabEdition.extensions.map { |prefix| "#{prefix}/#{foss_prefix}" } - [foss_prefix, *extension_prefixes] - end - - # Note: We only search for partials with depth 1. We don't do recursive search, as - # it is probably not necessary for a first iteration. - def find_partials(file) - partial_paths = find_pattern_in_file(file, RAILS_PARTIAL_INVOCATION_REGEXP) - partial_paths.flat_map do |partial_path| - view_file_folder = File.dirname(file) - partial_relative_folder = File.dirname(partial_path) - - dirname = - if partial_relative_folder == '.' # The partial is in the same folder as the HTML file - view_file_folder - else - File.join(view_file_folder, partial_relative_folder) - end - - Dir["#{dirname}/_#{File.basename(partial_path)}.*"] - end - end - - def find_pattern_in_file(file, pattern) - File.read(file).scan(pattern).flatten.uniq - end - end -end -- cgit v1.2.3