danger-app_size_report 0.0.1 → 0.0.3
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.
- checksums.yaml +4 -4
- data/Gemfile +3 -1
- data/Gemfile.lock +170 -0
- data/Guardfile +2 -0
- data/LICENSE +21 -0
- data/README.md +46 -8
- data/Rakefile +2 -0
- data/Resources/App Thinning Size Report.txt +57 -0
- data/Resources/expectedReportJSON.json +590 -0
- data/danger-app_size_report.gemspec +13 -11
- data/lib/app_size_report/gem_version.rb +3 -1
- data/lib/app_size_report/plugin.rb +152 -78
- data/lib/converter/helper/json_converter.rb +14 -0
- data/lib/converter/helper/memory_size.rb +107 -0
- data/lib/converter/helper/result_factory.rb +34 -0
- data/lib/converter/models/app_size_model.rb +32 -0
- data/lib/converter/models/device_model.rb +17 -0
- data/lib/converter/models/variant_model.rb +24 -0
- data/lib/converter/parser/app_size_parser.rb +48 -0
- data/lib/converter/parser/model_parser.rb +13 -0
- data/lib/converter/parser/report_parser.rb +65 -0
- data/lib/converter/parser/variant_descriptor_parser.rb +53 -0
- data/lib/converter/parser/variant_parser.rb +10 -0
- data/lib/danger_app_size_report.rb +3 -1
- data/lib/danger_plugin.rb +3 -1
- data/spec/app_size_report_spec.rb +18 -27
- data/spec/spec_helper.rb +19 -17
- metadata +35 -21
- data/LICENSE.txt +0 -22
@@ -1,111 +1,185 @@
|
|
1
|
-
|
2
|
-
require 'set'
|
1
|
+
# frozen_string_literal: false
|
3
2
|
|
4
3
|
module Danger
|
5
|
-
|
4
|
+
require 'json'
|
5
|
+
require_relative '../converter/parser/report_parser'
|
6
|
+
require_relative '../converter/helper/memory_size'
|
7
|
+
|
8
|
+
# A Danger plugin for reporting iOS app size violations.
|
9
|
+
# A valid App Thinning Size Report must be passed to the plugin
|
10
|
+
# for accurate functionality.
|
11
|
+
#
|
12
|
+
# @example Report app size violations if one or more App variants
|
13
|
+
# exceed 4GB.
|
14
|
+
#
|
15
|
+
# report_path = "/Path/to/AppSize/Report.txt"
|
16
|
+
# app_size_report.flag_violations(
|
17
|
+
# report_path,
|
18
|
+
# build_type: 'App',
|
19
|
+
# size_limit: 4,
|
20
|
+
# limit_unit: 'GB',
|
21
|
+
# fail_on_warning: false
|
22
|
+
# )
|
23
|
+
#
|
24
|
+
# @example Report app size violations if one or more App Clip variants
|
25
|
+
# exceed 8MB.
|
6
26
|
#
|
7
|
-
#
|
27
|
+
# report_path = "/Path/to/AppSize/Report.txt"
|
28
|
+
# app_size_report.flag_violations(
|
29
|
+
# report_path,
|
30
|
+
# build_type: 'Clip',
|
31
|
+
# size_limit: 8,
|
32
|
+
# limit_unit: 'MB',
|
33
|
+
# fail_on_warning: false
|
34
|
+
# )
|
8
35
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# # The result is sent to the pull request with a markdown format and
|
12
|
-
# # notifies failure which variants is violating the size limit.
|
36
|
+
# @example Fail PR if one or more App Clip variants exceed 8MB.
|
13
37
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
38
|
+
# report_path = "/Path/to/AppSize/Report.txt"
|
39
|
+
# app_size_report.flag_violations(
|
40
|
+
# report_path,
|
41
|
+
# build_type: 'Clip',
|
42
|
+
# size_limit: 8,
|
43
|
+
# limit_unit: 'MB',
|
44
|
+
# fail_on_warning: true
|
45
|
+
# )
|
46
|
+
#
|
47
|
+
# @example Get JSON string representation of app thinning size report
|
48
|
+
#
|
49
|
+
# report_path = "/Path/to/AppSize/Report.txt"
|
50
|
+
# app_size_json = app_size_report.report_json(report_path)
|
51
|
+
#
|
52
|
+
# @see ChargePoint/danger-app_size_report
|
53
|
+
# @tags ios, xcode, appclip, thinning, size
|
18
54
|
#
|
19
55
|
class DangerAppSizeReport < Plugin
|
20
|
-
#
|
21
|
-
# @
|
56
|
+
# Reports app size violations given a valid App Thinning Size Report.
|
57
|
+
# @param [String, required] report_path
|
58
|
+
# Path to valid App Thinning Size Report text file.
|
59
|
+
# @param [String, optional] build_type
|
60
|
+
# Specify whether the report corresponds to an App or an App Clip.
|
61
|
+
# Default: 'App'
|
62
|
+
# Supported values: 'App', 'Clip'
|
63
|
+
# @param [Numeric, optional] size_limit
|
64
|
+
# Specify the app size limit.
|
65
|
+
# Default: 4
|
66
|
+
# @param [String, optional] limit_unit
|
67
|
+
# Specific the unit for the given size limit.
|
68
|
+
# Default: 'GB'
|
69
|
+
# Supported values: 'KB', 'MB', 'GB'
|
70
|
+
# @param [Boolean, optional] fail_on_warning
|
71
|
+
# Specify whether the PR should fail if one or more app variants
|
72
|
+
# exceed the given size limit. By default, the plugin issues
|
73
|
+
# a warning in this case.
|
74
|
+
# Default: 'false'
|
75
|
+
# @return [void]
|
22
76
|
#
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
json = JSON.parse(report)
|
27
|
-
|
28
|
-
flagged_variants = parse_json(*json, app_size_limit)
|
29
|
-
|
30
|
-
if flagged_variants.length > 0
|
31
|
-
failure "The App Clip size limit of 10 MB has been exceeded by one or more variants"
|
32
|
-
end
|
77
|
+
def flag_violations(report_path, build_type: 'App', size_limit: 4, limit_unit: 'GB', fail_on_warning: false)
|
78
|
+
report_text = File.read(report_path)
|
79
|
+
variants = ReportParser.parse(report_text)
|
33
80
|
|
34
|
-
|
35
|
-
|
36
|
-
init_ads()
|
81
|
+
unless %w[App Clip].include? build_type
|
82
|
+
raise ArgumentError, "The 'build_type' argument only accepts the values \"App\" and \"Clip\""
|
37
83
|
end
|
38
|
-
end
|
39
84
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
json.each do |variant, value|
|
47
|
-
app_size = variant["app_size"]["uncompressed"]["value"]
|
48
|
-
app_odr_size = variant["app_on_demand_resources_size"]["uncompressed"]["value"]
|
49
|
-
if app_size > app_size_limit || app_odr_size > app_size_limit
|
50
|
-
flagged_variants.append(variant)
|
51
|
-
end
|
85
|
+
raise ArgumentError, "The 'size_limit' argument only accepts numeric values" unless size_limit.is_a? Numeric
|
86
|
+
|
87
|
+
limit_unit.upcase!
|
88
|
+
unless %w[KB MB GB].include? limit_unit
|
89
|
+
raise ArgumentError, "The 'build_type' argument only accepts the values \"KB\", \"MB\" and \"GB\""
|
52
90
|
end
|
53
|
-
|
54
|
-
|
55
|
-
|
91
|
+
|
92
|
+
unless [true, false].include? fail_on_warning
|
93
|
+
raise ArgumentError, "The 'fail_on_warning' argument only accepts the values 'true' and 'false'"
|
56
94
|
end
|
57
95
|
|
58
|
-
|
96
|
+
generate_size_report_markdown(variants, build_type, size_limit, limit_unit, fail_on_warning)
|
97
|
+
generate_variant_descriptors_markdown(variants)
|
98
|
+
generate_ads_label_markdown
|
59
99
|
end
|
60
100
|
|
61
|
-
#
|
62
|
-
#
|
101
|
+
# Returns a JSON string representation of the given App Thinning Size Report.
|
102
|
+
# @param [String, required] report_path
|
103
|
+
# Path to valid App Thinning Size Report text file.
|
104
|
+
# @return [String]
|
63
105
|
#
|
64
|
-
def
|
106
|
+
def report_json(report_path)
|
107
|
+
report_text = File.read(report_path)
|
108
|
+
variants = ReportParser.parse(report_text)
|
109
|
+
JSON.pretty_generate(variants)
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def generate_size_report_markdown(variants, build_type, size_limit, limit_unit, fail_on_warning)
|
115
|
+
limit_size = MemorySize.new("#{size_limit}#{limit_unit}")
|
116
|
+
|
117
|
+
if build_type == 'Clip' && limit_size.megabytes > 10
|
118
|
+
message "The size limit was set to 10 MB as the given limit of #{size_limit} #{limit_unit} exceeds Apple's App Clip size restrictions"
|
119
|
+
size_limit = 10
|
120
|
+
limit_unit = 'MB'
|
121
|
+
limit_size.kilobytes = 10 * 1024
|
122
|
+
elsif build_type == 'App' && limit_size.gigabytes > 4
|
123
|
+
message "The size limit was set to 4 GB as the given limit of #{size_limit} #{limit_unit} exceeds Apple's App size restrictions"
|
124
|
+
size_limit = 4
|
125
|
+
limit_unit = 'GB'
|
126
|
+
limit_size.kilobytes = 4 * 1024 * 1024
|
127
|
+
end
|
128
|
+
|
129
|
+
flagged_variant_names = []
|
130
|
+
variants.each do |variant|
|
131
|
+
if variant.app_size.uncompressed.value > limit_size.megabytes || variant.on_demand_resources_size.uncompressed.value > limit_size.megabytes
|
132
|
+
flagged_variant_names.append(variant.variant)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
if flagged_variant_names.length.positive?
|
137
|
+
if fail_on_warning
|
138
|
+
failure "The size limit of #{size_limit} #{limit_unit.upcase} has been exceeded by one or more variants"
|
139
|
+
else
|
140
|
+
warn "The size limit of #{size_limit} #{limit_unit.upcase} has been exceeded by one or more variants"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
65
144
|
size_report = "# App Thinning Size Report\n"
|
66
|
-
size_report << "### Size limit = #{
|
145
|
+
size_report << "### Size limit = #{size_limit} #{limit_unit.upcase}\n\n"
|
67
146
|
size_report << "| Under Limit | Variant | App Size - Compressed | App Size - Uncompressed | ODR Size - Compressed | ODR Size - Uncompressed |\n"
|
68
|
-
size_report << "|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
147
|
+
size_report << "| :-: | :-: | :-: | :-: | :-: | :-: |\n"
|
148
|
+
|
149
|
+
flagged_variants_set = flagged_variant_names.to_set
|
150
|
+
|
151
|
+
variants.each do |variant|
|
152
|
+
is_violating = flagged_variants_set.include?(variant.variant) ? '❌' : '✅'
|
153
|
+
app_size_compressed = "#{variant.app_size.compressed.value} #{variant.app_size.compressed.unit}"
|
154
|
+
app_size_uncompressed = "#{variant.app_size.uncompressed.value} #{variant.app_size.uncompressed.unit}"
|
155
|
+
odr_size_compressed = "#{variant.on_demand_resources_size.compressed.value} #{variant.on_demand_resources_size.compressed.unit}"
|
156
|
+
odr_size_uncompressed = "#{variant.on_demand_resources_size.uncompressed.value} #{variant.on_demand_resources_size.uncompressed.unit}"
|
157
|
+
|
158
|
+
size_report << "#{is_violating} | #{variant.variant} | #{app_size_compressed} | #{app_size_uncompressed} | #{odr_size_compressed} | #{odr_size_uncompressed} |\n"
|
78
159
|
end
|
79
160
|
|
80
161
|
markdown size_report
|
81
162
|
end
|
82
163
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
variant_descriptor_report << "| ----- | ---------------- | \n"
|
93
|
-
variant["supported_variant_descriptors"].each do |model, _|
|
94
|
-
variant_descriptor_report << "#{model["device"]} | #{model["os_version"]} | \n"
|
164
|
+
def generate_variant_descriptors_markdown(variants)
|
165
|
+
variant_descriptors_report = "### Supported Variant Descriptors \n\n"
|
166
|
+
variants.each do |variant|
|
167
|
+
variant_descriptors_report << "<details> \n"
|
168
|
+
variant_descriptors_report << "<summary> #{variant.variant} </summary> \n\n"
|
169
|
+
variant_descriptors_report << "| Model | Operating System | \n"
|
170
|
+
variant_descriptors_report << "| - | :-: |\n"
|
171
|
+
variant.supported_variant_descriptors.each do |variant_descriptor|
|
172
|
+
variant_descriptors_report << "#{variant_descriptor.device} | #{variant_descriptor.os_version} | \n"
|
95
173
|
end
|
96
|
-
|
174
|
+
variant_descriptors_report << "</details> \n\n"
|
97
175
|
end
|
98
176
|
|
99
|
-
markdown
|
177
|
+
markdown variant_descriptors_report
|
100
178
|
end
|
101
179
|
|
102
|
-
|
103
|
-
|
104
|
-
#
|
105
|
-
def init_ads
|
106
|
-
ads_label = "Powered by [danger-app_size_report](https://github.com/ChargePoint)"
|
180
|
+
def generate_ads_label_markdown
|
181
|
+
ads_label = 'Powered by [danger-app_size_report](https://github.com/ChargePoint)'
|
107
182
|
markdown ads_label
|
108
183
|
end
|
109
|
-
|
110
184
|
end
|
111
185
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
class JSONConverter
|
6
|
+
def to_json(_options = {})
|
7
|
+
hash = {}
|
8
|
+
instance_variables.each do |var|
|
9
|
+
key = var.to_s[1..]
|
10
|
+
hash[key] = instance_variable_get(var)
|
11
|
+
end
|
12
|
+
JSON.pretty_generate(hash)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
require_relative '../helper/json_converter'
|
4
|
+
class MemorySize < JSONConverter
|
5
|
+
attr_accessor :kilobytes
|
6
|
+
|
7
|
+
ZERO_SIZE = 'zero kb'.freeze
|
8
|
+
|
9
|
+
UNIT = {
|
10
|
+
bytes: 'B',
|
11
|
+
kilobytes: 'KB',
|
12
|
+
megabytes: 'MB',
|
13
|
+
gigabytes: 'GB'
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def bytes
|
17
|
+
@kilobytes * 1024
|
18
|
+
end
|
19
|
+
|
20
|
+
def megabytes
|
21
|
+
@kilobytes / 1024
|
22
|
+
end
|
23
|
+
|
24
|
+
def gigabytes
|
25
|
+
@kilobytes / 1024 / 1024
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(text)
|
29
|
+
super()
|
30
|
+
value = parse_from(text)
|
31
|
+
|
32
|
+
@kilobytes = value || 0
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def parse_from(text)
|
38
|
+
text_to_memory_unit = {
|
39
|
+
'b' => :bytes,
|
40
|
+
'byte' => :bytes,
|
41
|
+
'bytes' => :bytes,
|
42
|
+
'kb' => :kilobytes,
|
43
|
+
'kilobyte' => :kilobytes,
|
44
|
+
'kilobytes' => :kilobytes,
|
45
|
+
'mb' => :megabytes,
|
46
|
+
'megabyte' => :megabytes,
|
47
|
+
'megabytes' => :megabytes,
|
48
|
+
'gb' => :gigabytes,
|
49
|
+
'gigabyte' => :gigabytes,
|
50
|
+
'gigabytes' => :gigabytes
|
51
|
+
}
|
52
|
+
|
53
|
+
unit = text_to_memory_unit[parse_units(text)]
|
54
|
+
size = parse_size(text)
|
55
|
+
|
56
|
+
return nil unless size
|
57
|
+
|
58
|
+
unit ||= :megabytes
|
59
|
+
|
60
|
+
case unit
|
61
|
+
when :bytes
|
62
|
+
kilobytes_from_bytes(size)
|
63
|
+
when :kilobytes
|
64
|
+
size
|
65
|
+
when :megabytes
|
66
|
+
kilobytes_from_megabytes(size)
|
67
|
+
when :gigabytes
|
68
|
+
kilobytes_from_gigabytes(size)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_units(text)
|
73
|
+
return 'kb' if text.downcase == ZERO_SIZE
|
74
|
+
|
75
|
+
result = ''
|
76
|
+
|
77
|
+
text.each_char do |char|
|
78
|
+
result << char if char.match?(/[[:alpha:]]/) && char != '.' && char != ','
|
79
|
+
end
|
80
|
+
|
81
|
+
result.downcase
|
82
|
+
end
|
83
|
+
|
84
|
+
def parse_size(text)
|
85
|
+
return 0.to_f if text.downcase == ZERO_SIZE
|
86
|
+
|
87
|
+
result = ''
|
88
|
+
|
89
|
+
text.each_char do |char|
|
90
|
+
result << char if char.match?(/[[:digit:]]/) || char == '.' || char == ','
|
91
|
+
end
|
92
|
+
|
93
|
+
result.to_f
|
94
|
+
end
|
95
|
+
|
96
|
+
def kilobytes_from_bytes(bytes)
|
97
|
+
bytes / 1024
|
98
|
+
end
|
99
|
+
|
100
|
+
def kilobytes_from_megabytes(megabytes)
|
101
|
+
megabytes * 1024
|
102
|
+
end
|
103
|
+
|
104
|
+
def kilobytes_from_gigabytes(gigabytes)
|
105
|
+
gigabytes * 1024 * 1024
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../parser/variant_parser'
|
4
|
+
require_relative '../parser/variant_descriptor_parser'
|
5
|
+
require_relative '../parser/app_size_parser'
|
6
|
+
|
7
|
+
class ResultFactory
|
8
|
+
def self.parse(from_text: '', parser: nil)
|
9
|
+
result = nil
|
10
|
+
case parser
|
11
|
+
when :variant
|
12
|
+
variant_parser = VariantParser.new(from_text)
|
13
|
+
variant_parser.parse
|
14
|
+
result = variant_parser.result
|
15
|
+
when :supported_variant_descriptors
|
16
|
+
variant_descriptor_parser = VariantDescriptorParser.new(from_text)
|
17
|
+
variant_descriptor_parser.parse
|
18
|
+
result = variant_descriptor_parser.result
|
19
|
+
when :app_on_demand_resources_size
|
20
|
+
app_size_parser = AppSizeParser.new(from_text)
|
21
|
+
app_size_parser.parse
|
22
|
+
result = app_size_parser.result
|
23
|
+
when :app_size
|
24
|
+
app_size_parser = AppSizeParser.new(from_text)
|
25
|
+
app_size_parser.parse
|
26
|
+
result = app_size_parser.result
|
27
|
+
when :on_demand_resources_size
|
28
|
+
app_size_parser = AppSizeParser.new(from_text)
|
29
|
+
app_size_parser.parse
|
30
|
+
result = app_size_parser.result
|
31
|
+
end
|
32
|
+
result
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../helper/json_converter'
|
4
|
+
class AppSizeModel < JSONConverter
|
5
|
+
attr_reader :compressed, :uncompressed
|
6
|
+
|
7
|
+
PARSING_KEYS = {
|
8
|
+
compressed: 'compressed',
|
9
|
+
uncompressed: 'uncompressed'
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
def initialize(compressed: SizeModel.placeholder, uncompressed: SizeModel.placeholder)
|
13
|
+
super()
|
14
|
+
@compressed = compressed
|
15
|
+
@uncompressed = uncompressed
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class SizeModel < JSONConverter
|
20
|
+
attr_reader :raw_value, :value, :unit
|
21
|
+
|
22
|
+
def initialize(raw_value, value, unit)
|
23
|
+
super()
|
24
|
+
@raw_value = raw_value
|
25
|
+
@value = value
|
26
|
+
@unit = unit
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.placeholder
|
30
|
+
SizeModel.new('Unknown', 0, MemorySize::UNIT[:bytes])
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../helper/json_converter'
|
4
|
+
class DeviceModel < JSONConverter
|
5
|
+
attr_reader :device, :os_version
|
6
|
+
|
7
|
+
PARSING_KEYS = {
|
8
|
+
device: 'device: ',
|
9
|
+
os_version: 'os-version: '
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
def initialize(device, os_version)
|
13
|
+
super()
|
14
|
+
@device = device
|
15
|
+
@os_version = os_version
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../helper/json_converter'
|
4
|
+
class VariantModel < JSONConverter
|
5
|
+
attr_reader :variant, :supported_variant_descriptors, :app_on_demand_resources_size, :app_size,
|
6
|
+
:on_demand_resources_size
|
7
|
+
|
8
|
+
PARSING_KEYS = {
|
9
|
+
variant: 'Variant: ',
|
10
|
+
supported_variant_descriptors: 'Supported variant descriptors: ',
|
11
|
+
app_on_demand_resources_size: 'App + On Demand Resources size: ',
|
12
|
+
app_size: 'App size: ',
|
13
|
+
on_demand_resources_size: 'On Demand Resources size: '
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def initialize(variant, supported_variant_descriptors, app_on_demand_resources_size, app_size, on_demand_resources_size)
|
17
|
+
super()
|
18
|
+
@variant = variant
|
19
|
+
@supported_variant_descriptors = supported_variant_descriptors
|
20
|
+
@app_on_demand_resources_size = app_on_demand_resources_size
|
21
|
+
@app_size = app_size
|
22
|
+
@on_demand_resources_size = on_demand_resources_size
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
require_relative '../models/app_size_model'
|
4
|
+
require_relative '../helper/memory_size'
|
5
|
+
require_relative './model_parser'
|
6
|
+
|
7
|
+
class AppSizeParser < ModelParser
|
8
|
+
attr_reader :standardized_unit
|
9
|
+
|
10
|
+
def initialize(text, standardized_unit: MemorySize::UNIT[:megabytes])
|
11
|
+
super(text)
|
12
|
+
@standardized_unit = standardized_unit
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse
|
16
|
+
@text = @text.strip
|
17
|
+
if @text.empty?
|
18
|
+
@result = nil
|
19
|
+
else
|
20
|
+
parseable_text = @text.split(', ')
|
21
|
+
properties = {}
|
22
|
+
parsing_keys = AppSizeModel::PARSING_KEYS
|
23
|
+
parseable_text.each do |size_text|
|
24
|
+
parsing_keys.each do |_property, key|
|
25
|
+
if size_text.include?(key) && !properties.fetch(key, nil)
|
26
|
+
value = size_text.gsub!(key, '')
|
27
|
+
properties[key] = value.strip
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
compressed_string = properties.fetch(parsing_keys[:compressed], nil)
|
33
|
+
uncompressed_string = properties.fetch(parsing_keys[:uncompressed], nil)
|
34
|
+
compressed_value = MemorySize.new(compressed_string).megabytes
|
35
|
+
uncompressed_value = MemorySize.new(uncompressed_string).megabytes
|
36
|
+
|
37
|
+
if compressed_string && uncompressed_string && compressed_value && uncompressed_value
|
38
|
+
compressed_raw_value = compressed_string.downcase == MemorySize::ZERO_SIZE ? '0 KB' : compressed_string
|
39
|
+
compressed_size = SizeModel.new(compressed_raw_value, compressed_value, @standardized_unit)
|
40
|
+
uncompressed_raw_value = uncompressed_string.downcase == MemorySize::ZERO_SIZE ? '0 KB' : uncompressed_string
|
41
|
+
uncompressed_size = SizeModel.new(uncompressed_raw_value, uncompressed_value, @standardized_unit)
|
42
|
+
@result = AppSizeModel.new(compressed: compressed_size, uncompressed: uncompressed_size)
|
43
|
+
else
|
44
|
+
@result = AppSizeModel.new
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
require_relative '../models/variant_model'
|
5
|
+
require_relative '../helper/result_factory'
|
6
|
+
|
7
|
+
class ReportParser
|
8
|
+
def self.parse(text)
|
9
|
+
splitter_id = SecureRandom.uuid
|
10
|
+
|
11
|
+
# First we trim the report text
|
12
|
+
preprocessed_data = text.strip
|
13
|
+
|
14
|
+
preprocessed_data.gsub!(/\n{2,3}/, "\n#{splitter_id}\n")
|
15
|
+
|
16
|
+
# Also append the splitter ID to the end of the text so we do not miss the last variant
|
17
|
+
preprocessed_data += "\n#{splitter_id}\n"
|
18
|
+
|
19
|
+
data = preprocessed_data.split("\n")
|
20
|
+
|
21
|
+
variants = []
|
22
|
+
dict = {}
|
23
|
+
|
24
|
+
data.each do |value|
|
25
|
+
parsing_keys = VariantModel::PARSING_KEYS
|
26
|
+
if value == splitter_id && dict.fetch(parsing_keys[:variant], nil)
|
27
|
+
variant = dict.fetch(parsing_keys[:variant], '')
|
28
|
+
supported_variant_descriptors = dict.fetch(parsing_keys[:supported_variant_descriptors], '')
|
29
|
+
app_on_demand_resources_size = dict.fetch(parsing_keys[:app_on_demand_resources_size], '')
|
30
|
+
app_size = dict.fetch(parsing_keys[:app_size], '')
|
31
|
+
on_demand_resources_size = dict.fetch(parsing_keys[:on_demand_resources_size], '')
|
32
|
+
|
33
|
+
# initialize variant model from all the parser result
|
34
|
+
model = VariantModel.new(variant,
|
35
|
+
supported_variant_descriptors,
|
36
|
+
app_on_demand_resources_size,
|
37
|
+
app_size,
|
38
|
+
on_demand_resources_size)
|
39
|
+
|
40
|
+
variants.append(model)
|
41
|
+
|
42
|
+
# reset all the properties
|
43
|
+
dict = {}
|
44
|
+
end
|
45
|
+
|
46
|
+
parsing_keys.each do |property, key|
|
47
|
+
next unless value.include? key
|
48
|
+
|
49
|
+
# clean the key from the text
|
50
|
+
# i.e. "Variant: ChargePointAppClip-354363463-...." remove the "Variant: " so we have a clean text that we can parse ("ChargePointAppClip-354363463-....")
|
51
|
+
# i.e. "Supported variant descriptors: [device: iPhone10,3, os-version:14.0], ..." will pass only the "[device: iPhone10,3, os-version:14.0], ..." to the parser
|
52
|
+
if (key == parsing_keys[:on_demand_resources_size]) && (value.include? parsing_keys[:app_on_demand_resources_size])
|
53
|
+
parseable_text = value.gsub(parsing_keys[:app_on_demand_resources_size], '')
|
54
|
+
dict[key] = parseable_text
|
55
|
+
dict[key] = ResultFactory.parse(from_text: parseable_text, parser: :app_on_demand_resources_size)
|
56
|
+
else
|
57
|
+
parseable_text = value.gsub(key, '')
|
58
|
+
dict[key] = parseable_text
|
59
|
+
dict[key] = ResultFactory.parse(from_text: parseable_text, parser: property)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
variants
|
64
|
+
end
|
65
|
+
end
|