workarea-upgrade 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ module Workarea
2
+ module Upgrade
3
+ class Diff
4
+ module CurrentApp
5
+ def self.files
6
+ @files ||= WorkareaFile.find_files(Dir.pwd)
7
+ end
8
+
9
+ def self.decorators
10
+ @decorators ||= WorkareaFile.find_decorators(Dir.pwd)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,99 @@
1
+ module Workarea
2
+ module Upgrade
3
+ class Diff
4
+ class GemDiff
5
+ def initialize(from_root, to_root, options = {})
6
+ @from_root = from_root
7
+ @to_root = to_root
8
+ @options = options
9
+ end
10
+
11
+ def all
12
+ @all ||= from_files.reduce([]) do |results, from_file|
13
+ if to_file = find_to_file(from_file.relative_path)
14
+ diff = diff_files(from_file, to_file)
15
+ results << diff unless diff.blank?
16
+ end
17
+
18
+ results
19
+ end
20
+ end
21
+
22
+ def added
23
+ @added ||= to_files.reduce([]) do |results, to_file|
24
+ from_file = find_from_file(to_file.relative_path)
25
+ results << to_file.relative_path if from_file.blank?
26
+ results
27
+ end
28
+ end
29
+
30
+ def removed
31
+ @removed ||= from_files.reduce([]) do |results, from_file|
32
+ to_file = find_to_file(from_file.relative_path)
33
+ results << from_file.relative_path if to_file.blank?
34
+ results
35
+ end
36
+ end
37
+
38
+ def overridden
39
+ @overridden ||= find_app_diff(CurrentApp.files)
40
+ end
41
+
42
+ def decorated
43
+ @decorated ||= find_app_diff(CurrentApp.decorators)
44
+ end
45
+
46
+ def customized_files
47
+ CurrentApp.files.map(&:relative_path) +
48
+ CurrentApp.decorators.map(&:relative_path)
49
+ end
50
+
51
+ def for_current_app
52
+ @for_current_app ||= overridden + decorated
53
+ end
54
+
55
+ def from_files
56
+ @from_files ||= WorkareaFile.find_files(@from_root)
57
+ end
58
+
59
+ def to_files
60
+ @to_files ||= WorkareaFile.find_files(@to_root)
61
+ end
62
+
63
+ private
64
+
65
+ def diff_files(from_file, to_file)
66
+ Diffy::Diff.new(
67
+ from_file.absolute_path,
68
+ to_file.absolute_path,
69
+ source: 'files',
70
+ context: @options[:context].presence || 5,
71
+ include_diff_info: true
72
+ ).to_s(@options[:format].presence || :text)
73
+ end
74
+
75
+ def find_from_file(relative_path)
76
+ from_files.detect { |file| file.relative_path == relative_path }
77
+ end
78
+
79
+ def find_to_file(relative_path)
80
+ to_files.detect { |file| file.relative_path == relative_path }
81
+ end
82
+
83
+ def find_app_diff(files)
84
+ files.reduce([]) do |results, decorated_file|
85
+ from_file = find_from_file(decorated_file.relative_path)
86
+ to_file = find_to_file(decorated_file.relative_path)
87
+
88
+ if from_file.present? && to_file.present?
89
+ diff = diff_files(from_file, to_file)
90
+ results << diff unless diff.blank?
91
+ end
92
+
93
+ results
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,43 @@
1
+ module Workarea
2
+ module Upgrade
3
+ class Diff
4
+ class WorkareaFile
5
+ attr_reader :relative_path
6
+
7
+ def self.find_files(root)
8
+ Dir.glob("#{root}/**/*")
9
+ .map { |f| f.gsub("#{root}/", '') }
10
+ .select do |relative|
11
+ !File.directory?("#{root}/#{relative}") &&
12
+ !relative.include?('decorators/') &&
13
+ (relative.include?('app/') || relative.include?('lib/')) &&
14
+ !relative.end_with?('.decorator')
15
+ end
16
+ .map { |relative| new(root, relative) }
17
+ end
18
+
19
+ def self.find_decorators(root)
20
+ Dir.glob("#{root}/app/decorators/**/*")
21
+ .reject { |file| File.directory?(file) }
22
+ .map { |file| file.gsub(root, '') }
23
+ .map { |file| file.gsub('/app/decorators', 'app') }
24
+ .map { |file| file.gsub('_decorator.rb', '.rb') }
25
+ .map { |file| new(nil, file) } +
26
+ Dir.glob("#{root}/app/**/*.decorator")
27
+ .map { |file| file.gsub("#{root}/", '') }
28
+ .map { |file| file.gsub('.decorator', '.rb') }
29
+ .map { |file| new(nil, file) }
30
+ end
31
+
32
+ def initialize(root, relative_path)
33
+ @root = root
34
+ @relative_path = relative_path
35
+ end
36
+
37
+ def absolute_path
38
+ "#{@root}/#{@relative_path}"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,12 @@
1
+ module Workarea
2
+ module Upgrade
3
+ class Engine < ::Rails::Engine
4
+ include Workarea::Plugin
5
+ isolate_namespace Workarea::Upgrade
6
+
7
+ initializer 'workarea.upgrade' do
8
+ # configure and customize Workarea here
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,102 @@
1
+ module Workarea
2
+ module Upgrade
3
+ class Gemfile
4
+ def initialize(filename = 'Gemfile')
5
+ @filename = filename
6
+ end
7
+
8
+ def self.diff(gemfile, gemfile_next)
9
+ gemfile_next.installed.reject do |gem|
10
+ gemfile.installed.to_h[gem.first] != gem.second
11
+ end
12
+ end
13
+
14
+ def exist?
15
+ File.exist?(@filename)
16
+ end
17
+
18
+ def lockfile_exist?
19
+ File.exist?("#{@filename}.lock")
20
+ end
21
+
22
+ def workarea
23
+ installed.select { |g| g.first == 'workarea' }
24
+ end
25
+
26
+ def workarea_version
27
+ workarea.first.last
28
+ end
29
+
30
+ def plugins(ignored = [])
31
+ installed.reject do |gem|
32
+ gem.first == 'workarea' || ignored.include?(gem.first)
33
+ end
34
+ end
35
+
36
+ def check_install
37
+ Bundler.with_original_env do
38
+ system("bundle install --gemfile #{@filename} > /dev/null")
39
+ $? == 0
40
+ end
41
+ end
42
+
43
+ def install!
44
+ Bundler.with_original_env do
45
+ `bundle install --gemfile #{@filename}`
46
+ end
47
+ end
48
+
49
+ def installed
50
+ @installed ||=
51
+ install!
52
+ .split("\n")
53
+ .select { |g| g.start_with?('Using workarea') }
54
+ .map(&:split)
55
+ .map { |g| [g[1], g[2]] }
56
+ .reject { |g| core_engines.include?(g.first) }
57
+ end
58
+
59
+ def all_gems(ignored = [])
60
+ installed.reject { |g| ignored.include?(g.first) }
61
+ end
62
+
63
+ def outdated
64
+ @outdated ||=
65
+ Bundler.with_original_env do
66
+ `bundle outdated --parseable`
67
+ .split("\n")
68
+ .select { |g| g.start_with?('workarea') }
69
+ .map { |g| g.gsub(/([^\s]*)\s*\(newest\s([^,]*).*/, '\\1|\\2') }
70
+ .map { |g| g.split('|') }
71
+ .reject { |g| core_engines.include?(g.first) }
72
+ end
73
+ end
74
+
75
+ def list
76
+ @list ||=
77
+ Bundler.with_original_env do
78
+ `bundle list`
79
+ .split("\n")
80
+ .select { |g| g.start_with?(' * workarea') }
81
+ .map { |g| g.gsub(/[ *]+([^\s]*)\s*\(([^)]*).*/, '\\1|\\2') }
82
+ .map { |g| g.split('|') }
83
+ .reject { |g| core_engines.include?(g.first) }
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def core_engines
90
+ %w[
91
+ admin
92
+ ci
93
+ core
94
+ storefront
95
+ testing
96
+ api-admin
97
+ api-storefront
98
+ ].map { |e| "workarea-#{e}" }
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,176 @@
1
+ module Workarea
2
+ module Upgrade
3
+ class Report
4
+ CATEGORIES = %w(
5
+ assets
6
+ controllers
7
+ helpers
8
+ mailers
9
+ middleware
10
+ models
11
+ queries
12
+ seeds
13
+ services
14
+ view_models
15
+ views
16
+ workers
17
+ )
18
+
19
+ def initialize(diff)
20
+ @diff = diff
21
+ end
22
+
23
+ def results
24
+ CATEGORIES.inject({}) do |memo, category|
25
+ memo[category] = calculate_grade(category)
26
+ memo
27
+ end
28
+ end
29
+
30
+ def customized_percents
31
+ @customized_percents ||= CATEGORIES.inject({}) do |memo, category|
32
+ percent_customized = customized_totals[category] / changed_totals[category].to_f
33
+ percent_customized *= 100
34
+
35
+ memo[category] = percent_customized.nan? ? 0 : percent_customized.round
36
+ memo
37
+ end
38
+ end
39
+
40
+ # If a file was decorated or overridden and removed, this is the
41
+ # biggest pain point.
42
+ def worst_files
43
+ @worst_files ||= CATEGORIES.inject({}) do |memo, category|
44
+ memo[category] = customized_files_now_missing
45
+ .select { |f| f.include?(category) }
46
+ .count
47
+
48
+ memo
49
+ end
50
+ end
51
+
52
+ def changed_totals
53
+ @changed_totals ||= CATEGORIES.inject({}) do |memo, category|
54
+ memo[category] = @diff.all.select { |f| f.include?(category) }.count
55
+ memo
56
+ end
57
+ end
58
+
59
+ def customized_totals
60
+ @customized_totals ||= CATEGORIES.inject({}) do |memo, category|
61
+ memo[category] = @diff.for_current_app.select { |f| f.include?(category) }.count
62
+ memo
63
+ end
64
+ end
65
+
66
+ def calculate_grade(category)
67
+ customized_total = customized_totals[category]
68
+ return 'A' if customized_total <= 3
69
+
70
+ percent_customized = customized_percents[category]
71
+ return 'A' if percent_customized < 5
72
+
73
+ score = worst_files[category]
74
+ score += percent_customized
75
+
76
+ if score.between?(0, 9)
77
+ 'A'
78
+ elsif score.between?(10, 24)
79
+ 'B'
80
+ elsif score.between?(25, 34)
81
+ 'C'
82
+ elsif score.between?(35, 44)
83
+ 'D'
84
+ else
85
+ 'F'
86
+ end
87
+ end
88
+
89
+ def diff_stats
90
+ [
91
+ {
92
+ status: ">>> #{pluralize(@diff.all.length, 'file')}",
93
+ message: 'modified in Workarea',
94
+ color: :yellow
95
+ },
96
+ {
97
+ status: ">>> #{pluralize(@diff.overridden.length, 'file')}",
98
+ message: 'overridden in your application may be be affected',
99
+ color: :yellow
100
+ },
101
+ {
102
+ status: ">>> #{pluralize(@diff.decorated.length, 'file')}",
103
+ message: 'decorated in your application may be be affected',
104
+ color: :yellow
105
+ },
106
+ {
107
+ status: "+++ #{pluralize(@diff.added.length, "file")}",
108
+ message: 'added to Workarea',
109
+ color: :green
110
+ },
111
+ {
112
+ status: "--- #{pluralize(@diff.removed.length, 'file')}",
113
+ message: 'removed from Workarea',
114
+ color: :red
115
+ }
116
+ ]
117
+ end
118
+
119
+ def report_card_stats
120
+ results.map do |category, grade|
121
+ color = :yellow
122
+ color = :green if grade.in?(%w(A B))
123
+ color = :red if grade == 'F'
124
+
125
+ {
126
+ status: "Grade: #{grade}",
127
+ message: category,
128
+ color: color
129
+ }
130
+ end
131
+ end
132
+
133
+ def breakdown_customized_stats
134
+ results
135
+ .reject { |category, _grade| customized_totals[category] == 0 }
136
+ .map do |category, _grade|
137
+ {
138
+ status: category,
139
+ message: <<~MESSAGE.gsub(/\n/, ' '),
140
+ #{pluralize(customized_totals[category], 'file')}
141
+ (#{customized_percents[category]}%) overridden or decorated
142
+ in this application have been changed in Workarea
143
+ MESSAGE
144
+ color: :yellow
145
+ }
146
+ end
147
+ end
148
+
149
+ def breakdown_worst_files_stats
150
+ results
151
+ .reject { |category, _grade| worst_files[category] == 0 }
152
+ .map do |category, _grade|
153
+ {
154
+ status: category,
155
+ message: <<~MESSAGE.gsub(/\n/, ' '),
156
+ #{pluralize(worst_files[category], 'file')} overridden or
157
+ decorated in this application have been moved or removed from
158
+ Workarea
159
+ MESSAGE
160
+ color: :red
161
+ }
162
+ end
163
+ end
164
+
165
+ private
166
+
167
+ def customized_files_now_missing
168
+ @diff.customized_files & @diff.removed
169
+ end
170
+
171
+ def pluralize(*args)
172
+ ActionController::Base.helpers.pluralize(*args)
173
+ end
174
+ end
175
+ end
176
+ end