fastlane 1.20.0 → 1.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/bin/fastlane +16 -16
  3. data/lib/assets/custom_action_template.rb +2 -2
  4. data/lib/fastlane.rb +1 -0
  5. data/lib/fastlane/action.rb +5 -6
  6. data/lib/fastlane/action_collector.rb +6 -6
  7. data/lib/fastlane/actions/actions_helper.rb +12 -74
  8. data/lib/fastlane/actions/add_git_tag.rb +2 -2
  9. data/lib/fastlane/actions/appstore.rb +2 -2
  10. data/lib/fastlane/actions/backup_file.rb +2 -2
  11. data/lib/fastlane/actions/backup_xcarchive.rb +11 -12
  12. data/lib/fastlane/actions/bundle_install.rb +5 -1
  13. data/lib/fastlane/actions/chatwork.rb +12 -12
  14. data/lib/fastlane/actions/clean_build_artifacts.rb +4 -6
  15. data/lib/fastlane/actions/clipboard.rb +1 -1
  16. data/lib/fastlane/actions/commit_version_bump.rb +35 -10
  17. data/lib/fastlane/actions/crashlytics.rb +26 -26
  18. data/lib/fastlane/actions/create_keychain.rb +6 -6
  19. data/lib/fastlane/actions/default_platform.rb +1 -1
  20. data/lib/fastlane/actions/delete_keychain.rb +3 -3
  21. data/lib/fastlane/actions/deliver.rb +3 -3
  22. data/lib/fastlane/actions/deploygate.rb +19 -18
  23. data/lib/fastlane/actions/dsym_zip.rb +7 -9
  24. data/lib/fastlane/actions/ensure_git_branch.rb +2 -3
  25. data/lib/fastlane/actions/ensure_no_debug_code.rb +8 -8
  26. data/lib/fastlane/actions/fastlane_version.rb +4 -2
  27. data/lib/fastlane/actions/frameit.rb +7 -7
  28. data/lib/fastlane/actions/gcovr.rb +5 -4
  29. data/lib/fastlane/actions/get_build_number.rb +27 -30
  30. data/lib/fastlane/actions/get_github_release.rb +11 -11
  31. data/lib/fastlane/actions/get_version_number.rb +28 -30
  32. data/lib/fastlane/actions/git_branch.rb +1 -2
  33. data/lib/fastlane/actions/gym.rb +2 -2
  34. data/lib/fastlane/actions/hg_commit_version_bump.rb +28 -6
  35. data/lib/fastlane/actions/hg_push.rb +0 -1
  36. data/lib/fastlane/actions/hipchat.rb +20 -20
  37. data/lib/fastlane/actions/hockey.rb +16 -17
  38. data/lib/fastlane/actions/import.rb +1 -2
  39. data/lib/fastlane/actions/import_certificate.rb +1 -1
  40. data/lib/fastlane/actions/import_from_git.rb +6 -2
  41. data/lib/fastlane/actions/increment_build_number.rb +25 -28
  42. data/lib/fastlane/actions/increment_version_number.rb +55 -57
  43. data/lib/fastlane/actions/install_carthage.rb +10 -11
  44. data/lib/fastlane/actions/install_cocoapods.rb +3 -3
  45. data/lib/fastlane/actions/ipa.rb +8 -6
  46. data/lib/fastlane/actions/is_ci.rb +37 -0
  47. data/lib/fastlane/actions/lane_context.rb +2 -2
  48. data/lib/fastlane/actions/last_git_tag.rb +1 -1
  49. data/lib/fastlane/actions/lcov.rb +28 -31
  50. data/lib/fastlane/actions/mailgun.rb +11 -42
  51. data/lib/fastlane/actions/oclint.rb +10 -13
  52. data/lib/fastlane/actions/pem.rb +2 -2
  53. data/lib/fastlane/actions/pilot.rb +4 -2
  54. data/lib/fastlane/actions/prompt.rb +6 -6
  55. data/lib/fastlane/actions/push_git_tags.rb +38 -0
  56. data/lib/fastlane/actions/push_to_git_remote.rb +4 -1
  57. data/lib/fastlane/actions/puts.rb +1 -1
  58. data/lib/fastlane/actions/register_devices.rb +4 -3
  59. data/lib/fastlane/actions/reset_git_repo.rb +6 -6
  60. data/lib/fastlane/actions/resign.rb +4 -5
  61. data/lib/fastlane/actions/restore_file.rb +2 -2
  62. data/lib/fastlane/actions/s3.rb +19 -22
  63. data/lib/fastlane/actions/set_build_number_repository.rb +26 -36
  64. data/lib/fastlane/actions/set_changelog.rb +4 -4
  65. data/lib/fastlane/actions/set_github_release.rb +10 -11
  66. data/lib/fastlane/actions/sigh.rb +1 -1
  67. data/lib/fastlane/actions/slack.rb +85 -74
  68. data/lib/fastlane/actions/snapshot.rb +2 -2
  69. data/lib/fastlane/actions/team_id.rb +2 -1
  70. data/lib/fastlane/actions/team_name.rb +2 -1
  71. data/lib/fastlane/actions/testflight.rb +2 -2
  72. data/lib/fastlane/actions/testmunk.rb +16 -16
  73. data/lib/fastlane/actions/typetalk.rb +14 -14
  74. data/lib/fastlane/actions/update_app_group_identifiers.rb +17 -17
  75. data/lib/fastlane/actions/update_fastlane.rb +16 -14
  76. data/lib/fastlane/actions/update_info_plist.rb +14 -16
  77. data/lib/fastlane/actions/update_project_code_signing.rb +3 -3
  78. data/lib/fastlane/actions/update_project_provisioning.rb +48 -36
  79. data/lib/fastlane/actions/xcode_select.rb +4 -3
  80. data/lib/fastlane/actions/xcode_server_get_assets.rb +23 -24
  81. data/lib/fastlane/actions/xcodebuild.rb +13 -13
  82. data/lib/fastlane/actions/xctool.rb +2 -2
  83. data/lib/fastlane/actions_list.rb +27 -29
  84. data/lib/fastlane/command_line_handler.rb +13 -14
  85. data/lib/fastlane/configuration_helper.rb +18 -20
  86. data/lib/fastlane/core_ext/string.rb +2 -2
  87. data/lib/fastlane/docs_generator.rb +33 -32
  88. data/lib/fastlane/erb_template_helper.rb +1 -1
  89. data/lib/fastlane/fast_file.rb +27 -18
  90. data/lib/fastlane/fastlane_folder.rb +1 -1
  91. data/lib/fastlane/helper/git_helper.rb +27 -0
  92. data/lib/fastlane/helper/sh_helper.rb +48 -0
  93. data/lib/fastlane/junit_generator.rb +1 -1
  94. data/lib/fastlane/lane.rb +6 -6
  95. data/lib/fastlane/lane_list.rb +12 -13
  96. data/lib/fastlane/lane_manager.rb +17 -11
  97. data/lib/fastlane/new_action.rb +1 -1
  98. data/lib/fastlane/runner.rb +23 -24
  99. data/lib/fastlane/setup.rb +16 -17
  100. data/lib/fastlane/supported_platforms.rb +2 -2
  101. data/lib/fastlane/version.rb +1 -1
  102. metadata +16 -12
