fastlane 2.69.0.beta.20171212010004 → 2.69.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/deliver/lib/assets/DeliverfileDefault.swift +13 -0
  3. data/deliver/lib/deliver/commands_generator.rb +2 -1
  4. data/deliver/lib/deliver/options.rb +8 -3
  5. data/deliver/lib/deliver/setup.rb +19 -3
  6. data/fastlane/lib/assets/AppfileTemplate.swift +8 -0
  7. data/fastlane/lib/assets/DefaultFastfileTemplate.swift +72 -0
  8. data/fastlane/lib/fastlane.rb +6 -0
  9. data/fastlane/lib/fastlane/.DS_Store +0 -0
  10. data/fastlane/lib/fastlane/action.rb +13 -0
  11. data/fastlane/lib/fastlane/actions/adb.rb +4 -0
  12. data/fastlane/lib/fastlane/actions/appium.rb +2 -0
  13. data/fastlane/lib/fastlane/actions/bundle_install.rb +2 -0
  14. data/fastlane/lib/fastlane/actions/carthage.rb +12 -24
  15. data/fastlane/lib/fastlane/actions/changelog_from_git_commits.rb +5 -0
  16. data/fastlane/lib/fastlane/actions/cocoapods.rb +2 -1
  17. data/fastlane/lib/fastlane/actions/commit_github_file.rb +4 -0
  18. data/fastlane/lib/fastlane/actions/environment_variable.rb +73 -0
  19. data/fastlane/lib/fastlane/actions/get_version_number.rb +4 -4
  20. data/fastlane/lib/fastlane/actions/increment_build_number.rb +4 -0
  21. data/fastlane/lib/fastlane/actions/increment_version_number.rb +4 -0
  22. data/fastlane/lib/fastlane/actions/is_ci.rb +4 -0
  23. data/fastlane/lib/fastlane/actions/last_git_commit.rb +4 -0
  24. data/fastlane/lib/fastlane/actions/last_git_tag.rb +4 -0
  25. data/fastlane/lib/fastlane/actions/latest_testflight_build_number.rb +4 -0
  26. data/fastlane/lib/fastlane/actions/modify_services.rb +1 -0
  27. data/fastlane/lib/fastlane/actions/number_of_commits.rb +4 -0
  28. data/fastlane/lib/fastlane/actions/podio_item.rb +1 -0
  29. data/fastlane/lib/fastlane/actions/prompt.rb +4 -0
  30. data/fastlane/lib/fastlane/actions/read_podspec.rb +4 -0
  31. data/fastlane/lib/fastlane/actions/register_device.rb +4 -0
  32. data/fastlane/lib/fastlane/actions/register_devices.rb +1 -0
  33. data/fastlane/lib/fastlane/actions/rocket.rb +4 -0
  34. data/fastlane/lib/fastlane/actions/set_github_release.rb +4 -0
  35. data/fastlane/lib/fastlane/actions/sh.rb +5 -0
  36. data/fastlane/lib/fastlane/actions/slather.rb +14 -0
  37. data/fastlane/lib/fastlane/actions/update_urban_airship_configuration.rb +1 -0
  38. data/fastlane/lib/fastlane/actions/xcode_install.rb +4 -0
  39. data/fastlane/lib/fastlane/actions/xcode_server_get_assets.rb +4 -0
  40. data/fastlane/lib/fastlane/actions/zip.rb +4 -0
  41. data/fastlane/lib/fastlane/boolean.rb +5 -0
  42. data/fastlane/lib/fastlane/command_line_handler.rb +6 -1
  43. data/fastlane/lib/fastlane/commands_generator.rb +40 -1
  44. data/fastlane/lib/fastlane/fast_file.rb +5 -1
  45. data/fastlane/lib/fastlane/lane_list.rb +67 -4
  46. data/fastlane/lib/fastlane/lane_manager.rb +3 -69
  47. data/fastlane/lib/fastlane/lane_manager_base.rb +124 -0
  48. data/fastlane/lib/fastlane/runner.rb +2 -2
  49. data/fastlane/lib/fastlane/server/command.rb +70 -0
  50. data/fastlane/lib/fastlane/server/command_executor.rb +9 -0
  51. data/fastlane/lib/fastlane/server/socket_server.rb +212 -0
  52. data/fastlane/lib/fastlane/server/socket_server_action_command_executor.rb +100 -0
  53. data/fastlane/lib/fastlane/setup/setup.rb +7 -2
  54. data/fastlane/lib/fastlane/setup/setup_ios.rb +72 -15
  55. data/fastlane/lib/fastlane/swift_fastlane_api_generator.rb +265 -0
  56. data/fastlane/lib/fastlane/swift_fastlane_function.rb +342 -0
  57. data/fastlane/lib/fastlane/swift_lane_manager.rb +246 -0
  58. data/fastlane/lib/fastlane/tools.rb +13 -0
  59. data/fastlane/lib/fastlane/version.rb +1 -1
  60. data/fastlane_core/lib/fastlane_core.rb +3 -0
  61. data/fastlane_core/lib/fastlane_core/analytics/action_launch_context.rb +6 -3
  62. data/fastlane_core/lib/fastlane_core/analytics/analytics_session.rb +7 -0
  63. data/fastlane_core/lib/fastlane_core/configuration/commander_generator.rb +13 -0
  64. data/fastlane_core/lib/fastlane_core/configuration/config_item.rb +33 -2
  65. data/fastlane_core/lib/fastlane_core/fastlane_folder.rb +32 -0
  66. data/gym/lib/assets/GymfileTemplate.swift +13 -0
  67. data/gym/lib/gym/commands_generator.rb +14 -2
  68. data/gym/lib/gym/generators/build_command_generator.rb +2 -2
  69. data/gym/lib/gym/options.rb +11 -0
  70. data/match/lib/assets/MatchfileTemplate.swift +9 -0
  71. data/match/lib/match/commands_generator.rb +9 -3
  72. data/match/lib/match/options.rb +1 -0
  73. data/match/lib/match/setup.rb +6 -2
  74. data/precheck/lib/.DS_Store +0 -0
  75. data/precheck/lib/assets/PrecheckfileTemplate +1 -1
  76. data/precheck/lib/assets/PrecheckfileTemplate.swift +19 -0
  77. data/precheck/lib/precheck/commands_generator.rb +13 -2
  78. data/scan/lib/assets/ScanfileTemplate.swift +13 -0
  79. data/scan/lib/scan/commands_generator.rb +14 -2
  80. data/scan/lib/scan/options.rb +6 -0
  81. data/screengrab/lib/assets/ScreengrabfileTemplate.swift +15 -0
  82. data/screengrab/lib/screengrab/commands_generator.rb +2 -1
  83. data/screengrab/lib/screengrab/setup.rb +9 -3
  84. data/snapshot/lib/assets/SnapfileTemplate.swift +41 -0
  85. data/snapshot/lib/snapshot/commands_generator.rb +2 -1
  86. data/snapshot/lib/snapshot/setup.rb +9 -3
  87. data/spaceship/lib/spaceship/client.rb +4 -1
  88. metadata +38 -16
