1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
# frozen_string_literal: true
require_relative 'base'
require_relative '../../../../lib/gitlab_edition'
# Returns view files that include the potential rails partials from the changed files passed as input.
module Tooling
module Mappings
class PartialToViewsMappings < Base
def initialize(changes_file, output_file, view_base_folder: 'app/views')
@output_file = output_file
@changed_files = File.read(changes_file).split(' ')
@view_base_folders = folders_for_available_editions(view_base_folder)
end
def execute
views_including_modified_partials = []
views_globs = view_base_folders.map { |view_base_folder| "#{view_base_folder}/**/*.html.haml" }
Dir[*views_globs].each do |view_file|
included_partial_names = find_pattern_in_file(view_file, partials_keywords_regexp)
next if included_partial_names.empty?
included_partial_names.each do |included_partial_name|
if view_includes_modified_partial?(view_file, included_partial_name)
views_including_modified_partials << view_file
end
end
end
File.write(output_file, views_including_modified_partials.join(' '))
end
def filter_files
@_filter_files ||= changed_files.select do |filename|
filename.start_with?(*view_base_folders) &&
File.basename(filename).start_with?('_') &&
File.basename(filename).end_with?('.html.haml') &&
File.exist?(filename)
end
end
def partials_keywords_regexp
partial_keywords = filter_files.map do |partial_filename|
extract_partial_keyword(partial_filename)
end
partial_regexps = partial_keywords.map do |keyword|
%r{(?:render|render_if_exists)(?: |\()(?:partial: ?)?['"]([\w\-_/]*#{keyword})['"]}
end
Regexp.union(partial_regexps)
end
# e.g. if app/views/clusters/clusters/_sidebar.html.haml was modified, the partial keyword is `sidebar`.
def extract_partial_keyword(partial_filename)
File.basename(partial_filename).delete_prefix('_').delete_suffix('.html.haml')
end
# Why do we need this method?
#
# Assume app/views/clusters/clusters/_sidebar.html.haml was modified in the MR.
#
# Suppose now you find = render 'sidebar' in a view. Is this view including the sidebar partial
# that was modified, or another partial called "_sidebar.html.haml" somewhere else?
def view_includes_modified_partial?(view_file, included_partial_name)
view_file_parent_folder = File.dirname(view_file)
included_partial_filename = reconstruct_partial_filename(included_partial_name)
included_partial_relative_path = File.join(view_file_parent_folder, included_partial_filename)
# We do this because in render (or render_if_exists)
# apparently looks for partials in other GitLab editions
#
# Example:
#
# ee/app/views/events/_epics_filter.html.haml is used in app/views/shared/_event_filter.html.haml
# with render_if_exists 'events/epics_filter'
included_partial_absolute_paths = view_base_folders.map do |view_base_folder|
File.join(view_base_folder, included_partial_filename)
end
filter_files.include?(included_partial_relative_path) ||
(filter_files & included_partial_absolute_paths).any?
end
def reconstruct_partial_filename(partial_name)
partial_path = partial_name.split('/')[..-2]
partial_filename = partial_name.split('/').last
full_partial_filename = "_#{partial_filename}.html.haml"
return full_partial_filename if partial_path.empty?
File.join(partial_path.join('/'), full_partial_filename)
end
def find_pattern_in_file(file, pattern)
File.read(file).scan(pattern).flatten.uniq
end
private
attr_reader :changed_files, :output_file, :view_base_folders
end
end
end
|