danger-app_size_report 0.0.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9b365600ee021d284beda6dcbfbf0458f1a5a70e57e5602de4f90fa96a42e89d
4
- data.tar.gz: 0c673c72637fd341e61c43ec044bdf9c5e2742e9571920269dfdacf8a2c97cb7
3
+ metadata.gz: 51678b949d92e02ba3e1a894123ac553027e7fad07dba8157877296e4177e6a2
4
+ data.tar.gz: '044917b9b27fe9bb6677649051d1757ffebe2cc42df99e02266cf5f8608d73c9'
5
5
  SHA512:
6
- metadata.gz: df752062c2f4f08a90f1ccaf9d153f25e1c782e4c342c22529175989a44dd0187470abeeabc88e726b8d0cd31f14df9abafd6c7f22b3c7b8d2e672f2556fde82
7
- data.tar.gz: 414f43733a1c575c051ed0bfce2a9bf9e28438facb30c361080fef18d58c4ecb58d12ad86806ce17feefa0ab8f8f52137a8a9e71bb91a2a1986003d392ee9b23
6
+ metadata.gz: 922c09c181eec687676307efd9031bc4634f4965388f2513431829c115d631c70f475a8f26f7a4f5f9e36e76f51d3c8035b1e0736ad9e8cb2d62537abc606b76
7
+ data.tar.gz: 7d8d16cc6a68d75ba094b5ba20da98cd119b8f4264c64f8c52b1f8b49a0618317370241cd280da71f73212bf9c79234ef01f40fb3b6ae70f81400e76db1bc5b0
data/.rubocop.yml ADDED
@@ -0,0 +1,21 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ TargetRubyVersion: 2.6.0
4
+ Layout/LineLength:
5
+ Max: 250
6
+ Metrics/MethodLength:
7
+ Max: 65
8
+ Metrics/BlockLength:
9
+ Max: 60
10
+ Metrics/PerceivedComplexity:
11
+ Max: 20
12
+ Metrics/CyclomaticComplexity:
13
+ Max: 15
14
+ Metrics/AbcSize:
15
+ Max: 70
16
+ Metrics/ClassLength:
17
+ Max: 200
18
+ Metrics/ParameterLists:
19
+ Max: 11
20
+ Style/Documentation:
21
+ AllowedConstants: [Danger]
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- danger-app_size_report (0.0.2)
4
+ danger-app_size_report (0.0.4)
5
5
  danger-plugin-api (~> 1.0)
6
6
 
7
7
  GEM
@@ -19,7 +19,7 @@ GEM
19
19
  colored2 (3.1.2)
20
20
  cork (0.3.0)
21
21
  colored2 (~> 3.1)
22
- danger (8.4.2)
22
+ danger (8.6.1)
23
23
  claide (~> 1.0)
24
24
  claide-plugins (>= 0.9.2)
25
25
  colored2 (~> 3.1)
@@ -35,7 +35,7 @@ GEM
35
35
  danger-plugin-api (1.0.0)
36
36
  danger (> 2.0)
37
37
  diff-lcs (1.5.0)
38
- faraday (1.9.3)
38
+ faraday (1.10.0)
39
39
  faraday-em_http (~> 1.0)
40
40
  faraday-em_synchrony (~> 1.0)
41
41
  faraday-excon (~> 1.1)
@@ -62,7 +62,7 @@ GEM
62
62
  faraday-retry (1.0.3)
63
63
  ffi (1.15.5)
64
64
  formatador (1.1.0)
65
- git (1.10.2)
65
+ git (1.11.0)
66
66
  rchardet (~> 1.8)
67
67
  guard (2.18.0)
68
68
  formatador (>= 0.2.4)
@@ -78,7 +78,7 @@ GEM
78
78
  guard (~> 2.1)
79
79
  guard-compat (~> 1.1)
80
80
  rspec (>= 2.99.0, < 4.0)
81
- kramdown (2.3.1)
81
+ kramdown (2.4.0)
82
82
  rexml
83
83
  kramdown-parser-gfm (1.1.0)
84
84
  kramdown (~> 2.0)
@@ -98,35 +98,35 @@ GEM
98
98
  faraday (>= 0.9)
99
99
  sawyer (~> 0.8.0, >= 0.5.3)
100
100
  open4 (1.3.4)
101
- parallel (1.21.0)
102
- parser (3.1.0.0)
101
+ parallel (1.22.1)
102
+ parser (3.1.2.0)
103
103
  ast (~> 2.4.1)
104
104
  pry (0.14.1)
105
105
  coderay (~> 1.1)
106
106
  method_source (~> 1.0)
107
- public_suffix (4.0.6)
107
+ public_suffix (4.0.7)
108
108
  rainbow (3.1.1)
109
109
  rake (10.5.0)
110
- rb-fsevent (0.11.0)
110
+ rb-fsevent (0.11.1)
111
111
  rb-inotify (0.10.1)
112
112
  ffi (~> 1.0)
113
113
  rchardet (1.8.0)
114
- regexp_parser (2.2.0)
114
+ regexp_parser (2.3.1)
115
115
  rexml (3.2.5)
116
- rspec (3.10.0)
117
- rspec-core (~> 3.10.0)
118
- rspec-expectations (~> 3.10.0)
119
- rspec-mocks (~> 3.10.0)
120
- rspec-core (3.10.2)
121
- rspec-support (~> 3.10.0)
122
- rspec-expectations (3.10.2)
116
+ rspec (3.11.0)
117
+ rspec-core (~> 3.11.0)
118
+ rspec-expectations (~> 3.11.0)
119
+ rspec-mocks (~> 3.11.0)
120
+ rspec-core (3.11.0)
121
+ rspec-support (~> 3.11.0)
122
+ rspec-expectations (3.11.0)
123
123
  diff-lcs (>= 1.2.0, < 2.0)