@@ -6,7 +6,7 @@ module Fastlane
6
6
  raise 'xctool not installed, please install using `brew install xctool`'.red if `which xctool`.length == 0
7
7
  end
8
8
 
9
- params = [] if params.kind_of?FastlaneCore::Configuration
9
+ params = [] if params.kind_of? FastlaneCore::Configuration
10
10
 
11
11
  Actions.sh('xctool ' + params.join(' '))
12
12
  end
@@ -27,7 +27,7 @@ module Fastlane
27
27
  end
28
28
 
29
29
  def self.is_supported?(platform)
30
- [:ios, :mac].include?platform
30
+ [:ios, :mac].include? platform
31
31
  end
32
32
  end
33
33
  end
@@ -2,8 +2,11 @@ module Fastlane
2
2
  class ActionsList
3
3
  def self.run(filter)
4
4
  require 'terminal-table'
5
- print_all unless filter
6
- show_details(filter) if filter
5
+ if filter
6
+ show_details(filter)
7
+ else
8
+ print_all
9
+ end
7
10
  end
8
11
 
9
12
  def self.print_all
@@ -18,8 +21,6 @@ module Fastlane
18
21
  authors = Array(action.author || action.authors)
19
22
  current << authors.first.green if authors.count == 1
20
23
  current << "Multiple".green if authors.count > 1
