fastlane-plugin-try_scan 0.2.0 → 0.5.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12d650730323d7c47cfcf510d28238dd5e4864bb4cf169e65139d14c909c33a0
4
- data.tar.gz: c5fceb554ef538e90917244e5ef303b79102289908aedd005e53a1b0a4d056d8
3
+ metadata.gz: 9b2cd7acb930212c6e8cf756c89e71741878e510c46af3f3f747feaafc3d5bac
4
+ data.tar.gz: 8e11f9f1caf48d396983cfd2aabdb30c49d5da44a7ef0db43ec4314765dbb7aa
5
5
  SHA512:
6
- metadata.gz: 0eaea36a94853fb1c5ba8eec440ba5329744c671c1970f6edc36df3db69cadb647e64023b3abfbcdf327b30813d69bef6b70566b2951323f6f0afd98574d9888
7
- data.tar.gz: 3bdfc6aede5d51e8ba1ae4089fc17be7e2cd6aa7ada7e0a09e696dfbf9412e001559d9341a7ee5ad659ae737460c00d7511e5064457416b06035fe3b356708e8
6
+ metadata.gz: 98f4a9b2f8db939dcf38507ad52ad275799b56a14cb3cc804fa20f9133e6bc01a7106ca3c38709d71b698941fff16b9b40b4330107b19545a2053810eff565a1
7
+ data.tar.gz: 8b0bcc7346f9fc389946e74fd1e22880e1f1cf82199c82f3cc3d2c2c21f416ad8ac84e734a130de5d62114c84c968d29a2c32d4073d37e6d34479430994d469b
data/README.md CHANGED
@@ -2,36 +2,37 @@
2
2
 
3
3
  [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-try_scan)
4
4
 