@@ -0,0 +1,342 @@
1
+ module Fastlane
2
+ class SwiftFunction
3
+ attr_accessor :function_name
4
+ attr_accessor :return_type
5
+ attr_accessor :param_names
6
+ attr_accessor :param_descriptions
7
+ attr_accessor :param_default_values
8
+ attr_accessor :param_optionality_values
9
+ attr_accessor :param_type_overrides
10
+ attr_accessor :reserved_words
11
+ attr_accessor :default_values_to_ignore
12
+
13
+ def initialize(action_name: nil, keys: nil, key_descriptions: nil, key_default_values: nil, key_optionality_values: nil, key_type_overrides: nil, return_type: nil)
14
+ @function_name = action_name
15
+ @param_names = keys
16
+ @param_descriptions = key_descriptions
17
+ @param_default_values = key_default_values
18
+ @param_optionality_values = key_optionality_values
19
+ @return_type = return_type
20
+ @param_type_overrides = key_type_overrides
21
+
22
+ # rubocop:disable LineLength
23
+ # class instance?
24
+ @reserved_words = %w[associativity break case catch class continue convenience default deinit didSet do else enum extension fallthrough false final for func get guard if in infix init inout internal lazy let mutating nil operator override postfix precedence prefix private public repeat required return self set static struct subscript super switch throws true try var weak where while willSet].to_set
25
+ # rubocop:enable LineLength
26
+
27
+ @default_values_to_ignore = {
28
+ "cert" => ["keychain_path"].to_set,
29
+ "set_github_release" => ["api_token"].to_set,
30
+ "github_api" => ["api_token"].to_set,
31
+ "create_pull_request" => ["api_token", "head", "api_url"].to_set,
32
+ "commit_github_file" => ["api_token"].to_set,
33
+ "verify_xcode" => ["xcode_path"].to_set,
34
+ "produce" => ["sku"].to_set,
35
+ "create_app_online" => ["sku"].to_set,
36
+ "screengrab" => ["android_home"].to_set
37
+ }
38
+ end
39
+
40
+ def ignore_default_value?(function_name: nil, param_name: nil)
41
+ action_set = @default_values_to_ignore[function_name]
42
+ unless action_set
43
+ return false
44
+ end
45
+
46
+ return action_set.include?(param_name)
47
+ end
48
+
49
+ def sanitize_reserved_word(word: nil)
50
+ unless @reserved_words.include?(word)
51
+ return word
52
+ end
53
+ return "`#{word}`"
54
+ end
55
+
56
+ def return_declaration
57
+ expected_type = swift_type_for_return_type
58
+ unless expected_type.to_s.length > 0
59
+ return ""
60
+ end
61
+
62
+ return " -> #{expected_type}"
63
+ end
64
+
65
+ def swift_type_for_return_type
66
+ unless @return_type
67
+ return ""
68
+ end
69
+
70
+ case @return_type
71
+ when :string
72
+ return "String"
73
+ when :array_of_strings
74
+ return "[String]"
75
+ when :hash_of_strings
76
+ return "[String : String]"
77
+ when :bool
78
+ return "Bool"
79
+ when :int
80
+ return "Int"
81
+ else
82
+ return ""
83
+ end
84
+ end
85
+
86
+ def camel_case_lower(string: nil)
87
+ string.split('_').inject([]) { |buffer, e| buffer.push(buffer.empty? ? e : e.capitalize) }.join
88
+ end
89
+
90
+ def determine_type_from_override(type_override: nil, default_type: nil)
91
+ if type_override == Array
92
+ return "[String]"
93
+ elsif type_override == Hash
94
+ return "[String : Any]"
95
+ elsif type_override == Integer
96
+ return "Int"
97
+ elsif type_override == Boolean
98
+ return "Bool"
99
+ elsif type_override == :string_callback
100
+ return "((String) -> Void)"
101
+ else
102
+ return default_type
103
+ end
104
+ end
105
+
106
+ def override_default_value_if_not_correct_type(param_name: nil, param_type: nil, default_value: nil)
107
+ default_value = nil if ignore_default_value?(function_name: @function_name, param_name: param_name)
108
+
109
+ return "[]" if param_type == "[String]" && default_value == ""
110
+ return "{_ in }" if param_type == "((String) -> Void)"
111
+
112
+ return default_value
113
+ end
114
+
115
+ def get_type(param: nil, default_value: nil, optional: nil, param_type_override: nil)
116
+ unless param_type_override.nil?
117
+ type = determine_type_from_override(type_override: param_type_override)
118
+ end
119
+ type ||= "String"
120
+
121
+ optional_specifier = ""
122
+ # if we are optional and don't have a default value, we'll need to use ?
123
+ optional_specifier = "?" if (optional && default_value.nil?) && type != "((String) -> Void)"
124
+
125
+ if optional && ignore_default_value?(function_name: @function_name, param_name: param)
126
+ optional_specifier = "?"
127
+ end
128
+
129
+ # If we have a default value of true or false, we can infer it is a Bool
130
+ if default_value.class == FalseClass
131
+ type = "Bool"
132
+ elsif default_value.class == TrueClass
133
+ type = "Bool"
134
+ elsif default_value.kind_of?(Array)
135
+ type = "[String]"
136
+ end
137
+ return "#{type}#{optional_specifier}"
138
+ end
139
+
140
+ def parameters
141
+ unless @param_names
142
+ return ""
143
+ end
144
+
145
+ param_names_and_types = @param_names.zip(param_default_values, param_optionality_values, param_type_overrides).map do |param, default_value, optional, param_type_override|
146
+ type = get_type(param: param, default_value: default_value, optional: optional, param_type_override: param_type_override)
147
+
148
+ unless default_value.nil?
149
+ if type == "[String : Any]"
150
+ # we can't handle default values for Hashes, yet
151
+ default_value = "[:]"
152
+ elsif type != "Bool" && type != "[String]" && type != "Int" && type != "((String) -> Void)"
153
+ default_value = "\"#{default_value}\""
154
+ end
155
+ end
156
+
157
+ # if we don't have a default value, but the param is optional, set a default value in Swift to be nil
158
+ if optional && default_value.nil?
159
+ default_value = "nil"
160
+ end
161
+
162
+ # sometimes we get to the point where we have a default value but its type is wrong
163
+ # so we need to correct that because [String] = "" is not valid swift
164
+ default_value = override_default_value_if_not_correct_type(param_type: type, param_name: param, default_value: default_value)
165
+
166
+ param = camel_case_lower(string: param)
167
+ param = sanitize_reserved_word(word: param)
168
+
169
+ if default_value.nil?
170
+ "#{param}: #{type}"
171
+ else
172
+ "#{param}: #{type} = #{default_value}"
173
+ end
174
+ end
175
+
176
+ return param_names_and_types
177
+ end
178
+ # rubocop:enable Metrics/PerceivedComplexity
179
+
180
+ def swift_code
181
+ function_name = camel_case_lower(string: self.function_name)
182
+ function_return_declaration = self.return_declaration
183
+ discardable_result = function_return_declaration.length > 0 ? "@discardableResult " : ''
184
+
185
+ # Calculate the necessary indent to line up parameter names on new lines
186
+ # with the first parameter after the opening paren following the function name.
187
+ # i.e.: @discardableResult func someFunctionName(firstParameter: T
188
+ # secondParameter: T)
189
+ # This just creates a string with as many spaces are necessary given whether or not
190
+ # the function has a 'discardableResult' annotation, the 'func' keyword, function name
191
+ # and the opening paren.
192
+ function_keyword_definition = 'func '
193
+ open_paren = '('
194
+ closed_paren = ')'
195
+ indent = ' ' * (discardable_result.length + function_name.length + function_keyword_definition.length + open_paren.length)
196
+ params = self.parameters.join(",\n#{indent}")
197
+
198
+ return "#{discardable_result}#{function_keyword_definition}#{function_name}#{open_paren}#{params}#{closed_paren}#{function_return_declaration} {\n#{self.implementation}\n}"
199
+ end
200
+
201
+ def build_argument_list
202
+ unless @param_names
203
+ return "[]" # return empty list for argument
204
+ end
205
+
206
+ argument_object_strings = @param_names.zip(param_type_overrides).map do |name, type_override|
207
+ sanitized_name = camel_case_lower(string: name)
208
+ sanitized_name = sanitize_reserved_word(word: sanitized_name)
209
+ type_string = type_override == :string_callback ? ", type: .stringClosure" : nil
210
+
211
+ "RubyCommand.Argument(name: \"#{name}\", value: #{sanitized_name}#{type_string})"
212
+ end
213
+ return argument_object_strings
214
+ end
215
+
216
+ def return_statement
217
+ returned_object = "runner.executeCommand(command)"
218
+ case @return_type
219
+ when :array_of_strings
220
+ returned_object = "parseArray(fromString: #{returned_object})"
221
+ when :hash_of_strings
222
+ returned_object = "parseDictionary(fromString: #{returned_object})"
223
+ when :bool
224
+ returned_object = "parseBool(fromString: #{returned_object})"
225
+ when :int
226
+ returned_object = "parseInt(fromString: #{returned_object})"
227
+ end
228
+
229
+ expected_type = swift_type_for_return_type
230
+
231
+ return_string = "_ = "
232
+ if expected_type.length > 0
233
+ return_string = "return "
234
+ end
235
+ return "#{return_string}#{returned_object}"
236
+ end
237
+
238
+ def implementation
239
+ args = build_argument_list
240
+
241
+ implm = " let command = RubyCommand(commandID: \"\", methodName: \"#{@function_name}\", className: nil, args: ["
242
+ # Get the indent of the first argument in the list to give each
243
+ # subsequent argument it's own line with proper indenting
244
+ indent = ' ' * implm.length
245
+ implm += args.join(",\n#{indent}")
246
+ implm += "])\n"
247
+ return implm + " #{return_statement}"
248
+ end
249
+ end
250
+
251
+ class ToolSwiftFunction < SwiftFunction
252
+ def protocol_name
253
+ function_name = camel_case_lower(string: self.function_name)
254
+ return function_name.capitalize + "fileProtocol"
255
+ end
256
+
257
+ def class_name
258
+ function_name = camel_case_lower(string: self.function_name)
259
+ return function_name.capitalize + "file"
260
+ end
261
+
262
+ def swift_vars
263
+ unless @param_names
264
+ return []
265
+ end
266
+ swift_vars = @param_names.zip(param_default_values, param_optionality_values, param_type_overrides).map do |param, default_value, optional, param_type_override|
267
+ type = get_type(param: param, default_value: default_value, optional: optional, param_type_override: param_type_override)
268
+
269
+ param = camel_case_lower(string: param)
270
+ param = sanitize_reserved_word(word: param)
271
+ static_var_for_parameter_name = param
272
+ " var #{static_var_for_parameter_name}: #{type} { get }"
273
+ end
274
+
275
+ return swift_vars
276
+ end
277
+
278
+ def swift_default_implementations
279
+ unless @param_names
280
+ return []
281
+ end
282
+
283
+ swift_implementations = @param_names.zip(param_default_values, param_optionality_values, param_type_overrides).map do |param, default_value, optional, param_type_override|
284
+ type = get_type(param: param, default_value: default_value, optional: optional, param_type_override: param_type_override)
285
+
286
+ default_value = nil if ignore_default_value?(function_name: @function_name, param_name: param)
287
+
288
+ param = camel_case_lower(string: param)
289
+ param = sanitize_reserved_word(word: param)
290
+ var_for_parameter_name = param
291
+
292
+ unless default_value.nil?
293
+ if type == "Bool" || type == "[String]" || type == "Int" || default_value.kind_of?(Array)
294
+ default_value = default_value.to_s
295
+ else
296
+ default_value = "\"#{default_value}\""
297
+ end
298
+ end
299
+
300
+ # if we don't have a default value, but the param is options, just set a default value to nil
301
+ if optional && default_value.nil?
302
+ default_value = "nil"
303
+ end
304
+
305
+ # if we don't have a default value still, we need to assign them based on type
306
+ if type == "String"
307
+ default_value ||= "\"\""
308
+ end
309
+
310
+ if type == "Bool"
311
+ default_value ||= "false"
312
+ end
313
+
314
+ if type == "[String]"
315
+ default_value ||= "[]"
316
+ end
317
+
318
+ " var #{var_for_parameter_name}: #{type} { return #{default_value} }"
319
+ end
320
+
321
+ return swift_implementations
322
+ end
323
+
324
+ def parameters
325
+ unless @param_names
326
+ return ""
327
+ end
328
+
329
+ param_names_and_types = @param_names.zip(param_default_values, param_optionality_values, param_type_overrides).map do |param, default_value, optional, param_type_override|
330
+ type = get_type(param: param, default_value: default_value, optional: optional, param_type_override: param_type_override)
331
+
332
+ param = camel_case_lower(string: param)
333
+ param = sanitize_reserved_word(word: param)
334
+ static_var_for_parameter_name = param
335
+
336
+ "#{param}: #{type} = #{self.class_name.downcase}.#{static_var_for_parameter_name}"
337
+ end
338
+
339
+ return param_names_and_types
340
+ end
341
+ end
342
+ end
@@ -0,0 +1,246 @@
1
+ require_relative 'lane_manager_base.rb'
2
+
3
+ module Fastlane
4
+ class SwiftLaneManager < LaneManagerBase
5
+ # @param lane_name The name of the lane to execute
6
+ # @param parameters [Hash] The parameters passed from the command line to the lane
7
+ # @param env Dot Env Information
8
+ def self.cruise_lane(lane, parameters = nil, env = nil)
9
+ UI.user_error!("lane must be a string") unless lane.kind_of?(String) or lane.nil?
10
+ UI.user_error!("parameters must be a hash") unless parameters.kind_of?(Hash) or parameters.nil?
11
+
12
+ # xcodeproj has a bug in certain versions that causes it to change directories
13
+ # and not return to the original working directory
14
+ # https://github.com/CocoaPods/Xcodeproj/issues/426
15
+ # Setting this environment variable causes xcodeproj to work around the problem
16
+ ENV["FORK_XCODE_WRITING"] = "true"
17
+
18
+ FastlaneCore.session.is_fastfile = true
19
+
20
+ load_dot_env(env)
21
+
22
+ started = Time.now
23
+ e = nil
24
+ begin
25
+ self.ensure_runner_built!
26
+ socket_thread = self.start_socket_thread
27
+ sleep(0.250) while socket_thread[:ready].nil?
28
+ # wait on socket_thread to be in ready state, then start the runner thread
29
+ runner_thread = self.cruise_swift_lane_in_thread(lane, parameters)
30
+
31
+ runner_thread.join
32
+ socket_thread.join
33
+ rescue Exception => ex # rubocop:disable Lint/RescueException
34
+ # We also catch Exception, since the implemented action might send a SystemExit signal
35
+ # (or similar). We still want to catch that, since we want properly finish running fastlane
36
+ # Tested with `xcake`, which throws a `Xcake::Informative` object
37
+
38
+ print_lane_context
39
+ UI.error ex.to_s if ex.kind_of?(StandardError) # we don't want to print things like 'system exit'
40
+ e = ex
41
+ end
42
+
43
+ duration = ((Time.now - started) / 60.0).round
44
+
45
+ finish_fastlane(nil, duration, e)
46
+ end
47
+
48
+ def self.display_lanes
49
+ self.ensure_runner_built!
50
+ Actions.sh(%(#{FastlaneCore::FastlaneFolder.swift_runner_path} lanes))
51
+ end
52
+
53
+ def self.cruise_swift_lane_in_thread(lane, parameters = nil)
54
+ if parameters.nil?
55
+ parameters = {}
56
+ end
57
+
58
+ parameter_string = ""
59
+ parameters.each do |key, value|
60
+ parameter_string += " #{key} #{value}"
61
+ end
62
+
63
+ if FastlaneCore::Globals.verbose?
64
+ parameter_string += " logMode verbose"
65
+ end
66
+
67
+ return Thread.new do
68
+ Actions.sh(%(#{FastlaneCore::FastlaneFolder.swift_runner_path} lane #{lane}#{parameter_string} > /dev/null))
69
+ end
70
+ end
71
+
72
+ def self.swap_paths_in_target(target: nil, file_refs_to_swap: nil, expected_path_to_replacement_path_tuples: nil)
73
+ made_project_updates = false
74
+ file_refs_to_swap.each do |file_ref|
75
+ expected_path_to_replacement_path_tuples.each do |preinstalled_config_relative_path, user_config_relative_path|
76
+ next unless file_ref.path == preinstalled_config_relative_path
77
+
78
+ file_ref.path = user_config_relative_path
79
+ made_project_updates = true
80
+ end
81
+ end
82
+ return made_project_updates
83
+ end
84
+
85
+ # Find all the config files we care about (Deliverfile, Gymfile, etc), and build tuples of what file we'll look for
86
+ # in the Xcode project, and what file paths we'll need to swap (since we have to inject the user's configs)
87
+ #
88
+ # Return a mapping of what file paths we're looking => new file pathes we'll need to inject
89
+ def self.collect_tool_paths_for_replacement(all_user_tool_file_paths: nil, look_for_new_configs: nil)
90
+ new_user_tool_file_paths = all_user_tool_file_paths.select do |user_config, preinstalled_config_relative_path, user_config_relative_path|
91
+ if look_for_new_configs
92
+ File.exist?(user_config)
93
+ else
94
+ !File.exist?(user_config)
95
+ end
96
+ end
97
+
98
+ # Now strip out the fastlane-relative path and leave us with xcodeproj relative paths
99
+ new_user_tool_file_paths = new_user_tool_file_paths.map do |user_config, preinstalled_config_relative_path, user_config_relative_path|
100
+ if look_for_new_configs
101
+ [preinstalled_config_relative_path, user_config_relative_path]
102
+ else
103
+ [user_config_relative_path, preinstalled_config_relative_path]
104
+ end
105
+ end
106
+ return new_user_tool_file_paths
107
+ end
108
+
109
+ # open and return the swift project
110
+ def self.runner_project
111
+ runner_project_path = FastlaneCore::FastlaneFolder.swift_runner_project_path
112
+ require 'xcodeproj'
113
+ project = Xcodeproj::Project.open(runner_project_path)
114
+ return project
115
+ end
116
+
117
+ # return the FastlaneRunner build target
118
+ def self.target_for_fastlane_runner_project(runner_project: nil)
119
+ fastlane_runner_array = runner_project.targets.select do |target|
120
+ target.name == "FastlaneRunner"
121
+ end
122
+
123
+ # get runner target
124
+ runner_target = fastlane_runner_array.first
125
+ return runner_target
126
+ end
127
+
128
+ def self.target_source_file_refs(target: nil)
129
+ return target.source_build_phase.files.to_a.map(&:file_ref)
130
+ end
131
+
132
+ def self.first_time_setup
133
+ setup_message = ["fastlane is now configured to use a swift-based Fastfile (Fastfile.swift) 🦅"]
134
+ setup_message << "To edit your new Fastfile.swift, type: `open #{FastlaneCore::FastlaneFolder.swift_runner_project_path}`"
135
+
136
+ # Go through and link up whatever we generated during `fastlane init swift` so the user can edit them easily
137
+ self.link_user_configs_to_project(updated_message: setup_message.join("\n"))
138
+ end
139
+
140
+ def self.link_user_configs_to_project(updated_message: nil)
141
+ tool_files_folder = FastlaneCore::FastlaneFolder.path
142
+
143
+ # All the tools that could have <tool name>file.swift their paths, and where we expect to find the user's tool files.
144
+ all_user_tool_file_paths = TOOL_CONFIG_FILES.map do |tool_name|
145
+ [
146
+ File.join(tool_files_folder, "#{tool_name}.swift"),
147
+ "../#{tool_name}.swift",
148
+ "../../#{tool_name}.swift"
149
+ ]
150
+ end
151
+
152
+ # Tool files the user now provides
153
+ new_user_tool_file_paths = collect_tool_paths_for_replacement(all_user_tool_file_paths: all_user_tool_file_paths, look_for_new_configs: true)
154
+
155
+ # Tool files we provide AND the user doesn't provide
156
+ user_tool_files_possibly_removed = collect_tool_paths_for_replacement(all_user_tool_file_paths: all_user_tool_file_paths, look_for_new_configs: false)
157
+
158
+ fastlane_runner_project = self.runner_project
159
+ runner_target = target_for_fastlane_runner_project(runner_project: fastlane_runner_project)
160
+ target_file_refs = target_source_file_refs(target: runner_target)
161
+
162
+ # Swap in all new user supplied configs into the project
163
+ project_modified = swap_paths_in_target(
164
+ target: runner_target,
165
+ file_refs_to_swap: target_file_refs,
166
+ expected_path_to_replacement_path_tuples: new_user_tool_file_paths
167
+ )
168
+
169
+ # Swap out any configs the user has removed, inserting fastlane defaults
170
+ project_modified ||= swap_paths_in_target(
171
+ target: runner_target,
172
+ file_refs_to_swap: target_file_refs,
173
+ expected_path_to_replacement_path_tuples: user_tool_files_possibly_removed
174
+ )
175
+
176
+ if project_modified
177
+ fastlane_runner_project.save
178
+ updated_message ||= "Updated #{FastlaneCore::FastlaneFolder.swift_runner_project_path}"
179
+ UI.success(updated_message)
180
+ else
181
+ UI.success("FastlaneSwiftRunner project is up-to-date")
182
+ end
183
+
184
+ return project_modified
185
+ end
186
+
187
+ def self.start_socket_thread
188
+ require 'fastlane/server/socket_server'
189
+ require 'fastlane/server/socket_server_action_command_executor'
190
+
191
+ return Thread.new do
192
+ command_executor = SocketServerActionCommandExecutor.new
193
+ server = Fastlane::SocketServer.new(command_executor: command_executor)
194
+ server.start
195
+ end
196
+ end
197
+
198
+ def self.ensure_runner_built!
199
+ UI.verbose("Checking for new user-provided tool configuration files")
200
+ # if self.link_user_configs_to_project returns true, that means we need to rebuild the runner
201
+ runner_needs_building = self.link_user_configs_to_project
202
+
203
+ if FastlaneCore::FastlaneFolder.swift_runner_built?
204
+ runner_last_modified_age = File.mtime(FastlaneCore::FastlaneFolder.swift_runner_path).to_i
205
+ fastfile_last_modified_age = File.mtime(FastlaneCore::FastlaneFolder.fastfile_path).to_i
206
+
207
+ if runner_last_modified_age < fastfile_last_modified_age
208
+ # It's older than the Fastfile, so build it again
209
+ UI.verbose("Found changes to user's Fastfile.swift, setting re-build runner flag")
210
+ runner_needs_building = true
211
+ end
212
+ else
213
+ # Runner isn't built yet, so build it
214
+ UI.verbose("No runner found, setting re-build runner flag")
215
+ runner_needs_building = true
216
+ end
217
+
218
+ if runner_needs_building
219
+ self.build_runner!
220
+ end
221
+ end
222
+
223
+ def self.build_runner!
224
+ UI.verbose("Building FastlaneSwiftRunner")
225
+ require 'fastlane_core'
226
+ require 'gym'
227
+ require 'gym/generators/build_command_generator'
228
+
229
+ project_options = {
230
+ project: FastlaneCore::FastlaneFolder.swift_runner_project_path,
231
+ skip_archive: true
232
+ }
233
+ Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, project_options)
234
+ build_command = Gym::BuildCommandGenerator.generate
235
+
236
+ FastlaneCore::CommandExecutor.execute(
237
+ command: build_command,
238
+ print_all: false,
239
+ print_command: !Gym.config[:silent],
240
+ error: proc do |output|
241
+ ErrorHandler.handle_build_error(output)
242
+ end
243
+ )
244
+ end
245
+ end
246
+ end