workarea-upgrade 3.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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