21
-
22
- l = (action.description || '').length
23
24
  else
24
25
  Helper.log.error "Please update your action file #{name} to be a subclass of `Action` by adding ` < Action` after your class name.".red
25
26
  current << "Please update action file".red
@@ -45,7 +46,7 @@ module Fastlane
45
46
 
46
47
  all_actions do |action, name|
47
48
  next unless name == filter.strip
48
-
49
+
49
50
  rows = []
50
51
  rows << [action.description] if action.description
51
52
  rows << [' ']
@@ -89,8 +90,6 @@ module Fastlane
89
90
  puts ""
90
91
  end
91
92
 
92
-
93
-
94
93
  puts "More information can be found on https://github.com/KrauseFx/fastlane/blob/master/docs/Actions.md"
95
94
  puts "\n"
96
95
 
@@ -102,36 +101,35 @@ module Fastlane
102
101
  print_all # show all available actions instead
103
102
  end
104
103
 
105
-
106
104
  # Iterates through all available actions and yields from there
107
105
  def self.all_actions
108
- all_actions = Fastlane::Actions.constants.select {|c| Class === Fastlane::Actions.const_get(c)}
109
- all_actions.sort.each do |symbol|
106
+ all_actions = Fastlane::Actions.constants.select {|c| Fastlane::Actions.const_get(c).kind_of? Class }
107
+ all_actions.sort.each do |symbol|
110
108
  action = Fastlane::Actions.const_get(symbol)
111
109
  name = symbol.to_s.gsub('Action', '').fastlane_underscore
112
110
  yield action, name
113
111
  end
114
112
  end
115
-
116
- private
117
- def self.parse_options(options, fill_all = true)
118
- rows = []
119
- rows << [options] if options.kind_of?String
120
-
121
- if options.kind_of?Array
122
- options.each do |current|
123
- if current.kind_of?FastlaneCore::ConfigItem
124
- rows << [current.key.to_s.yellow, current.description, current.env_name]
125
- elsif current.kind_of?Array
126
- raise "Invalid number of elements in this row: #{current}. Must be 2 or 3".red unless ([2, 3].include?current.count)
127
- rows << current
128
- rows.last[0] = rows.last.first.yellow # color it yellow :)
129
- rows.last << nil while (fill_all and rows.last.count < 3) # to have a nice border in the table
130
- end
113
+
114
+ # Helper:
115
+ def self.parse_options(options, fill_all = true)
116
+ rows = []
117
+ rows << [options] if options.kind_of? String
118
+
119
+ if options.kind_of? Array
120
+ options.each do |current|
121
+ if current.kind_of? FastlaneCore::ConfigItem
122
+ rows << [current.key.to_s.yellow, current.description, current.env_name]
123
+ elsif current.kind_of? Array
124
+ raise "Invalid number of elements in this row: #{current}. Must be 2 or 3".red unless [2, 3].include? current.count
125
+ rows << current
126
+ rows.last[0] = rows.last.first.yellow # color it yellow :)
127
+ rows.last << nil while fill_all and rows.last.count < 3 # to have a nice border in the table
131
128
  end
132
129
  end
133
-
134
- rows
135
130
  end
131
+
132
+ rows
133
+ end
136
134
  end
137
- end
135
+ end
@@ -7,9 +7,9 @@ module Fastlane
7
7
  lane_parameters = {} # the parameters we'll pass to the lane
8
8
  platform_lane_info = [] # the part that's responsible for the lane/platform definition
9
9
  args.each do |current|
10
- if current.include?":" # that's a key/value which we want to pass to the lane
11
- key, value = current.split(":")
12
- raise "Please pass values like this: key:value" unless (key.length > 0 and value.length > 0)
10
+ if current.include? ":" # that's a key/value which we want to pass to the lane
11
+ key, value = current.split(":")
12
+ raise "Please pass values like this: key:value" unless key.length > 0 and value.length > 0
13
13
  value = convert_value(value)
14
14
  Helper.log.debug "Using #{key}: #{value}".green
15
15
  lane_parameters[key.to_sym] = value
@@ -26,19 +26,18 @@ module Fastlane
26
26
  lane = platform_lane_info[0]