5
+ ## About try_scan
6
+
7
+ The easiest way to rerun tests of your iOS and Mac app 🚀
8
+
9
+ Under the hood `try_scan` uses official [`fastlane scan action`](https://docs.fastlane.tools/actions/scan/), it means that you are able to provide any `scan` options and use `Scanfile` as before — everything will work like a charm, `try_scan` just brings couple of new amazing options:
10
+
11
+ | Option | Description | Default |
12
+ | ------- |------------ | ------- |
13
+ | try_count | Number of times to try to get your tests green | 1 |
14
+ | try_parallel | Should first run be executed in parallel? Equivalent to `-parallel-testing-enabled` | true |
15
+ | retry_parallel | Should subsequent runs be executed in parallel? Required `try_parallel: true` | true |
16
+ | parallel_workers | Specify the exact number of test runners that will be spawned during parallel testing. Equivalent to `-parallel-testing-worker-count` and `concurrent_workers` | |
17
+
18
+ ## Requirements
19
+
20
+ * Xcode 11.x or greater. Download it at the [Apple Developer - Downloads](https://developer.apple.com/downloads) or the [Mac App Store](https://apps.apple.com/us/app/xcode/id497799835?mt=12).
21
+
5
22
  ## Getting Started
6
23
 
7
- This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `try_scan`, add it to your project by running:
24
+ To get started with `try_scan`, add it to your project by running:
8
25
 
9
26
  ```bash
10
- fastlane add_plugin try_scan
27
+ $ fastlane add_plugin try_scan
11
28
  ```
12
29
 
13
- ## About try_scan
14
-
15
- Simple way to retry your scan action 🚀
16
-
17
30
  ## Usage
18
31
 
19
32
  ```ruby
20
33
  try_scan(
21
34
  workspace: "Example.xcworkspace",
22
- devices: ["iPhone 6s", "iPad Air"],
35
+ devices: ["iPhone 7", "iPad Air"],
23
36
  try_count: 3
24
37
  )
25
38
  ```
26
-
27
- ## Issues and Feedback
28
-
29
- For any other issues and feedback about this plugin, please submit it to this repository.
30
-
31
- ## Troubleshooting
32
-
33
- If you have trouble using plugins, check out the [Plugins Troubleshooting](https://docs.fastlane.tools/plugins/plugins-troubleshooting/) guide.
34
-
35
- ## About _fastlane_
36
-
37
- _fastlane_ is the easiest way to automate beta deployments and releases for your iOS and Android apps. To learn more, check out [fastlane.tools](https://fastlane.tools).
@@ -2,7 +2,6 @@ module Fastlane
2
2
  module Actions
3
3
 
4
4
  require 'json'
5
- require 'nokogiri'
6
5
  require 'fastlane/actions/scan'
7
6
  require_relative '../helper/scan_helper'
8
7
  require_relative '../helper/try_scan_runner'
@@ -11,61 +10,16 @@ module Fastlane
11
10
 
12
11
  class TryScanAction < Action
13
12
  def self.run(params)
14
- prepare_for_testing(params)
15
- success = TryScanManager::Runner.new(params.values).run
13
+ if Helper.xcode_at_least?('11.0.0')
14
+ params[:destination] = [params[:destination]] if params[:destination] && !params[:destination].kind_of?(Array)
15
+ success = TryScanManager::Runner.new(params.values).run
16
16
 
17
- raise FastlaneCore::UI.test_failure!('Tests have failed') if params[:fail_build] && !success
18
- end
19
-
20
- def self.prepare_for_testing(params)
21
- warn_of_xcode11_result_bundle_incompatability(params)
22
- use_scanfile_to_override_settings(params.values)
23
- turn_off_concurrent_workers(params.values)
24
- prepare_scan_config(params.values)
25
- coerce_destination_to_array(params)
26
- end
27
-
28
- def self.warn_of_xcode11_result_bundle_incompatability(params)
29
- if FastlaneCore::Helper.xcode_at_least?('11.0.0')
30
- if params[:result_bundle]
31
- FastlaneCore::UI.important('As of Xcode 11, test_result bundles created in the output directory are actually symbolic links to an xcresult bundle')
32
- end
33
- elsif params[:output_types]&.include?('xcresult')
34
- FastlaneCore::UI.important("The 'xcresult' :output_type is only supported for Xcode 11 and greater. You are using #{FastlaneCore::Helper.xcode_version}.")
35
- end
36
- end
37
-
38
- def self.coerce_destination_to_array(params)
39
- destination = params[:destination] || Scan.config[:destination] || []
40
- unless destination.kind_of?(Array)
41
- params[:destination] = Scan.config[:destination] = [destination]
17
+ raise FastlaneCore::UI.test_failure!('Tests have failed') if params[:fail_build] && !success
18
+ else
19
+ raise FastlaneCore::UI.user_error!("Minimum supported Xcode: `v11.0.0` (used: `v#{Helper.xcode_version}`)")
42
20
  end
43
21
  end
44
22
 
45
- def self.turn_off_concurrent_workers(scan_options)
46
- if Gem::Version.new(Fastlane::VERSION) >= Gem::Version.new('2.142.0')
47
- scan_options.delete(:concurrent_workers) if scan_options[:concurrent_workers].to_i > 0
48
- end
49
- end
50
-
51
- def self.use_scanfile_to_override_settings(scan_options)
52
- overridden_options = FastlaneScanHelper.options_from_configuration_file(
53
- FastlaneScanHelper.scan_options_from_try_scan_options(scan_options)
54
- )
55
-
56
- unless overridden_options.empty?
57
- FastlaneCore::UI.important("Scanfile found: overriding try_scan options with it's values.")
58
- overridden_options.each { |key, val| scan_options[key] = val }
59
- end
60
- end
61
-
62
- def self.prepare_scan_config(scan_options)
63
- Scan.config ||= FastlaneCore::Configuration.create(
64
- Fastlane::Actions::ScanAction.available_options,
65
- FastlaneScanHelper.scan_options_from_try_scan_options(scan_options)
66
- )
67
- end
68
-
69
23
  #####################################################
70
24
  # Documentation #
71
25
  #####################################################
@@ -78,20 +32,40 @@ module Fastlane
78
32
  ["Alexey Alter-Pesotskiy"]
79
33
  end
80
34
 
81
- def self.scan_options
82
- ScanAction.available_options.reject { |config| %i[output_types].include?(config.key) }
83
- end
84
-
85
35
  def self.available_options
86
- scan_options = ScanAction.available_options.reject { |config| %i[output_types].include?(config.key) }
87
- scan_options + [
36
+ ScanAction.available_options + [
88
37
  FastlaneCore::ConfigItem.new(
89
38
  key: :try_count,
90
39
  env_name: "FL_TRY_SCAN_TRY_COUNT",
91
- description: "The number of times to retry running tests via scan",
40
+ description: "Number of times to try to get your tests green",
92
41
  type: Integer,
93
42
  is_string: false,
43
+ optional: true,
94
44
  default_value: 1
45
+ ),
46
+ FastlaneCore::ConfigItem.new(
47
+ key: :try_parallel,
48
+ env_name: "FL_TRY_SCAN_TRY_PARALLEL",
49
+ description: "Should first run be executed in parallel? Equivalent to -parallel-testing-enabled",
50
+ is_string: false,
51
+ optional: true,
52
+ default_value: true
53
+ ),
54
+ FastlaneCore::ConfigItem.new(
55
+ key: :retry_parallel,
56
+ env_name: "FL_TRY_SCAN_RETRY_PARALLEL",
57
+ description: "Should subsequent runs be executed in parallel? Required :try_parallel: true",
58
+ is_string: false,
59
+ optional: true,
60
+ default_value: true
61
+ ),
62
+ FastlaneCore::ConfigItem.new(
63
+ key: :parallel_workers,
64
+ env_name: "FL_TRY_SCAN_PARALLEL_WORKERS",
65
+ description: "Specify the exact number of test runners that will be spawned during parallel testing. Equivalent to -parallel-testing-worker-count and :concurrent_workers",
66
+ type: Integer,
67
+ is_string: false,
68
+ optional: true
95
69
  )
96
70
  ]
97
71
  end
@@ -101,7 +75,7 @@ module Fastlane
101
75
  end
102
76
 
103
77
  def self.is_supported?(platform)
104
- [:ios].include?(platform)
78
+ [:ios, :mac].include?(platform)
105
79
  end
106
80
  end
107
81
  end
@@ -19,31 +19,9 @@ module TryScanManager
19
19
  end
20
20
 
21
21
  def self.scan_options_from_try_scan_options(params)
22
- valid_scan_keys = Fastlane::Actions::ScanAction.available_options.map(&:key)
23
22
  params.select { |key, _| valid_scan_keys.include?(key) }
24
23
  end
25
24
 
26
- def self.options_from_configuration_file(params)
27
- config = FastlaneCore::Configuration.create(
28
- Fastlane::Actions::ScanAction.available_options,
29
- params
30
- )
31
- config_file = config.load_configuration_file(Scan.scanfile_name, nil, true)
32
- overridden_options = config_file ? config_file.options : {}
33
-
34
- FastlaneCore::Project.detect_projects(config)
35
- project = FastlaneCore::Project.new(config)
36
-
37
- imported_path = File.expand_path(Scan.scanfile_name)
38
- Dir.chdir(File.expand_path("..", project.path)) do
39
- if File.expand_path(Scan.scanfile_name) != imported_path
40
- config_file = config.load_configuration_file(Scan.scanfile_name, nil, true)
41
- end
42
- overridden_options.merge!(config_file.options) if config_file
43
- end
44
- overridden_options
45
- end
46
-
47
25
  def self.remove_preexisting_simulator_logs(params)
48
26
  return unless params[:include_simulator_logs]
49
27
 
@@ -90,4 +68,4 @@ module TryScanManager
90
68
  end
91
69
  end
92
70
  end
93
- end
71
+ end
@@ -5,42 +5,39 @@ module TryScanManager
5
5
 
6
6
  def initialize(options = {})
7
7
  @options = options
8
+ @options[:try_count] = 1 if @options[:try_count] < 1
9
+ @options[:result_bundle] = true
8
10
  end
9
11
 
10
12
  def run
11
- update_scan_config
13
+ configure_xcargs
14
+ prepare_scan_config(@options)
12
15
  print_summary
13
16
  @attempt = 1
14
17
  begin
15
18
  warn_of_performing_attempts
16
19
  clear_preexisting_data
17
20
  Scan::Runner.new.run
18
- print_parallel_scan_result
21
+ print_try_scan_result
19
22
  return true
20
23
  rescue FastlaneCore::Interface::FastlaneTestFailure => _
21
- failed_tests = extract_failed_tests
22
- print_parallel_scan_result(failed_tests_count: failed_tests.size)
23
- update_scan_junit_report
24
- return false if @attempt >= @options[:try_count]
24
+ failed_tests = failed_tests_from_xcresult_report
25
+ print_try_scan_result(failed_tests_count: failed_tests.size)
26
+ return false if finish?
25
27
 
26
28
  @attempt += 1
27
29
  update_scan_options(failed_tests)
28
30
  retry
29
31
  rescue FastlaneCore::Interface::FastlaneBuildFailure => _
30
- return false if @attempt >= @options[:try_count]
32
+ return false if finish?
31
33
 
32
34
  @attempt += 1
33
35
  retry
34
36
  end
35
37
  end
36
38
 
37
- def update_scan_config
38
- if !Scan.config[:output_types].include?('junit') && !parallel_running?
39
- output_types = Scan.config[:output_types].split(',')
40
- output_types << 'junit'
41
- Scan.config[:output_types] = output_types.join(',')
42
- end
43
- @options[:try_count] = 1 if @options[:try_count] < 1
39
+ def finish?
40
+ @attempt >= @options[:try_count]
44
41
  end
45
42
 
46
43
  def print_summary
@@ -70,9 +67,7 @@ module TryScanManager
70
67
  FastlaneScanHelper.remove_report_files
71
68
  end
72
69
 
73
- def print_parallel_scan_result(failed_tests_count: 0)
74
- return unless parallel_running?
75
-
70
+ def print_try_scan_result(failed_tests_count: 0)
76
71
  FastlaneCore::UI.important("TryScan: result after #{ordinalized_attempt} shot 👇")
77
72
  FastlaneCore::PrintTable.print_values(
78
73
  config: {"Number of tests" => tests_count_from_xcresult_report, "Number of failures" => failed_tests_count},
@@ -93,98 +88,61 @@ module TryScanManager
93
88
  end
94
89
  end
95
90
 
96
- def update_scan_options(failed_tests)
97
- scan_options = @options.select { |key, _|
98
- FastlaneScanHelper.valid_scan_keys.include?(key)
99
- }.merge(plugin_scan_options)
100
- scan_options[:only_testing] = failed_tests
101
- scan_options[:skip_build] = true
102
- scan_options[:test_without_building] = true
103
- scan_options[:build_for_testing] = false
104
- scan_options.delete(:skip_testing)
105
- Scan.cache.clear
106
- scan_options.each do |key, val|
107
- next if val.nil?
108
-
109
- Scan.config.set(key, val) unless val.nil?
110
- FastlaneCore::UI.verbose("\tSetting #{key.to_s} to #{val}")
111
- end
91
+ def prepare_scan_config(scan_options)
92
+ Scan.config = FastlaneCore::Configuration.create(
93
+ Fastlane::Actions::ScanAction.available_options,
94
+ FastlaneScanHelper.scan_options_from_try_scan_options(scan_options)
95
+ )
112
96
  end
113
97
 
114
- def plugin_scan_options
115
- xcargs = @options[:xcargs] || ''
116
- if xcargs&.include?('build-for-testing')
117
- FastlaneCore::UI.important(":xcargs, #{xcargs}, contained 'build-for-testing', removing it")
118
- xcargs.slice!('build-for-testing')
119
- end
120
- if xcargs.include?('-quiet')
121
- FastlaneCore::UI.important('Disabling -quiet as failing tests cannot be found with it enabled.')
122
- xcargs.gsub!('-quiet', '')
98
+ def configure_xcargs
99
+ if @options[:xcargs]&.include?('-parallel-testing-enabled')
100
+ FastlaneCore::UI.important("TryScan overwrites `-parallel-testing-enabled` in :xcargs, use :try_parallel option instead")
101
+ @options[:xcargs].gsub!(/-parallel-testing-enabled(=|\s+)(YES|NO)/, '')
123
102
  end
124
- @options.select { |key, _| FastlaneScanHelper.valid_scan_keys.include?(key) }.merge({ xcargs: xcargs })
125
- end
126
103
 
127
- def extract_failed_tests
128
- if parallel_running?
129
- failed_tests_from_xcresult_report
130
- else
131
- failed_tests_from_junit_report
104
+ if @options[:xcargs]&.include?('-parallel-testing-worker-count')
105
+ FastlaneCore::UI.important("TryScan overwrites `-parallel-testing-worker-count` in :xcargs, use :concurrent_workers option instead")
106
+ @options[:xcargs].gsub!(/-parallel-testing-worker-count(=|\s+)(\d+)/, '')
132
107
  end
133
- end
134
108
 
135
- def parallel_running?
136
- return @options[:concurrent_workers].to_i > 0 ||
137
- (@options[:devices] && @options[:devices].size > 1) ||
138
- (@options[:xcargs] && (@options[:xcargs] =~ /-parallel-testing-enabled(=|\s+)YES/ || @options[:xcargs].split('-destination').size > 2))
139
- end
140
-
141
- def failed_tests_from_junit_report
142
- report = junit_report
143
- suite_name = report.xpath('testsuites/@name').to_s.split('.')[0]
144
- test_cases = report.xpath('//testcase')
145
- only_testing = []
146
- test_cases.each do |test_case|
147
- next if test_case.xpath('failure').empty?
148
-
149
- test_class = test_case.xpath('@classname').to_s.split('.')[1]
150
- test_name = test_case.xpath('@name')
151
- only_testing << "#{suite_name}/#{test_class}/#{test_name}"
109
+ if @options[:xcargs]&.include?('build-for-testing') || @options[:build_for_testing]
110
+ FastlaneCore::UI.important("TryScan rejects `build-for-testing` request, use it in a separate scan lane")
111
+ @options[:xcargs].slice!('build-for-testing')
112
+ @options[:build_for_testing] = nil
152
113
  end
153
- only_testing
154
- end
155
114
 
156
- def junit_report(cached: false)
157
- unless cached
158
- report_options = FastlaneScanHelper.report_options
159
- output_files = report_options.instance_variable_get(:@output_files)
160
- output_directory = report_options.instance_variable_get(:@output_directory)
161
- file_name = output_files.select { |name| name.include?('.xml') }.first
162
- @junit_report_path = "#{output_directory}/#{file_name}"
163
- @cached_junit_report = File.open(@junit_report_path) { |f| Nokogiri::XML(f) }
115
+ xcargs = []
116
+ if @options[:try_parallel]
117
+ xcargs << '-parallel-testing-enabled YES'
118
+ if @options[:parallel_workers] || @options[:concurrent_workers]
119
+ workers_count = [@options[:parallel_workers].to_i, @options[:concurrent_workers].to_i].max
120
+ xcargs << "-parallel-testing-worker-count #{workers_count}"
121
+ @options[:concurrent_workers] = nil
122
+ end
123
+ else
124
+ xcargs << '-parallel-testing-enabled NO'
164
125
  end
165
- @cached_junit_report
126
+ @options[:xcargs] = "#{@options[:xcargs].to_s} #{xcargs.join(' ')}"
166
127
  end
167
128
 
168
- def update_scan_junit_report
169
- return if @attempt == 1 || parallel_running?
170
-
171
- old_junit_report = junit_report(cached: true)
172
- new_junit_report = junit_report(cached: false)
173
-
174
- new_junit_report.css("testsuites").zip(old_junit_report.css("testsuites")).each do |new_suites, old_suites|
175
- old_suites.attributes["failures"].value = new_suites.attributes["failures"].value
176
- new_suites.css("testsuite").zip(old_suites.css("testsuite")).each do |new_suite, old_suite|
177
- old_suite.attributes["failures"].value = new_suite.attributes["failures"].value
178
- end
129
+ def update_scan_options(failed_tests)
130
+ scan_options = FastlaneScanHelper.scan_options_from_try_scan_options(@options)
131
+ scan_options[:only_testing] = failed_tests
132
+ scan_options[:skip_build] = true
133
+ scan_options.delete(:skip_testing)
134
+ if @options[:try_parallel] && !@options[:retry_parallel]
135
+ scan_options[:xcargs].gsub!(/-parallel-testing-enabled(=|\s+)(YES|NO)/, '-parallel-testing-enabled NO')
136
+ scan_options[:xcargs].gsub!(/-parallel-testing-worker-count(=|\s+)(\d+)/, '')
179
137
  end
180
138
 
181
- new_junit_report.css('testcase').each do |node1|
182
- old_junit_report.css('testcase').each do |node2|
183
- node2.children = node1.children if node1['name'] == node2['name']
184
- end
185
- end
139
+ Scan.cache.clear
140
+ scan_options.each do |key, val|
141
+ next if val.nil?
186
142
 
187
- File.open(@junit_report_path, "w+") { |f| f.write(old_junit_report.to_xml) }
143
+ Scan.config.set(key, val)
144
+ FastlaneCore::UI.verbose("\tSetting #{key.to_s} to #{val}")
145
+ end
188
146
  end
189
147
 
190
148
  def parse_xcresult_report
@@ -201,8 +159,14 @@ module TryScanManager
201
159
  only_testing = []
202
160
  parse_xcresult_report['issues']['testFailureSummaries']['_values'].each do |failed_test|
203
161
  suite_name = failed_test['producingTarget']['_value']
204
- test_class = failed_test['testCaseName']['_value'].split('.').first
205
- test_name = failed_test['testCaseName']['_value'].split('.')[1].split('(').first
162
+ test_path = failed_test['testCaseName']['_value']
163
+ begin
164
+ test_class = test_path.split('.').first
165
+ test_name = test_path.split('.')[1].split('(').first
166
+ rescue NoMethodError => _
167
+ test_class = test_path.split('[')[1].split(' ').first
168
+ test_name = test_path.split(' ')[1].split(']').first
169
+ end
206
170
  only_testing << "#{suite_name}/#{test_class}/#{test_name}"
207
171
  end
208
172
  only_testing
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module TryScan
3
- VERSION = "0.2.0"
3
+ VERSION = "0.5.1"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,43 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-try_scan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexey Alter-Pesotskiy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-13 00:00:00.000000000 Z
11
+ date: 2020-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: nokogiri
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: json
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
13
  - !ruby/object:Gem::Dependency
42
14
  name: pry
43
15
  requirement: !ruby/object:Gem::Requirement