124
- rspec-support (~> 3.10.0)
125
- rspec-mocks (3.10.2)
124
+ rspec-support (~> 3.11.0)
125
+ rspec-mocks (3.11.1)
126
126
  diff-lcs (>= 1.2.0, < 2.0)
127
- rspec-support (~> 3.10.0)
128
- rspec-support (3.10.3)
129
- rubocop (1.25.0)
127
+ rspec-support (~> 3.11.0)
128
+ rspec-support (3.11.0)
129
+ rubocop (1.25.1)
130
130
  parallel (~> 1.10)
131
131
  parser (>= 3.1.0.0)
132
132
  rainbow (>= 2.2.2, < 4.0)
@@ -135,8 +135,8 @@ GEM
135
135
  rubocop-ast (>= 1.15.1, < 2.0)
136
136
  ruby-progressbar (~> 1.7)
137
137
  unicode-display_width (>= 1.4.0, < 3.0)
138
- rubocop-ast (1.15.1)
139
- parser (>= 3.0.1.1)
138
+ rubocop-ast (1.17.0)
139
+ parser (>= 3.1.1.0)
140
140
  ruby-progressbar (1.11.0)
141
141
  ruby2_keywords (0.0.5)
142
142
  sawyer (0.8.2)
@@ -153,6 +153,7 @@ GEM
153
153
 
154
154
  PLATFORMS
155
155
  x86_64-darwin-20
156
+ x86_64-darwin-21
156
157
 
157
158
  DEPENDENCIES
158
159
  bundler (~> 2.0)
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # danger-app_size_report
2
2
 