27
27
  end
28
28
 
29
- dot_env = options.env rescue nil # we don't really care about this option in tests
29
+ dot_env = Helper.is_test? ? nil : options.env
30
30
 
31
31
  Fastlane::LaneManager.cruise_lane(platform, lane, lane_parameters, dot_env)
32
32
  end
33
33
 
34
- private
35
- # Conert into the right data type
36
- def self.convert_value(value)
37
- return true if (value == 'true' or value == 'yes')
38
- return false if (value == 'false' || value == 'no')
39
-
40
- # Default case:
41
- return value
42
- end
34
+ # Helper to convert into the right data type
35
+ def self.convert_value(value)
36
+ return true if value == 'true' || value == 'yes'
37
+ return false if value == 'false' || value == 'no'
38
+
39
+ # Default case:
40
+ return value
41
+ end
43
42
  end
44
- end
43
+ end
@@ -1,29 +1,27 @@
1
1
  module Fastlane
2
2
  class ConfigurationHelper
3
3
  def self.parse(action, params)
4
- begin
5
- first_element = (action.available_options.first rescue nil) # might also be nil
6
-
7
- if first_element and first_element.kind_of?FastlaneCore::ConfigItem
8
- # default use case
9
- return FastlaneCore::Configuration.create(action.available_options, params)
4
+ first_element = (action.available_options || []).first
10
5
 
11
- elsif first_element
12
- puts "Old configuration format for action '#{action}'".red if Helper.is_test?
13
- return params
14
- else
6
+ if first_element and first_element.kind_of? FastlaneCore::ConfigItem
7
+ # default use case
8
+ return FastlaneCore::Configuration.create(action.available_options, params)
15
9
 
16
- # No parameters... we still need the configuration object array
17
- FastlaneCore::Configuration.create(action.available_options, {})
10
+ elsif first_element
11
+ puts "Old configuration format for action '#{action}'".red if Helper.is_test?
12
+ return params
13
+ else
18
14
 
19
- end
20
- rescue => ex
21
- if action.respond_to?:action_name
22
- Helper.log.fatal "You passed invalid parameters to '#{action.action_name}'.".red
23
- Helper.log.fatal "Check out the error below and available options by running `fastlane action #{action.action_name}`".red
24
- end
25
- raise ex
15
+ # No parameters... we still need the configuration object array
16
+ FastlaneCore::Configuration.create(action.available_options, {})
17
+
18
+ end
19
+ rescue => ex
20
+ if action.respond_to? :action_name
21
+ Helper.log.fatal "You passed invalid parameters to '#{action.action_name}'.".red
22
+ Helper.log.fatal "Check out the error below and available options by running `fastlane action #{action.action_name}`".red
26
23
  end
24
+ raise ex
27
25
  end
28
26
  end
29
- end
27
+ end
@@ -5,8 +5,8 @@ class String
5
5
 
6
6
  def fastlane_underscore
7
7
  self.gsub(/::/, '/').
8
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
9
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
8
+ gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
9
+ gsub(/([a-z\d])([A-Z])/, '\1_\2').
10
10
  tr("-", "_").
11
11
  downcase
12
12
  end
@@ -10,25 +10,24 @@ module Fastlane
10
10
  output << "```"
11
11
 
12
12
  output << "# Available Actions"
13
-
14
- all_keys = ff.runner.lanes.keys.reject(&:nil?)
13
+
14
+ all_keys = ff.runner.lanes.keys.reject(&:nil?)
15
15
  all_keys.unshift(nil) # because we want root elements on top. always! They have key nil
16
16
 
17
17
  all_keys.each do |platform|
18
18
  output << "## #{formatted_platform(platform)}" if platform
19
19
 
20
20
  value = ff.runner.lanes[platform]
21
+ next unless value
21
22
 
22
- if value
23
- value.each do |lane_name, lane|
24
- next if lane.is_private
25
- output << render(platform, lane_name, lane.description.join("\n\n"))
26
- end
27
-
28
- output << ""
29
- output << "----"
30
- output << ""
23
+ value.each do |lane_name, lane|
24
+ next if lane.is_private
25
+ output << render(platform, lane_name, lane.description.join("\n\n"))
31
26
  end
