fastlane-plugin-try_scan 0.2.0 → 0.5.1

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: 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