3
- A [Danger](https://github.com/danger/danger) plugin for reporting iOS app size violations. A valid [App Thinning Size Report](https://developer.apple.com/documentation/xcode/reducing-your-app-s-size) must be passed to the plugin for accurate functionality.
3
+ A [Danger](https://github.com/danger/danger) plugin for reporting iOS and Android app size violations.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,34 +8,40 @@ A [Danger](https://github.com/danger/danger) plugin for reporting iOS app size v
8
8
 
9
9
  ## Usage
10
10
 
11
- ### `flag_violations`
11
+ ### `flag_ios_violations`
12
12
 
13
- Report app size violations given a valid App Thinning Size Report.
13
+ Report **iOS** app size violations given a valid App Thinning Size Report. A valid [App Thinning Size Report](https://developer.apple.com/documentation/xcode/reducing-your-app-s-size) must be passed to the plugin for accurate functionality.
14
14
 
15
15
  // Dangerfile
16
16
 
17
17
  report_path = "/Path/to/AppSizeReport.txt"
18
- app_size_report.flag_violations(
19
- report_path,
20
- build_type: 'App',
21
- size_limit: 4,
22
- limit_unit: 'GB',
18
+ app_size_report.flag_ios_violations(
19
+ report_path,
20
+ build_type: 'App',
21
+ size_limit: 4,
22
+ limit_unit: 'GB',
23
23
  fail_on_warning: false
24
24
  )
25
25
 
26
+ The list of variants with their sizes and any violations will be displayed in the PR report as seen below. Supported variant descriptors are placed under collapsible sections.
27
+
28
+ <p align="center">
29
+ <img src="Resources/Images/app_thinning_size_report.png">
30
+ </p>
31
+
26
32
  #### Parameters
27
33
 
28
34
  - `report_path` [String, required] Path to valid App Thinning Size Report text file.
29
- - `build_type` [String, optional] [Default: 'App'] Specify whether the report corresponds to an App or an App Clip.
35
+ - `build_type` [String, optional] [Default: 'App'] Specify whether the report corresponds to an App or an App Clip.
30
36
  - Supported values: 'App', 'Clip'
31
- - `size_limit` [Numeric, optional] [Default: 4] Specify the app size limit.
37
+ - `size_limit` [Numeric, optional] [Default: 4] Specify the app size limit.
32
38
  - `limit_unit` [String, optional] [Default: 'GB'] Specific the unit for the given size limit.
33
39
  - Supported values: 'KB', 'MB', 'GB'
34
- - `fail_on_warning` [Boolean, optional] [Default: false] Specify whether the PR should fail if one or more app variants exceed the given size limit. By default, the plugin issues a warning in this case.
40
+ - `fail_on_warning` [Boolean, optional] [Default: false] Specify whether the PR should fail if one or more app variants exceed the given size limit. By default, the plugin issues a warning in this case.
35
41
 
36
42
  ### `report_json`
37
43
 
38
- Returns a JSON string representation of the given App Thinning Size Report.
44
+ Returns a JSON string representation of the given iOS App Thinning Size Report.
39
45
 
40
46
  // Dangerfile
41
47
 
@@ -46,10 +52,62 @@ Returns a JSON string representation of the given App Thinning Size Report.
46
52
 
47
53
  - `report_path` [String, required] Path to valid App Thinning Size Report text file.
48
54
 
49
- ## Development
55
+ ### `flag_android_violations`
56
+
57
+ Report <b>Android</b> app size violations given a valid Android App Bundle (AAB).
58
+
59
+ // Dangerfile
60
+
61
+ aab_path = "/Path/to/app.aab"
62
+ ks_path = "/Path/to/keyStore"
63
+ ks_alias = "KeyAlias"
64
+ ks_password = "Key Password"
65
+ ks_alias_password = "Key Alias Password"
66
+ app_size_report.flag_android_violations(
67
+ aab_path,
68
+ ks_path,
69
+ ks_alias,
70
+ ks_password,
71
+ ks_alias_password,
72
+ screen_densities: ["MDPI", "HDPI", "XHDPI", "XXHDPI", "XXXHDPI"],
73
+ languages: ["en", "de", "da", "es", "fr", "it", "nb", "nl", "sv"],
74
+ build_type: 'Instant',
75
+ size_limit: 4,
76
+ limit_unit: 'MB',
77
+ fail_on_warning: false
78
+ )
79
+
80
+ The top 25 violations will be displayed in the PR report with any remaining violations and variants under the size limit placed under collapsible sections as seen below.
50
81
 
51
- 1. Clone this repo
82
+ <p align="center">
83
+ <img src="Resources/Images/android_instant_size_report.png">
84
+ </p>
85
+
86
+ #### Parameters
87
+
88
+ - `aab_path` [String, required] Path to valid AAB file.
89
+ - `ks_path` [String, required] Path to valid signing key file.
90
+ - `ks_alias` [String, required] Alias of signing key.
91
+ - `ks_password` [String, required] Password of signing key.
92
+ - `ks_alias_password` [String, required] Alias Password of signing key.
93
+ - `screen_densities` [Array, optional] [Default: ["MDPI", "HDPI", "XHDPI", "XXHDPI", "XXXHDPI"]] Array of screen densities to check APK size.
94
+ - `languages` [Array, optional] [Default: ["en"]] Array of languages to check APK size
95
+ - `build_type` [String, optional] [Default: 'App'] Specify whether the report corresponds to an App or an Instant.
96
+ - Supported values: 'App', 'Instant'
97
+ - `size_limit` [Numeric, optional] [Default: 150] Specify the app size limit.
98
+ - `limit_unit` [String, optional] [Default: 'MB'] Specific the unit for the given size limit.
99
+ - Supported values: 'KB', 'MB', 'GB'
100
+ - `fail_on_warning` [Boolean, optional] [Default: false] Specify whether the PR should fail if one or more app variants exceed the given size limit. By default, the plugin issues a warning in this case.
101
+
102
+ ## Contribute
103
+
104
+ 1. Fork this repo.
52
105
  2. Run `bundle install` to setup dependencies.
53
106
  3. Run `bundle exec rake spec` to run the tests.
54
107
  4. Use `bundle exec guard` to automatically have tests run as you make changes.
55
108
  5. Make your changes.
109
+ 6. Submit a pull request with your desired changes to be merged on review.
110
+
111
+ ## More Info
112
+
113
+ [Open an issue](https://github.com/ChargePoint/danger-app_size_report/issues) if you have any questions!
data/Resources/app.aab ADDED
Binary file
Binary file
@@ -8,13 +8,14 @@ require 'app_size_report/gem_version'
8
8
  Gem::Specification.new do |spec|
9
9
  spec.name = 'danger-app_size_report'
10
10
  spec.version = AppSizeReport::VERSION
11
- spec.authors = ['Rishab Sukumar']
11
+ spec.authors = ['Rishab Sukumar', 'Bharath Thakkallapally', 'Vido Shaweddy']
12
12
  spec.email = ['rishab.sukumar@chargepoint.com']
13
- spec.description = 'A Danger plugin for reporting iOS app size violations.'
14
- spec.summary = 'A Danger plugin for reporting iOS app size violations. A valid App Thinning Size Report must be passed to the plugin for accurate functionality.'
13
+ spec.description = 'A Danger plugin for reporting iOS and Android app size violations.'
14
+ spec.summary = 'A Danger plugin for reporting iOS and Android app size violations. A valid App Thinning Size Report or Android App Bundle must be passed to the plugin for accurate functionality.'
15
15
  spec.homepage = 'https://github.com/ChargePoint/danger-app_size_report'
16
16
  spec.license = 'MIT'
17
17
 
18
+ spec.required_ruby_version = '>= 2.6.0'
18
19
  spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
19
20
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
21
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
@@ -48,4 +49,5 @@ Gem::Specification.new do |spec|
48
49
  #
49
50
  # This will stop test execution and let you inspect the results
50
51
  spec.add_development_dependency 'pry', '~> 0.14.1'
52
+ spec.metadata['rubygems_mfa_required'] = 'true'
51
53
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AppSizeReport
4
- VERSION = '0.0.3'
4
+ VERSION = '1.0.0'
5
5
  end
@@ -2,18 +2,24 @@
2
2
 
3
3
  module Danger
4
4
  require 'json'
5
+ require 'open-uri'
6
+ require 'fileutils'
7
+
5
8
  require_relative '../converter/parser/report_parser'
6
9
  require_relative '../converter/helper/memory_size'
10
+ require_relative '../converter/helper/android_utils'
7
11
 
8
- # A Danger plugin for reporting iOS app size violations.
12
+ # A Danger plugin for reporting iOS and Android app size violations.
13
+ #
14
+ #
9
15
  # A valid App Thinning Size Report must be passed to the plugin
10
- # for accurate functionality.
16
+ # for accurate functionality in case of iOS.
11
17
  #
12
- # @example Report app size violations if one or more App variants
18
+ # @example Report iOS app size violations if one or more App variants
13
19
  # exceed 4GB.
14
20
  #
15
21
  # report_path = "/Path/to/AppSize/Report.txt"
16
- # app_size_report.flag_violations(
22
+ # app_size_report.flag_ios_violations(
17
23
  # report_path,
18
24
  # build_type: 'App',
19
25
  # size_limit: 4,
@@ -21,11 +27,11 @@ module Danger
21
27
  # fail_on_warning: false
22
28
  # )
23
29
  #
24
- # @example Report app size violations if one or more App Clip variants
30
+ # @example Report iOS app size violations if one or more App Clip variants
25
31
  # exceed 8MB.
26
32
  #
27
33
  # report_path = "/Path/to/AppSize/Report.txt"
28
- # app_size_report.flag_violations(
34
+ # app_size_report.flag_ios_violations(
29
35
  # report_path,
30
36
  # build_type: 'Clip',
31
37
  # size_limit: 8,
@@ -33,10 +39,10 @@ module Danger
33
39
  # fail_on_warning: false
34
40
  # )
35
41
  #
36
- # @example Fail PR if one or more App Clip variants exceed 8MB.
42
+ # @example Fail PR if one or more iOS App Clip variants exceed 8MB.
37
43
  #
38
44
  # report_path = "/Path/to/AppSize/Report.txt"
39
- # app_size_report.flag_violations(
45
+ # app_size_report.flag_ios_violations(
40
46
  # report_path,
41
47
  # build_type: 'Clip',
42
48
  # size_limit: 8,
@@ -44,16 +50,79 @@ module Danger
44
50
  # fail_on_warning: true
45
51
  # )
46
52
  #
47
- # @example Get JSON string representation of app thinning size report
53
+ # @example Get JSON string representation of iOS app thinning size report
48
54
  #
49
55
  # report_path = "/Path/to/AppSize/Report.txt"
50
56
  # app_size_json = app_size_report.report_json(report_path)
51
57
  #
58
+ # @example Report Android app size violations if one or more App variants
59
+ #
60
+ # aab_path = "/Path/to/app.aab"
61
+ # ks_path = "/Path/to/keyStore"
62
+ # ks_alias = "KeyAlias"
63
+ # ks_password = "Key Password"
64
+ # ks_alias_password = "Key Alias Password"
65
+ # app_size_report.flag_android_violations(
66
+ # aab_path,
67
+ # ks_path,
68
+ # ks_alias,
69
+ # ks_password,
70
+ # ks_alias_password,
71
+ # screen_densities: ["MDPI", "HDPI", "XHDPI", "XXHDPI", "XXXHDPI"],
72
+ # languages: ["en", "de", "da", "es", "fr", "it", "nb", "nl", "sv"],
73
+ # build_type: 'App',
74
+ # size_limit: 14,
75
+ # limit_unit: 'MB',
76
+ # fail_on_warning: false
77
+ # )
78
+ #
79
+ # @example Report Android Instant app size violations if one or more App variants
80
+ #
81
+ # aab_path = "/Path/to/app.aab"
82
+ # ks_path = "/Path/to/keyStore"
83
+ # ks_alias = "KeyAlias"
84
+ # ks_password = "Key Password"
85
+ # ks_alias_password = "Key Alias Password"
86
+ # app_size_report.flag_android_violations(
87
+ # aab_path,
88
+ # ks_path,
89
+ # ks_alias,
90
+ # ks_password,
91
+ # ks_alias_password,
92
+ # screen_densities: ["MDPI", "HDPI", "XHDPI", "XXHDPI", "XXXHDPI"],
93
+ # languages: ["en", "de", "da", "es", "fr", "it", "nb", "nl", "sv"],
94
+ # build_type: 'Instant',
95
+ # size_limit: 4,
96
+ # limit_unit: 'MB',
97
+ # fail_on_warning: false
98
+ # )
99
+ #
100
+ # @example Fail PR if one or more Android Instant App variants exceed 4MB.
101
+ #
102
+ # aab_path = "/Path/to/app.aab"
103
+ # ks_path = "/Path/to/keyStore"
104
+ # ks_alias = "KeyAlias"
105
+ # ks_password = "Key Password"
106
+ # ks_alias_password = "Key Alias Password"
107
+ # app_size_report.flag_android_violations(
108
+ # aab_path,
109
+ # ks_path,
110
+ # ks_alias,
111
+ # ks_password,
112
+ # ks_alias_password,
113
+ # screen_densities: ["MDPI", "HDPI", "XHDPI", "XXHDPI", "XXXHDPI"],
114
+ # languages: ["en", "de", "da", "es", "fr", "it", "nb", "nl", "sv"],
115
+ # build_type: 'Instant',
116
+ # size_limit: 4,
117
+ # limit_unit: 'MB',
118
+ # fail_on_warning: true
119
+ # )
120
+ #
52
121
  # @see ChargePoint/danger-app_size_report
53
122
  # @tags ios, xcode, appclip, thinning, size
54
123
  #
55
124
  class DangerAppSizeReport < Plugin
56
- # Reports app size violations given a valid App Thinning Size Report.
125
+ # Reports IOS app size violations given a valid App Thinning Size Report.
57
126
  # @param [String, required] report_path
58
127
  # Path to valid App Thinning Size Report text file.
59
128
  # @param [String, optional] build_type
@@ -74,30 +143,97 @@ module Danger
74
143
  # Default: 'false'
75
144
  # @return [void]
76
145
  #
77
- def flag_violations(report_path, build_type: 'App', size_limit: 4, limit_unit: 'GB', fail_on_warning: false)
146
+ def flag_ios_violations(report_path, build_type: 'App', size_limit: 4, limit_unit: 'GB', fail_on_warning: false)
78
147
  report_text = File.read(report_path)
79
148
  variants = ReportParser.parse(report_text)
80
149
 
81
- unless %w[App Clip].include? build_type
82
- raise ArgumentError, "The 'build_type' argument only accepts the values \"App\" and \"Clip\""
83
- end
150
+ raise ArgumentError, "The 'build_type' argument only accepts the values \"App\" and \"Clip\"" unless %w[App Clip].include? build_type
84
151
 
85
152
  raise ArgumentError, "The 'size_limit' argument only accepts numeric values" unless size_limit.is_a? Numeric
86
153
 
87
154
  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\""
90
- end
155
+ raise ArgumentError, "The 'build_type' argument only accepts the values \"KB\", \"MB\" and \"GB\"" unless %w[KB MB GB].include? limit_unit
91
156
 
92
- unless [true, false].include? fail_on_warning
93
- raise ArgumentError, "The 'fail_on_warning' argument only accepts the values 'true' and 'false'"
94
- end
157
+ raise ArgumentError, "The 'fail_on_warning' argument only accepts the values 'true' and 'false'" unless [true, false].include? fail_on_warning
95
158
 
96
159
  generate_size_report_markdown(variants, build_type, size_limit, limit_unit, fail_on_warning)
97
160
  generate_variant_descriptors_markdown(variants)
98
161
  generate_ads_label_markdown
99
162
  end
100
163
 
164
+ # Reports Android app size violations given a valid AAB.
165
+ # @param [String, required] aab_path
166
+ # Path to valid AAB file.
167
+ # @param [String, required] ks_path
168
+ # Path to valid signing key file.
169
+ # @param [String, required] ks_alias
170
+ # Alias of signing key
171
+ # @param [String, required] ks_password
172
+ # Password of signing key
173
+ # @param [String, required] ks_alias_password
174
+ # Alias Password of signing key.
175
+ # @param [Array, optional] screen_densities
176
+ # Array of screen densities to check APK size
177
+ # Default: ["MDPI", "HDPI", "XHDPI", "XXHDPI", "XXXHDPI"]
178
+ # @param [Array, optional] languages
179
+ # Array of languages to check APK size
180
+ # Default: ["en"]
181
+ # @param [String, optional] build_type
182
+ # Specify whether the report corresponds to an App, Instant.
183
+ # Default: 'App'
184
+ # Supported values: 'App', 'Instant'
185
+ # @param [Numeric, optional] size_limit
186
+ # Specify the app size limit.
187
+ # Default: 150
188
+ # @param [String, optional] limit_unit
189
+ # Specific the unit for the given size limit.
190
+ # Default: 'MB'
191
+ # Supported values: 'KB', 'MB', 'GB'
192
+ # @param [Boolean, optional] fail_on_warning
193
+ # Specify whether the PR should fail if one or more app variants
194
+ # exceed the given size limit. By default, the plugin issues
195
+ # a warning in this case.
196
+ # Default: 'false'
197
+ # @return [void]
198
+ #
199
+ def flag_android_violations(aab_path, ks_path, ks_alias, ks_password, ks_alias_password, screen_densities: %w[MDPI HDPI XHDPI XXHDPI XXXHDPI], languages: ['en'], build_type: 'App', size_limit: 150, limit_unit: 'MB', fail_on_warning: false)
200
+ project_root = Dir.pwd
201
+ temp_path = "#{project_root}/temp"
202
+ apks_path = "#{temp_path}/output.apks"
203
+ size_csv_path = "#{temp_path}/output.csv"
204
+ bundletool_path = "#{temp_path}/bundletool.jar"
205
+ bundletool_version = '1.8.2'
206
+ variants_limit = 25
207
+
208
+ raise ArgumentError, "The 'build_type' argument only accepts the values \"App\" and \"Instant\"" unless %w[App Instant].include? build_type
209
+
210
+ raise ArgumentError, "The 'size_limit' argument only accepts numeric values" unless size_limit.is_a? Numeric
211
+
212
+ limit_unit.upcase!
213
+ raise ArgumentError, "The 'limit_unit' argument only accepts the values \"KB\", \"MB\" and \"GB\"" unless %w[KB MB GB].include? limit_unit
214
+
215
+ raise ArgumentError, "The 'fail_on_warning' argument only accepts the values 'true' and 'false'" unless [true, false].include? fail_on_warning
216
+
217
+ create_temp_dir(temp_path)
218
+
219
+ unless AndroidUtils.download_bundletool(bundletool_version, bundletool_path)
220
+ clean_temp!
221
+ return
222
+ end
223
+
224
+ AndroidUtils.generate_apks(aab_path, ks_path, ks_alias, ks_password, ks_alias_password, apks_path,
225
+ bundletool_path)
226
+ AndroidUtils.generate_estimated_sizes(apks_path, size_csv_path, bundletool_path, build_type)
227
+ filtered_sizes = AndroidUtils.filter_estimated_sizes(size_csv_path, screen_densities, languages)
228
+ sorted_sizes = AndroidUtils.sort_estimated_sizes(filtered_sizes)
229
+
230
+ clean_temp!(temp_path)
231
+
232
+ generate_android_size_report_markdown(sorted_sizes, build_type, size_limit, limit_unit, fail_on_warning,
233
+ variants_limit)
234
+ generate_ads_label_markdown
235
+ end
236
+
101
237
  # Returns a JSON string representation of the given App Thinning Size Report.
102
238
  # @param [String, required] report_path
103
239
  # Path to valid App Thinning Size Report text file.
@@ -111,6 +247,96 @@ module Danger
111
247
 
112
248
  private
113
249
 
250
+ def create_temp_dir(temp_path)
251
+ Dir.mkdir temp_path
252
+ end
253
+
254
+ def clean_temp!(temp_path)
255
+ FileUtils.rm_rf(temp_path)
256
+ end
257
+
258
+ def generate_android_size_report_markdown(sorted_sizes, build_type, size_limit, limit_unit, fail_on_warning, variants_limit)
259
+ limit_size = MemorySize.new("#{size_limit}#{limit_unit}")
260
+
261
+ if build_type == 'Instant' && limit_size.megabytes > 4
262
+ message "The size limit was set to 4 MB as the given limit of #{size_limit} #{limit_unit} exceeds Android Instant App size restrictions"
263
+ size_limit = 4
264
+ limit_unit = 'MB'
265
+ limit_size.kilobytes = 4 * 1024
266
+ elsif build_type == 'App' && limit_size.megabytes > 150
267
+ message "The size limit was set to 150 MB as the given limit of #{size_limit} #{limit_unit} exceeds Android App size restrictions"
268
+ size_limit = 150
269
+ limit_unit = 'MB'
270
+ limit_size.kilobytes = 150 * 1024
271
+ end
272
+
273
+ violation_count = AndroidUtils.violations_count(sorted_sizes, limit_size.bytes)
274
+
275
+ if violation_count.positive?
276
+ if fail_on_warning
277
+ failure "The size limit of #{size_limit} #{limit_unit.upcase} has been exceeded by #{violation_count} variants"
278
+ else
279
+ warn "The size limit of #{size_limit} #{limit_unit.upcase} has been exceeded by #{violation_count} variants"
280
+ end
281
+ end
282
+
283
+ exceed_size_report = "| Under Limit | SDK | ABI | Screen Density | Language | Size (Bytes) |\n"
284
+ exceed_size_report << "| :-: | :-: | :-: | :-: | :-: | :-: |\n"
285
+
286
+ more_exceed_size_report = "| Under Limit | SDK | ABI | Screen Density | Language | Size (Bytes) |\n"
287
+ more_exceed_size_report << "| :-: | :-: | :-: | :-: | :-: | :-: |\n"
288
+
289
+ under_size_report = "| Under Limit | SDK | ABI | Screen Density | Language | Size (Bytes) |\n"
290
+ under_size_report << "| :-: | :-: | :-: | :-: | :-: | :-: |\n"
291
+
292
+ counter = sorted_sizes.length - 1
293
+ exceed_counter = 1
294
+ under_counter = 1
295
+
296
+ while counter >= 0
297
+ variant = sorted_sizes[counter]
298
+ is_violating = variant.max > limit_size.bytes ? '❌' : '✅'
299
+ variant_report = "#{is_violating} | #{variant.sdk} | #{variant.abi} | #{variant.screen_density} | #{variant.language} | #{variant.max} |\n"
300
+
301
+ if variant.max > limit_size.bytes
302
+ if exceed_counter <= variants_limit
303
+ exceed_counter += 1
304
+ exceed_size_report << variant_report
305
+ else
306
+ more_exceed_size_report << variant_report
307
+ end
308
+ elsif under_counter <= variants_limit
309
+ under_counter += 1
310
+ under_size_report << variant_report
311
+ end
312
+
313
+ counter -= 1
314
+ end
315
+
316
+ size_report = "# Android #{build_type} Size Report\n"
317
+ size_report << "### Size limit = #{size_limit} #{limit_unit.upcase}\n\n"
318
+
319
+ if violation_count.positive?
320
+ size_report << "## Variants exceeding the size limit\n\n"
321
+ size_report << exceed_size_report
322
+ size_report << "\n"
323
+
324
+ if violation_count > variants_limit
325
+ size_report << "<details>\n<summary>Click to view more violating variants!</summary>\n\n"
326
+ size_report << more_exceed_size_report
327
+ size_report << "</details>\n\n"
328
+ end
329
+ end
330
+
331
+ size_report << "## Variants under or equal to the size limit\n\n"
332
+ size_report << "<details>\n<summary>Click to expand!</summary>\n\n"
333
+
334
+ size_report << under_size_report
335
+ size_report << "</details>\n"
336
+
337
+ markdown size_report
338
+ end
339
+
114
340
  def generate_size_report_markdown(variants, build_type, size_limit, limit_unit, fail_on_warning)
115
341
  limit_size = MemorySize.new("#{size_limit}#{limit_unit}")
116
342
 
@@ -128,9 +354,7 @@ module Danger
128
354
 
129
355
  flagged_variant_names = []
130
356
  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
357
+ flagged_variant_names.append(variant.variant) if variant.app_size.uncompressed.value > limit_size.megabytes || variant.on_demand_resources_size.uncompressed.value > limit_size.megabytes
134
358
  end
135
359
 
136
360
  if flagged_variant_names.length.positive?
@@ -178,7 +402,7 @@ module Danger
178
402
  end
179
403
 
180
404
  def generate_ads_label_markdown
181
- ads_label = 'Powered by [danger-app_size_report](https://github.com/ChargePoint)'
405
+ ads_label = 'Powered by [danger-app_size_report](https://github.com/ChargePoint/danger-app_size_report)'
182
406
  markdown ads_label
183
407
  end
184
408
  end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'csv'
4
+ require 'open3'
5
+
6
+ require_relative '../models/android_variant_model'
7
+
8
+ # Used to obtain and parse Android app size information
9
+ class AndroidUtils
10
+ def self.filter_estimated_sizes(path, screen_densities, languages)
11
+ filtered_sizes = []
12
+ CSV.foreach(path, headers: true) do |row|
13
+ sdk = row[AndroidVariant::PARSING_KEYS[:sdk]]
14
+ abi = row[AndroidVariant::PARSING_KEYS[:abi]]
15
+ screen_density = row[AndroidVariant::PARSING_KEYS[:screen_density]]
16
+ language = row[AndroidVariant::PARSING_KEYS[:language]]
17
+ texture_compression_format = row[AndroidVariant::PARSING_KEYS[:texture_compression_format]]
18
+ device_tire = row[AndroidVariant::PARSING_KEYS[:device_tire]]
19
+ min = row[AndroidVariant::PARSING_KEYS[:min]]
20
+ max = row[AndroidVariant::PARSING_KEYS[:max]]
21
+
22
+ if screen_densities.include?(screen_density) && languages.include?(language)
23
+ filtered_sizes << AndroidVariant.new(sdk, abi, screen_density, language, texture_compression_format,
24
+ device_tire, min, max)
25
+ end
26
+ end
27
+ filtered_sizes
28
+ end
29
+
30
+ def self.sort_estimated_sizes(size_arr)
31
+ size_arr.sort { |o1, o2| o1.max <=> o2.max }
32
+ end
33
+
34
+ def self.violations_count(sorted_arr, limit)
35
+ lb = -1
36
+ ub = sorted_arr.length
37
+ while ub - lb > 1
38
+ mid = (lb + ub) / 2
39
+ if sorted_arr[mid].max <= limit
40
+ lb = mid
41
+ else
42
+ ub = mid
43
+ end
44
+ end
45
+ sorted_arr.length - ub
46
+ end
47
+
48
+ def self.generate_apks(aab_path, ks_path, ks_alias, ks_password, ks_alias_password, apks_path, bundletool_path)
49
+ keystore_params = "--ks=\"#{ks_path}\" --ks-key-alias=\"#{ks_alias}\" --ks-pass=\"pass:#{ks_password}\" --key-pass=\"pass:#{ks_alias_password}\""
50
+ cmd = "java -jar #{bundletool_path} build-apks --bundle=\"#{aab_path}\" --output=\"#{apks_path}\" #{keystore_params}"
51
+ Open3.popen3(cmd) do |_, _, stderr, wait_thr|
52
+ exit_status = wait_thr.value
53
+ raise stderr.read unless exit_status.success?
54
+ end
55
+ end
56
+
57
+ def self.generate_estimated_sizes(apks_path, size_csv_path, bundletool_path, build_type)
58
+ bundletool_cmd = "java -jar #{bundletool_path} get-size total --apks=\"#{apks_path}\" --dimensions=ALL"
59
+ bundletool_cmd += ' --instant' if build_type == 'Instant'
60
+ cmd = "#{bundletool_cmd} > #{size_csv_path}"
61
+ Open3.popen3(cmd) do |_, _, stderr, wait_thr|
62
+ exit_status = wait_thr.value
63
+ raise stderr.read unless exit_status.success?
64
+ end
65
+ end
66
+
67
+ def self.download_bundletool(version, bundletool_path)
68
+ URI.open("https://github.com/google/bundletool/releases/download/#{version}/bundletool-all-#{version}.jar") do |bundletool|
69
+ File.binwrite(bundletool_path, bundletool.read)
70
+ end
71
+ rescue OpenURI::HTTPError
72
+ false
73
+ end
74
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'json'
4
4
 
5
+ # Generate pretty JSON from list of variants
5
6
  class JSONConverter
6
7
  def to_json(_options = {})
7
8
  hash = {}
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: false
2
2
 
3
3
  require_relative '../helper/json_converter'
4
+ # Defines memory size object to be used to parse the App Thinning Size Report
4
5
  class MemorySize < JSONConverter
5
6
  attr_accessor :kilobytes
6
7
 
@@ -4,6 +4,7 @@ require_relative '../parser/variant_parser'
4
4
  require_relative '../parser/variant_descriptor_parser'
5
5
  require_relative '../parser/app_size_parser'
6
6
 
7
+ # Used to parse sections of the App Thinning Size Report using different parsers
7
8
  class ResultFactory
8
9
  def self.parse(from_text: '', parser: nil)
9
10
  result = nil
@@ -16,15 +17,7 @@ class ResultFactory
16
17
  variant_descriptor_parser = VariantDescriptorParser.new(from_text)
17
18
  variant_descriptor_parser.parse
18
19
  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
20
+ when :app_on_demand_resources_size, :app_size, :on_demand_resources_size
28
21
  app_size_parser = AppSizeParser.new(from_text)
29
22
  app_size_parser.parse
30
23
  result = app_size_parser.result
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Android variant model
4
+ class AndroidVariant
5
+ attr_reader :sdk, :abi, :screen_density, :language, :texture_compression_format, :device_tire, :min, :max
6
+
7
+ PARSING_KEYS = {
8
+ sdk: 'SDK',
9
+ abi: 'ABI',
10
+ screen_density: 'SCREEN_DENSITY',
11
+ language: 'LANGUAGE',
12
+ texture_compression_format: 'TEXTURE_COMPRESSION_FORMAT',
13
+ device_tire: 'DEVICE_TIER',
14
+ min: 'MIN',
15
+ max: 'MAX'
16
+ }.freeze
17
+
18
+ def initialize(sdk, abi, screen_density, language, texture_compression_format, device_tire, min, max)
19
+ @sdk = sdk
20
+ @abi = abi
21
+ @screen_density = screen_density
22
+ @language = language
23
+ @texture_compression_format = texture_compression_format
24
+ @device_tire = device_tire
25
+ @min = min.to_i
26
+ @max = max.to_i
27
+ end
28
+ end
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../helper/json_converter'
4
+
5
+ # App Size Model
6
+ # @example 'App size: 6.6 MB compressed, 12.9 MB uncompressed'
4
7
  class AppSizeModel < JSONConverter
5
8
  attr_reader :compressed, :uncompressed
6
9
 
@@ -16,6 +19,7 @@ class AppSizeModel < JSONConverter
16
19
  end
17
20
  end
18
21
 
22
+ # Size Model
19
23
  class SizeModel < JSONConverter
20
24
  attr_reader :raw_value, :value, :unit
21
25
 
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../helper/json_converter'
4
+
5
+ # Device Model
6
+ # @example 'device: iPhone10,3, os-version: 14.0'
4
7
  class DeviceModel < JSONConverter
5
8
  attr_reader :device, :os_version
6
9
 
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../helper/json_converter'
4
+
5
+ # Variant Model
4
6
  class VariantModel < JSONConverter
5
7
  attr_reader :variant, :supported_variant_descriptors, :app_on_demand_resources_size, :app_size,
6
8
  :on_demand_resources_size
@@ -4,6 +4,7 @@ require_relative '../models/app_size_model'
4
4
  require_relative '../helper/memory_size'
5
5
  require_relative './model_parser'
6
6
 
7
+ # Parser for App Size section of App Thinning Size Report
7
8
  class AppSizeParser < ModelParser
8
9
  attr_reader :standardized_unit
9
10
 
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: false
2
2
 
3
+ # Parent class for parsers
3
4
  class ModelParser
4
5
  attr_reader :text, :result
5
6
 
@@ -4,6 +4,7 @@ require 'securerandom'
4
4
  require_relative '../models/variant_model'
5
5
  require_relative '../helper/result_factory'
6
6
 
7
+ # Parses App Thinning Size Report in its entirety
7
8
  class ReportParser
8
9
  def self.parse(text)
9
10
  splitter_id = SecureRandom.uuid
@@ -4,6 +4,7 @@ require_relative '../models/device_model'
4
4
  require 'securerandom'
5
5
  require_relative './model_parser'
6
6
 
7
+ # Parser for Variant descriptor section of App Thinning Size Report
7
8
  class VariantDescriptorParser < ModelParser
8
9
  def parse
9
10
  @text = @text.strip
@@ -2,6 +2,8 @@
2
2
 
3
3
  require_relative './model_parser'
4
4
 
5
+ # Parse Variant section of App Thinning Size Report
6
+ # @example 'Variant: ChargePointAppClip-35AD0331-EA57-4B82-B8E6-029D7786B9B7.ipa'
5
7
  class VariantParser < ModelParser
6
8
  def parse
7
9
  @text = @text.strip
@@ -22,8 +22,8 @@ module Danger
22
22
  expect(json_string).to eq(expected_json)
23
23
  end
24
24
 
25
- it 'Generates App Size Danger Report' do
26
- @app_size_report.flag_violations(
25
+ it 'Generates IOS App Size Danger Report' do
26
+ @app_size_report.flag_ios_violations(
27
27
  "#{File.dirname(__dir__)}/Resources/App\ Thinning\ Size\ Report.txt",
28
28
  build_type: 'Clip',
29
29
  size_limit: 12,
@@ -32,6 +32,23 @@ module Danger
32
32
 
33
33
  expect(@dangerfile.status_report[:warnings]).to eq(['The size limit of 10 MB has been exceeded by one or more variants'])
34
34
  end
35
+
36
+ it 'Generates Android App Size Danger Report' do
37
+ @app_size_report.flag_android_violations(
38
+ "#{File.dirname(__dir__)}/Resources/app.aab",
39
+ "#{File.dirname(__dir__)}/Resources/testKey1",
40
+ 'testKey1',
41
+ 'testKey1',
42
+ 'testKey1',
43
+ screen_densities: %w[MDPI HDPI XHDPI XXHDPI XXXHDPI],
44
+ languages: %w[en de da es fr it nb nl sv],
45
+ build_type: 'Instant',
46
+ size_limit: 1.459,
47
+ limit_unit: 'MB'
48
+ )
49
+
50
+ expect(@dangerfile.status_report[:warnings]).to eq(['The size limit of 1.459 MB has been exceeded by 18 variants'])
51
+ end
35
52
  end
36
53
  end
37
54
  end
data/spec/spec_helper.rb CHANGED
@@ -22,6 +22,7 @@ RSpec.configure do |config|
22
22
  config.filter_gems_from_backtrace 'bundler'
23
23
  config.color = true
24
24
  config.tty = true
25
+ config.formatter = :documentation
25
26
  end
26
27
 
27
28
  require 'danger_plugin'
metadata CHANGED
@@ -1,14 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: danger-app_size_report
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rishab Sukumar
8
+ - Bharath Thakkallapally
9
+ - Vido Shaweddy
8
10
  autorequire:
9
11
  bindir: bin
10
12
  cert_chain: []
11
- date: 2022-02-03 00:00:00.000000000 Z
13
+ date: 2022-04-27 00:00:00.000000000 Z
12
14
  dependencies:
13
15
  - !ruby/object:Gem::Dependency
14
16
  name: danger-plugin-api
@@ -150,7 +152,7 @@ dependencies:
150
152
  - - "~>"
151
153
  - !ruby/object:Gem::Version
152
154
  version: 0.14.1
153
- description: A Danger plugin for reporting iOS app size violations.
155
+ description: A Danger plugin for reporting iOS and Android app size violations.
154
156
  email:
155
157
  - rishab.sukumar@chargepoint.com
156
158
  executables: []
@@ -158,6 +160,7 @@ extensions: []
158
160
  extra_rdoc_files: []
159
161
  files:
160
162
  - ".gitignore"
163
+ - ".rubocop.yml"
161
164
  - Gemfile
162
165
  - Gemfile.lock
163
166
  - Guardfile
@@ -165,13 +168,19 @@ files:
165
168
  - README.md
166
169
  - Rakefile
167
170
  - Resources/App Thinning Size Report.txt
171
+ - Resources/Images/android_instant_size_report.png
172
+ - Resources/Images/app_thinning_size_report.png
173
+ - Resources/app.aab
168
174
  - Resources/expectedReportJSON.json
175
+ - Resources/testKey1
169
176
  - danger-app_size_report.gemspec
170
177
  - lib/app_size_report/gem_version.rb
171
178
  - lib/app_size_report/plugin.rb
179
+ - lib/converter/helper/android_utils.rb
172
180
  - lib/converter/helper/json_converter.rb
173
181
  - lib/converter/helper/memory_size.rb
174
182
  - lib/converter/helper/result_factory.rb
183
+ - lib/converter/models/android_variant_model.rb
175
184
  - lib/converter/models/app_size_model.rb
176
185
  - lib/converter/models/device_model.rb
177
186
  - lib/converter/models/variant_model.rb
@@ -187,7 +196,8 @@ files:
187
196
  homepage: https://github.com/ChargePoint/danger-app_size_report
188
197
  licenses:
189
198
  - MIT
190
- metadata: {}
199
+ metadata:
200
+ rubygems_mfa_required: 'true'
191
201
  post_install_message:
192
202
  rdoc_options: []
193
203
  require_paths:
@@ -196,7 +206,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
196
206
  requirements:
197
207
  - - ">="
198
208
  - !ruby/object:Gem::Version
199
- version: '0'
209
+ version: 2.6.0
200
210
  required_rubygems_version: !ruby/object:Gem::Requirement
201
211
  requirements:
202
212
  - - ">="
@@ -206,8 +216,9 @@ requirements: []
206
216
  rubygems_version: 3.3.6
207
217
  signing_key:
208
218
  specification_version: 4
209
- summary: A Danger plugin for reporting iOS app size violations. A valid App Thinning
210
- Size Report must be passed to the plugin for accurate functionality.
219
+ summary: A Danger plugin for reporting iOS and Android app size violations. A valid
220
+ App Thinning Size Report or Android App Bundle must be passed to the plugin for
221
+ accurate functionality.
211
222
  test_files:
212
223
  - spec/app_size_report_spec.rb
213
224
  - spec/spec_helper.rb