27
+
28
+ output << ""
29
+ output << "----"
30
+ output << ""
32
31
  end
33
32
 
34
33
  output << "Generate this documentation by running `fastlane docs`"
@@ -39,29 +38,31 @@ module Fastlane
39
38
  Helper.log.info "Successfully generated documentation to path '#{File.expand_path(output_path)}'".green
40
39
  end
41
40
 
42
- private
41
+ #####################################################
42
+ # @!group Helper
43
+ #####################################################
43
44
 
44
- def self.formatted_platform(pl)
45
- pl = pl.to_s
46
- return "iOS" if pl == 'ios'
47
- return "Mac" if pl == 'mac'
45
+ def self.formatted_platform(pl)
46
+ pl = pl.to_s
47
+ return "iOS" if pl == 'ios'
48
+ return "Mac" if pl == 'mac'
48
49
 
49
- return pl
50
- end
50
+ return pl
51
+ end
51
52
 
52
- # @param platform [String]
53
- # @param lane [Fastlane::Lane]
54
- # @param description [String]
55
- def self.render(platform, lane, description)
56
- full_name = [platform, lane].reject(&:nil?).join(' ')
53
+ # @param platform [String]
54
+ # @param lane [Fastlane::Lane]
55
+ # @param description [String]
56
+ def self.render(platform, lane, description)
57
+ full_name = [platform, lane].reject(&:nil?).join(' ')
57
58
 
58
- output = []
59
- output << "### #{full_name}"
60
- output << "```"
61
- output << "fastlane #{full_name}"
62
- output << "```"
63
- output << description
64
- output
65
- end
59
+ output = []
60
+ output << "### #{full_name}"
61
+ output << "```"
62
+ output << "fastlane #{full_name}"
63
+ output << "```"
64
+ output << description
65
+ output
66
+ end
66
67
  end
67
- end
68
+ end
@@ -13,7 +13,7 @@ module Fastlane
13
13
  File.read(template_filepath)
14
14
  end
15
15
 
16
- def self.render(template,template_vars_hash)
16
+ def self.render(template, template_vars_hash)
17
17
  Fastlane::ErbalT.new(template_vars_hash).render(template)
18
18
  end
19
19
 
@@ -23,13 +23,14 @@ module Fastlane
23
23
  @runner ||= Runner.new
24
24
 
25
25
  Dir.chdir(Fastlane::FastlaneFolder.path || Dir.pwd) do # context: fastlane subfolder
26
+ # rubocop:disable Lint/Eval
26
27
  eval(data) # this is okay in this case
28
+ # rubocop:enable Lint/Eval
27
29
  end
28
30
 
29
31
  self
30
32
  end
31
33
 
32
-
33
34
  #####################################################
34
35
  # @!group DSL
35
36
  #####################################################
@@ -37,7 +38,7 @@ module Fastlane
37
38
  # User defines a new lane
38
39
  def lane(lane_name, &block)
39
40
  raise "You have to pass a block using 'do' for lane '#{lane_name}'. Make sure you read the docs on GitHub.".red unless block
