fastlane-plugin-retry 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6f8e3ca7cd39efec921792b15c54547aa6ca852aa22042878426240292c7d2fb
4
+ data.tar.gz: 13207f94594b1e2b56ed7cb281a9138f8ab7a3ceb5151ded1c44ae03118e9eb5
5
+ SHA512:
6
+ metadata.gz: ba9ca0bff84f6678ba1e62eb085d9f71ae1189f80c3c5ccd1b8b7ceba0f34b2c1be4572d57ca9d57a10bd20629117cfa9da62e4f532e7d9deab5da4ddecb7e80
7
+ data.tar.gz: 1be1c820c5104db4ad1cced8576887945414c466c4c6bb43d92faa14d19ea6f1cccccc570ae8efb1b64706d1abe80e72516bf7463cfe88ed05b988b948654ee5
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 gmgchow <gchow@mercari.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,59 @@
1
+ # retry plugin
2
+
3
+ [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-retry)
4
+
5
+ A fastlane plugin that automatically retries failed XCUITest test cases. This plugin is loosely based off [lyndsey-ferguson's fastlane-plugin-test_center](https://github.com/lyndsey-ferguson/fastlane-plugin-test_center) plugin, which uses JUnit reports instead of PList to generate a simple HTML report.
6
+
7
+ This plugin works with the following logic:
8
+ 1) Run the whole test suite once
9
+ 2) Parse the generated .plist results to obtain a list of the failed tests
10
+ 3) Retry the failed tests an 'x' number of times (see below for how to specify the number of retries) and generate a .plist report for each retry run
11
+ 4) Merge all .plist reports together to generate one final .plist report
12
+
13
+ Tip: You can then use the final .plist report with the [XCHtmlReport plugin](https://github.com/TitouanVanBelle/XCTestHTMLReport) to generate a very beautiful HTML report including screenshots and console logs! See the sample fastfile for usage.
14
+
15
+ This plugin is available in the [Ruby Gems directory](https://rubygems.org/gems/fastlane-plugin-retry).
16
+
17
+ ## Installation
18
+
19
+ This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-retry`, add it to your project by running:
20
+
21
+ ```
22
+ fastlane add_plugin retry
23
+ ```
24
+ Check that the command above generated a Pluginfile in your project's fastlane folder. The Pluginfile should contain the following text:
25
+ ```
26
+ gem 'fastlane-plugin-retry', '~> 1.0', '>= 1.0.5'
27
+ ```
28
+ Add the following line to your project's Gemfile:
29
+ ```
30
+ plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
31
+ eval_gemfile(plugins_path) if File.exist?(plugins_path)
32
+ ```
33
+ Finally, run the following command to install the plugin files to your project's Bundler folder.
34
+ ```
35
+ bundle install
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ See the sample fastfile for how to configure your project's fastfile. Once you have configured your fastfile, use the following command to run your tests with retry (you can change the number of tries and the device).
41
+ ```
42
+ bundle exec fastlane run_tests_with_retry tries:3 devices:"platform=iOS Simulator,name=iPhone 8,OS=11.4"
43
+ ```
44
+
45
+ ## Issues and Feedback
46
+
47
+ For any other issues and feedback about this plugin, please submit it to this repository.
48
+
49
+ ## Troubleshooting
50
+
51
+ If you have trouble using plugins, check out the [Plugins Troubleshooting](https://docs.fastlane.tools/plugins/plugins-troubleshooting/) guide.
52
+
53
+ ## Using _fastlane_ Plugins
54
+
55
+ For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://docs.fastlane.tools/plugins/create-plugin/).
56
+
57
+ ## About _fastlane_
58
+
59
+ _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).
@@ -0,0 +1,16 @@
1
+ require 'fastlane/plugin/retry/version'
2
+
3
+ module Fastlane
4
+ module Retry
5
+ # Return all .rb files inside the "actions" and "helper" directory
6
+ def self.all_classes
7
+ Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
8
+ end
9
+ end
10
+ end
11
+
12
+ # By default we want to import all available actions and helpers
13
+ # A plugin can contain any number of actions and plugins
14
+ Fastlane::Retry.all_classes.each do |current|
15
+ require current
16
+ end
@@ -0,0 +1,167 @@
1
+ require 'nokogiri'
2
+ require 'nokogiri-plist'
3
+ require 'FileUtils'
4
+
5
+ module Fastlane
6
+ module Actions
7
+ class CollateJunitReportsAction < Action
8
+
9
+ def self.run(params)
10
+ report_filepaths = params[:reports].reverse
11
+ # If no retries are required return the results
12
+ if report_filepaths.size == 1
13
+ FileUtils.cp(report_filepaths[0], params[:collated_report])
14
+ else
15
+ target_report = File.open(report_filepaths.shift) {|f| Nokogiri::XML(f)}
16
+ reports = report_filepaths.map { |report_filepath| Nokogiri::XML(Nokogiri::PList(open(report_filepath)).to_plist) }
17
+ # Clean each retry report and merge it into the first report
18
+ reports.each do |retry_report|
19
+ retry_report = clean_report(retry_report.to_s)
20
+ mergeLists(target_report, retry_report, params)
21
+ end
22
+ end
23
+ # Merge screenshots and console logs from all retry runs
24
+ merge_assets(params[:assets], params[:collated_report] + "/Attachments")
25
+ merge_logs(params[:logs], params[:collated_report] + "/")
26
+ end
27
+
28
+ # Merges .plist reports
29
+ def self.mergeLists(target_report, retry_report, params)
30
+ UI.verbose("Merging retried results...")
31
+ Dir.mkdir(params[:collated_report]) unless File.exists?(params[:collated_report])
32
+ file_name = params[:collated_report] + "/action_TestSummaries.plist"
33
+ retried_tests = retry_report.xpath("//key[contains(.,'TestSummaryGUID')]/..")
34
+ current_node = retried_tests.shift
35
+ while (current_node != nil)
36
+ # For each retried test, get the corresponding node of data from the retried report and merge it into the base report
37
+ testName = get_test_name(current_node)
38
+ matching_node = target_report.at_xpath("//string[contains(.,'#{testName}')]/..")
39
+ if (!matching_node.nil?)
40
+ matching_node.previous.next.replace(current_node)
41
+ write_report_to_file(target_report, file_name)
42
+ end
43
+ current_node = retried_tests.shift
44
+ end
45
+ end
46
+
47
+ # Merges screenshots from all retries
48
+ def self.merge_assets(asset_files, assets_folder)
49
+ UI.verbose ("Merging screenshot folders...")
50
+ Dir.mkdir(assets_folder) unless File.exists?(assets_folder)
51
+ asset_files.each do |folder|
52
+ FileUtils.cp_r(Dir[folder + '/*'], assets_folder)
53
+ end
54
+ end
55
+
56
+ # Cleans formatting of report
57
+ def self.clean_report(report)
58
+ # Removes unescaped <> characters which cause the final .plist to become unreadable
59
+ report = report.gsub("<XCAccessibilityElement:/>0x", " XCAccessibilityElement ")
60
+ report = report.gsub("<XCAccessibilityElement:></XCAccessibilityElement:>", " XCAccessibilityElement ")
61
+ report = Nokogiri::XML(report)
62
+ report
63
+ end
64
+
65
+ # Merges console log of reports
66
+ def self.merge_logs(log_files, logs_folder)
67
+ UI.verbose("Merging console logs...")
68
+ target_log = log_files.shift
69
+ log_files.each do |log|
70
+ to_append = File.read(log)
71
+ File.open(target_log, "a") do |handle|
72
+ handle.puts to_append
73
+ end
74
+ FileUtils.cp_r(target_log, logs_folder)
75
+ end
76
+ end
77
+
78
+ # Outputs report to a new file
79
+ def self.write_report_to_file(report, file_name)
80
+ UI.verbose("Writing merged results to file...")
81
+ File.new(file_name, 'w')
82
+ File.open(file_name, 'w') do |f|
83
+ f.write(report.to_xml)
84
+ end
85
+ end
86
+
87
+ # Returns the test name of the retried test
88
+ def self.get_test_name(test_data)
89
+ test_name = test_data.xpath("(//key[contains(.,'TestSummaryGUID')])/../key[contains(.,'TestName')]/following-sibling::string").to_a[0].to_s
90
+ test_name = test_name[8..-10]
91
+ test_name
92
+ end
93
+
94
+ #####################################################
95
+ # @!group Documentation
96
+ #####################################################
97
+
98
+ def self.description
99
+ "Combines test results from multiple plist files."
100
+ end
101
+
102
+ def self.details
103
+ "Based on the fastlane-plugins-test_center plugin by lyndsey-ferguson/@lyndseydf"
104
+ end
105
+
106
+ def self.available_options
107
+ [
108
+ FastlaneCore::ConfigItem.new(
109
+ key: :reports,
110
+ env_name: 'COLLATE_PLIST_REPORTS_REPORTS',
111
+ description: 'An array of plist reports to collate. The first report is used as the base into which other reports are merged in',
112
+ optional: false,
113
+ type: Array,
114
+ verify_block: proc do |reports|
115
+ UI.user_error!('No plist report files found') if reports.empty?
116
+ reports.each do |report|
117
+ UI.user_error!("Error: plist report not found: '#{report}'") unless File.exist?(report)
118
+ end
119
+ end
120
+ ),
121
+ FastlaneCore::ConfigItem.new(
122
+ key: :collated_report,
123
+ env_name: 'COLLATE_PLIST_REPORTS_COLLATED_REPORT',
124
+ description: 'The final plist report file where all testcases will be merged into',
125
+ optional: true,
126
+ default_value: 'result.xml',
127
+ type: String
128
+ ),
129
+ FastlaneCore::ConfigItem.new(
130
+ key: :assets,
131
+ env_name: 'COLLATE_PLIST_REPORTS_ASSETS',
132
+ description: 'An array of plist reports to collate. The first report is used as the base into which other reports are merged in',
133
+ optional: false,
134
+ type: Array,
135
+ verify_block: proc do |assets|
136
+ UI.user_error!('No plist report files found') if assets.empty?
137
+ assets.each do |asset|
138
+ UI.user_error!("Error: plist report not found: '#{asset}'") unless File.exist?(asset)
139
+ end
140
+ end
141
+ ),
142
+ FastlaneCore::ConfigItem.new(
143
+ key: :logs,
144
+ env_name: 'COLLATE_PLIST_REPORTS_LOGS',
145
+ description: 'An array of plist reports to collate. The first report is used as the base into which other reports are merged in',
146
+ optional: false,
147
+ type: Array,
148
+ verify_block: proc do |logs|
149
+ UI.user_error!('No plist report files found') if logs.empty?
150
+ logs.each do |log|
151
+ UI.user_error!("Error: plist report not found: '#{log}'") unless File.exist?(log)
152
+ end
153
+ end
154
+ )
155
+ ]
156
+ end
157
+
158
+ def self.authors
159
+ ["Gloria Chow/@gmgchow"]
160
+ end
161
+
162
+ def self.is_supported?(platform)
163
+ platform == :ios
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,122 @@
1
+ module Fastlane
2
+ module Actions
3
+ require 'fastlane/actions/scan'
4
+ require 'shellwords'
5
+ require 'nokogiri'
6
+ require 'nokogiri-plist'
7
+
8
+ class MultiScanAction < Action
9
+ def self.run(params)
10
+ try_count = 0
11
+ scan_options = params.values.reject { |k| k == :try_count }
12
+ final_report_path = scan_options[:result_bundle]
13
+ unless Helper.test?
14
+ FastlaneCore::PrintTable.print_values(
15
+ config: params._values.reject { |k, v| scan_options.key?(k) },
16
+ title: "Summary for multi_scan"
17
+ )
18
+ end
19
+
20
+ begin
21
+ try_count += 1
22
+ scan_options = config_with_retry(scan_options, try_count)
23
+ config = FastlaneCore::Configuration.create(Fastlane::Actions::ScanAction.available_options, scan_options)
24
+ Fastlane::Actions::ScanAction.run(config)
25
+ rescue FastlaneCore::Interface::FastlaneTestFailure => e
26
+ UI.verbose("Scan failed with #{e}")
27
+ if try_count < params[:try_count]
28
+ report_filepath = plist_report_filepath(scan_options)
29
+ failed_tests = parse_failures(report_filepath, params[:scheme])
30
+ scan_options[:only_testing] = failed_tests
31
+ retry
32
+ end
33
+ end
34
+ merge_reports(scan_options, final_report_path)
35
+ end
36
+
37
+ # Parse the names of the failed test cases
38
+ def self.parse_failures(plist, scheme_name)
39
+ failures = Array.new
40
+ target_report = File.open(plist) {|f| Nokogiri::XML(f)}
41
+ # Get the names of all the failed tests from the specified report
42
+ failed = target_report.xpath("//key[contains(.,'Failure')]/../key[contains(.,'TestIdentifier')]/following-sibling::string[contains(.,'()') and contains (., '/')]")
43
+ failed.each do |test_name|
44
+ # Reformat the test name to be usable by the xcodebuild 'only_testing' flag
45
+ failures << ("#{scheme_name}/" + test_name.to_s.split('(')[0].split('>')[1])
46
+ end
47
+ failures
48
+ end
49
+
50
+ # Merge results from all retries
51
+ def self.merge_reports(scan_options, final_report_path)
52
+ folder = get_folder_root(scan_options[:output_directory])
53
+ report_files = Dir.glob("#{folder}*/*/*/action_TestSummaries.plist")
54
+ asset_files = Dir.glob("#{folder}*/*/*/Attachments")
55
+ log_files = Dir.glob("#{folder}*/*/*/action.xcactivitylog")
56
+ if report_files.size > 1
57
+ other_action.collate_junit_reports(
58
+ reports: report_files,
59
+ collated_report: final_report_path,
60
+ assets: asset_files,
61
+ logs: log_files,
62
+ )
63
+ end
64
+ end
65
+
66
+ # Create scan config
67
+ def self.config_with_retry(config, count)
68
+ folder = get_folder_root(config[:result_bundle])
69
+ config[:result_bundle] = (folder + count.to_s)
70
+ config[:output_directory] = (folder + count.to_s)
71
+ config
72
+ end
73
+
74
+ # Get folder location
75
+ def self.get_folder_root(folder)
76
+ folder = folder.gsub(/ *\d+$/, '')
77
+ folder
78
+ end
79
+
80
+ def self.plist_report_filepath(config)
81
+ File.absolute_path(File.join(config[:output_directory], "/#{config[:scheme]}.test_result/TestSummaries.plist"))
82
+ end
83
+
84
+ #####################################################
85
+ # @!group Documentation
86
+ #####################################################
87
+
88
+ def self.description
89
+ "Uses scan to run Xcode tests a given number of times: only re-testing failing tests."
90
+ end
91
+
92
+ def self.details
93
+ "Use this action to run your tests if you have fragile tests that fail sporadically."
94
+ end
95
+
96
+ def self.scan_options
97
+ ScanAction.available_options
98
+ end
99
+
100
+ def self.available_options
101
+ scan_options + [
102
+ FastlaneCore::ConfigItem.new(
103
+ key: :try_count,
104
+ env_name: "FL_MULTI_SCAN_TRY_COUNT",
105
+ description: "The number of times to retry running tests via scan",
106
+ type: Integer,
107
+ is_string: false,
108
+ default_value: 1
109
+ )
110
+ ]
111
+ end
112
+
113
+ def self.authors
114
+ ["Gloria Chow/@gmgchow"]
115
+ end
116
+
117
+ def self.is_supported?(platform)
118
+ platform == :ios
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,47 @@
1
+ require 'fastlane/action'
2
+ require_relative '../helper/retry_helper'
3
+
4
+ module Fastlane
5
+ module Actions
6
+ class RetryAction < Action
7
+ def self.run(params)
8
+ UI.message("The retry plugin is working!")
9
+ end
10
+
11
+ def self.description
12
+ "Retries failed XCUITest test cases"
13
+ end
14
+
15
+ def self.authors
16
+ ["gmgchow"]
17
+ end
18
+
19
+ def self.return_value
20
+ # If your method provides a return value, you can describe here what it does
21
+ end
22
+
23
+ def self.details
24
+ # Optional:
25
+ ""
26
+ end
27
+
28
+ def self.available_options
29
+ [
30
+ # FastlaneCore::ConfigItem.new(key: :your_option,
31
+ # env_name: "RETRY_YOUR_OPTION",
32
+ # description: "A description of your option",
33
+ # optional: false,
34
+ # type: String)
35
+ ]
36
+ end
37
+
38
+ def self.is_supported?(platform)
39
+ # Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
40
+ # See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
41
+ #
42
+ # [:ios, :mac, :android].include?(platform)
43
+ true
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,16 @@
1
+ require 'fastlane_core/ui/ui'
2
+
3
+ module Fastlane
4
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
5
+
6
+ module Helper
7
+ class RetryHelper
8
+ # class methods that you define here become available in your action
9
+ # as `Helper::RetryHelper.your_method`
10
+ #
11
+ def self.show_message
12
+ UI.message("Hello from the retry plugin helper!")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ module Fastlane
2
+ module Retry
3
+ VERSION = "1.1.0"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,177 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastlane-plugin-retry
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - gmgchow
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-06-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pry
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
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: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec_junit_formatter
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 0.49.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 0.49.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-require_tools
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: fastlane
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: 2.97.0
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: 2.97.0
139
+ description:
140
+ email: gchow@mercari.com
141
+ executables: []
142
+ extensions: []
143
+ extra_rdoc_files: []
144
+ files:
145
+ - LICENSE
146
+ - README.md
147
+ - lib/fastlane/plugin/retry.rb
148
+ - lib/fastlane/plugin/retry/actions/collate_junit_reports.rb
149
+ - lib/fastlane/plugin/retry/actions/multi_scan.rb
150
+ - lib/fastlane/plugin/retry/actions/retry_action.rb
151
+ - lib/fastlane/plugin/retry/helper/retry_helper.rb
152
+ - lib/fastlane/plugin/retry/version.rb
153
+ homepage:
154
+ licenses:
155
+ - MIT
156
+ metadata: {}
157
+ post_install_message:
158
+ rdoc_options: []
159
+ require_paths:
160
+ - lib
161
+ required_ruby_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ required_rubygems_version: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ requirements: []
172
+ rubyforge_project:
173
+ rubygems_version: 2.7.7
174
+ signing_key:
175
+ specification_version: 4
176
+ summary: Retries failed XCUITest test cases
177
+ test_files: []