40
-
41
+
41
42
  self.runner.add_lane(Lane.new(platform: self.current_platform,
42
43
  block: block,
43
44
  description: desc_collection,
@@ -72,10 +73,10 @@ module Fastlane
72
73
 
73
74
  @desc_collection = nil # reset the collected description again for the next lane
74
75
  end
75
-
76
+
76
77
  # User defines a platform block
77
78
  def platform(platform_name, &block)
78
- SupportedPlatforms.verify!platform_name
79
+ SupportedPlatforms.verify! platform_name
79
80
 
80
81
  self.current_platform = platform_name
81
82
 
@@ -101,12 +102,15 @@ module Fastlane
101
102
 
102
103
  # Is used to look if the method is implemented as an action
103
104
  def method_missing(method_sym, *arguments, &_block)
105
+ method_str = method_sym.to_s
106
+ method_str.delete!('?') # as a `?` could be at the end of the method name
107
+
104
108
  # First, check if there is a predefined method in the actions folder
105
- class_name = method_sym.to_s.fastlane_class + 'Action'
109
+ class_name = method_str.fastlane_class + 'Action'
106
110
  class_ref = nil
107
111
  begin
108
112
  class_ref = Fastlane::Actions.const_get(class_name)
109
- rescue NameError => ex
113
+ rescue NameError
110
114
  # Action not found
111
115
  # Is there a lane under this name?
112
116
  return self.runner.try_switch_to_lane(method_sym, arguments)
@@ -135,8 +139,8 @@ module Fastlane
135
139
  def is_platform_block?(key)
136
140
  raise 'No key given'.red unless key
137
141
 
138
- return false if (self.runner.lanes[nil][key.to_sym] rescue false)
139
- return true if self.runner.lanes[key.to_sym].kind_of?Hash
142
+ return false if self.runner.lanes.fetch(nil, {}).fetch(key.to_sym, nil)
143
+ return true if self.runner.lanes[key.to_sym].kind_of? Hash
140
144
 
141
145
  raise "Could not find '#{key}'. Available lanes: #{self.runner.available_lanes.join(', ')}".red
142
146
  end
@@ -169,9 +173,9 @@ module Fastlane
169
173
  unless Pathname.new(path).absolute? # unless an absolute path
170
174
  path = File.join(File.expand_path('..', @path), path)
171
175
  end
172
-
173
- raise "Could not find Fastfile at path '#{path}'".red unless File.exists?(path)
174
-
176
+
177
+ raise "Could not find Fastfile at path '#{path}'".red unless File.exist?(path)
178
+
175
179
  collector.did_launch_action(:import)
176
180
  parse(File.read(path))
177
181
 
@@ -181,8 +185,9 @@ module Fastlane
181
185
  end
182
186
 
183
187
  # @param url [String] The git URL to clone the repository from
188
+ # @param branch [String] The branch to checkout in the repository
184
189
  # @param path [String] The path to the Fastfile
185
- def import_from_git(url: nil, path: 'fastlane/Fastfile')
190
+ def import_from_git(url: nil, branch: 'HEAD', path: 'fastlane/Fastfile')
186
191
  raise "Please pass a path to the `import_from_git` action".red if url.to_s.length == 0
187
192
 
188
193
  Actions.execute_action('import_from_git') do
@@ -192,13 +197,17 @@ module Fastlane
192
197
  repo_name = url.split("/").last
193
198
 
194
199
  folder = File.join("/tmp", "fl_clones", repo_name)
195
- clone_command = "git clone '#{url}' '#{folder}' --depth 1 -n"
196
200
 
197
- if File.directory?folder
201
+ branch_option = ""
202
+ branch_option = "--branch #{branch}" if branch != 'HEAD'
203
+
204
+ clone_command = "git clone '#{url}' '#{folder}' --depth 1 -n #{branch_option}"
205
+
206
+ if File.directory? folder
198
207
  Helper.log.info "Using existing git repo..."
199
208
  begin
200
209
  Actions.sh("cd '#{folder}' && git pull")
201
- rescue => ex
210
+ rescue
202
211
  # Something went wrong, clear the folder and pull again
203
212
  Actions.sh("rm -rf '#{folder}'")
204
213
  Actions.sh(clone_command)
@@ -209,15 +218,15 @@ module Fastlane
209
218
  Actions.sh(clone_command)
210
219
  end
211
220
 
212
- Actions.sh("cd '#{folder}' && git checkout HEAD '#{path}'")
221
+ Actions.sh("cd '#{folder}' && git checkout #{branch} '#{path}'")
213
222
 
214
223
  # We also want to check out all the local actions of this fastlane setup
215
224
  containing = path.split(File::SEPARATOR)[0..-2]
216
225
  containing = "." if containing.count == 0
217
226
  actions_folder = File.join(containing, "actions")
218
227
  begin
219
- Actions.sh("cd '#{folder}' && git checkout HEAD '#{actions_folder}'")
220
- rescue => ex
228
+ Actions.sh("cd '#{folder}' && git checkout #{branch} '#{actions_folder}'")
229
+ rescue
221
230
  # We don't care about a failure here, as local actions are optional
222
231
  end
223
232
 
@@ -13,7 +13,7 @@ module Fastlane
13
13
  return value
14
14
  end
15
15
 
16
- # Does a fastlane configuration already exist?
16
+ # Does a fastlane configuration already exist?
17
17
  def self.setup?
18
18
  return false unless path
19
19
  File.exist?(File.join(path, "Fastfile"))