Class: Fastlane::SwiftLaneManager

Inherits:
LaneManagerBase show all
Defined in:
fastlane/lib/fastlane/swift_lane_manager.rb

Class Method Summary collapse

Methods inherited from LaneManagerBase

finish_fastlane, print_error_line, print_lane_context, print_table, skip_docs?

Class Method Details

.build_runner!Object


276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'fastlane/lib/fastlane/swift_lane_manager.rb', line 276

def self.build_runner!
  UI.verbose("Building FastlaneSwiftRunner")
  require 'fastlane_core'
  require 'gym'
  require 'gym/generators/build_command_generator'

  project_options = {
      project: FastlaneCore::FastlaneFolder.swift_runner_project_path,
      skip_archive: true
    }
  Gym.config = FastlaneCore::Configuration.create(Gym::Options.available_options, project_options)
  build_command = Gym::BuildCommandGenerator.generate

  FastlaneCore::CommandExecutor.execute(
    command: build_command,
    print_all: false,
    print_command: !Gym.config[:silent]
  )
end

.collect_tool_paths_for_replacement(all_user_tool_file_paths: nil, look_for_new_configs: nil) ⇒ Object

Find all the config files we care about (Deliverfile, Gymfile, etc), and build tuples of what file we'll look for in the Xcode project, and what file paths we'll need to swap (since we have to inject the user's configs)

Return a mapping of what file paths we're looking => new file pathes we'll need to inject


118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'fastlane/lib/fastlane/swift_lane_manager.rb', line 118

def self.collect_tool_paths_for_replacement(all_user_tool_file_paths: nil, look_for_new_configs: nil)
  new_user_tool_file_paths = all_user_tool_file_paths.select do |user_config, preinstalled_config_relative_path, user_config_relative_path|
    if look_for_new_configs
      File.exist?(user_config)
    else
      !File.exist?(user_config)
    end
  end

  # Now strip out the fastlane-relative path and leave us with xcodeproj relative paths
  new_user_tool_file_paths = new_user_tool_file_paths.map do |user_config, preinstalled_config_relative_path, user_config_relative_path|
    if look_for_new_configs
      [preinstalled_config_relative_path, user_config_relative_path]
    else
      [user_config_relative_path, preinstalled_config_relative_path]
    end
  end
  return new_user_tool_file_paths
end

.cruise_lane(lane, parameters = nil, env = nil, disable_runner_upgrades: false, swift_server_port: nil) ⇒ Object

Parameters:

  • lane_name

    The name of the lane to execute

  • parameters (Hash) (defaults to: nil)

    The parameters passed from the command line to the lane

  • env (defaults to: nil)

    Dot Env Information


9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'fastlane/lib/fastlane/swift_lane_manager.rb', line 9

def self.cruise_lane(lane, parameters = nil, env = nil, disable_runner_upgrades: false, swift_server_port: nil)
  UI.user_error!("lane must be a string") unless lane.kind_of?(String) || lane.nil?
  UI.user_error!("parameters must be a hash") unless parameters.kind_of?(Hash) || parameters.nil?

  # Sets environment variable and lane context for lane name
  ENV["FASTLANE_LANE_NAME"] = lane
  Actions.lane_context[Actions::SharedValues::LANE_NAME] = lane

  started = Time.now
  e = nil
  begin
    display_upgraded_message = false
    if disable_runner_upgrades
      UI.verbose("disable_runner_upgrades is true, not attempting to update the FastlaneRunner project".yellow)
    elsif Helper.ci?
      UI.verbose("Running in CI, not attempting to update the FastlaneRunner project".yellow)
    else
      display_upgraded_message = self.ensure_runner_up_to_date_fastlane!
    end

    self.ensure_runner_built!
    swift_server_port ||= 2000
    socket_thread = self.start_socket_thread(port: swift_server_port)
    sleep(0.250) while socket_thread[:ready].nil?
    # wait on socket_thread to be in ready state, then start the runner thread
    self.cruise_swift_lane_in_thread(lane, parameters, swift_server_port)

    socket_thread.join
  rescue Exception => ex # rubocop:disable Lint/RescueException
    e = ex
  end
  # If we have a thread exception, drop that in the exception
  # won't ever have a situation where e is non-nil, and socket_thread[:exception] is also non-nil
  e ||= socket_thread[:exception]

  unless e.nil?
    print_lane_context

    # We also catch Exception, since the implemented action might send a SystemExit signal
    # (or similar). We still want to catch that, since we want properly finish running fastlane
    # Tested with `xcake`, which throws a `Xcake::Informative` object
    UI.error(e.to_s) if e.kind_of?(StandardError) # we don't want to print things like 'system exit'
  end

  skip_message = false

  # if socket_thread is nil, we were probably debugging, or something else weird happened
  exit_reason = :cancelled if socket_thread.nil?

  # normal exit means we have a reason
  exit_reason ||= socket_thread[:exit_reason]

  if exit_reason == :cancelled && e.nil?
    skip_message = true
  end

  duration = ((Time.now - started) / 60.0).round

  finish_fastlane(nil, duration, e, skip_message: skip_message)

  if display_upgraded_message
    UI.message("We updated your FastlaneRunner project during this run to make it compatible with your current version of fastlane.".yellow)
    UI.message("Please make sure to check the changes into source control.".yellow)
  end
end

.cruise_swift_lane_in_thread(lane, parameters = nil, swift_server_port) ⇒ Object


80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'fastlane/lib/fastlane/swift_lane_manager.rb', line 80

def self.cruise_swift_lane_in_thread(lane, parameters = nil, swift_server_port)
  if parameters.nil?
    parameters = {}
  end

  parameter_string = ""
  parameters.each do |key, value|
    parameter_string += " #{key} #{value}"
  end

  if FastlaneCore::Globals.verbose?
    parameter_string += " logMode verbose"
  end

  parameter_string += " swiftServerPort #{swift_server_port}"

  return Thread.new do
    Actions.sh(%(#{FastlaneCore::FastlaneFolder.swift_runner_path} lane #{lane}#{parameter_string} > /dev/null))
  end
end

.display_lanesObject


75
76
77
78
# File 'fastlane/lib/fastlane/swift_lane_manager.rb', line 75

def self.display_lanes
  self.ensure_runner_built!
  Actions.sh(%(#{FastlaneCore::FastlaneFolder.swift_runner_path} lanes))
end

.ensure_runner_built!Object


227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'fastlane/lib/fastlane/swift_lane_manager.rb', line 227

def self.ensure_runner_built!
  UI.verbose("Checking for new user-provided tool configuration files")
  # if self.link_user_configs_to_project returns true, that means we need to rebuild the runner
  runner_needs_building = self.link_user_configs_to_project

  if FastlaneCore::FastlaneFolder.swift_runner_built?
    runner_last_modified_age = File.mtime(FastlaneCore::FastlaneFolder.swift_runner_path).to_i
    fastfile_last_modified_age = File.mtime(FastlaneCore::FastlaneFolder.fastfile_path).to_i

    if runner_last_modified_age < fastfile_last_modified_age
      # It's older than the Fastfile, so build it again
      UI.verbose("Found changes to user's Fastfile.swift, setting re-build runner flag")
      runner_needs_building = true
    end
  else
    # Runner isn't built yet, so build it
    UI.verbose("No runner found, setting re-build runner flag")
    runner_needs_building = true
  end

  if runner_needs_building
    self.build_runner!
  end
end

.ensure_runner_up_to_date_fastlane!Object

do we have the latest FastlaneSwiftRunner code from the current version of fastlane?


253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'fastlane/lib/fastlane/swift_lane_manager.rb', line 253

def self.ensure_runner_up_to_date_fastlane!
  upgraded = false
  upgrader = SwiftRunnerUpgrader.new

  upgrade_needed = upgrader.upgrade_if_needed!(dry_run: true)
  if upgrade_needed
    UI.message("It looks like your `FastlaneSwiftRunner` project is not up-to-date".green)
    UI.message("If you don't update it, fastlane could fail".green)
    UI.message("We can try to automatically update it for you, usually this works 🎈 🐐".green)
    user_wants_upgrade = UI.confirm("Should we try to upgrade just your `FastlaneSwiftRunner` project?")

    UI.important("Ok, if things break, you can try to run this lane again and you'll be prompted to upgrade another time") unless user_wants_upgrade

    if user_wants_upgrade
      upgraded = upgrader.upgrade_if_needed!
      UI.success("Updated your FastlaneSwiftRunner project with the newest runner code") if upgraded
      self.build_runner! if upgraded
    end
  end

  return upgraded
end

.first_time_setupObject


161
162
163
164
165
166
167
# File 'fastlane/lib/fastlane/swift_lane_manager.rb', line 161

def self.first_time_setup
  setup_message = ["fastlane is now configured to use a swift-based Fastfile (Fastfile.swift) 🦅"]
  setup_message << "To edit your new Fastfile.swift, type: `open #{FastlaneCore::FastlaneFolder.swift_runner_project_path}`"

  # Go through and link up whatever we generated during `fastlane init swift` so the user can edit them easily
  self.link_user_configs_to_project(updated_message: setup_message.join("\n"))
end

169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'fastlane/lib/fastlane/swift_lane_manager.rb', line 169

def self.link_user_configs_to_project(updated_message: nil)
  tool_files_folder = FastlaneCore::FastlaneFolder.path

  # All the tools that could have <tool name>file.swift their paths, and where we expect to find the user's tool files.
  all_user_tool_file_paths = TOOL_CONFIG_FILES.map do |tool_name|
    [
      File.join(tool_files_folder, "#{tool_name}.swift"),
      "../#{tool_name}.swift",
      "../../#{tool_name}.swift"
    ]
  end

  # Tool files the user now provides
  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)

  # Tool files we provide AND the user doesn't provide
  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)

  fastlane_runner_project = self.runner_project
  runner_target = target_for_fastlane_runner_project(runner_project: fastlane_runner_project)
  target_file_refs = target_source_file_refs(target: runner_target)

  # Swap in all new user supplied configs into the project
  project_modified = swap_paths_in_target(
    target: runner_target,
    file_refs_to_swap: target_file_refs,
    expected_path_to_replacement_path_tuples: new_user_tool_file_paths
  )

  # Swap out any configs the user has removed, inserting fastlane defaults
  project_modified = swap_paths_in_target(
    target: runner_target,
    file_refs_to_swap: target_file_refs,
    expected_path_to_replacement_path_tuples: user_tool_files_possibly_removed
  ) || project_modified

  if project_modified
    fastlane_runner_project.save
    updated_message ||= "Updated #{FastlaneCore::FastlaneFolder.swift_runner_project_path}"
    UI.success(updated_message)
  else
    UI.success("FastlaneSwiftRunner project is up-to-date")
  end

  return project_modified
end

.runner_projectObject

open and return the swift project


139
140
141
142
143
144
# File 'fastlane/lib/fastlane/swift_lane_manager.rb', line 139

def self.runner_project
  runner_project_path = FastlaneCore::FastlaneFolder.swift_runner_project_path
  require 'xcodeproj'
  project = Xcodeproj::Project.open(runner_project_path)
  return project
end

.start_socket_thread(port: nil) ⇒ Object


216
217
218
219
220
221
222
223
224
225
# File 'fastlane/lib/fastlane/swift_lane_manager.rb', line 216

def self.start_socket_thread(port: nil)
  require 'fastlane/server/socket_server'
  require 'fastlane/server/socket_server_action_command_executor'

  return Thread.new do
    command_executor = SocketServerActionCommandExecutor.new
    server = Fastlane::SocketServer.new(command_executor: command_executor, port: port)
    server.start
  end
end

.swap_paths_in_target(target: nil, file_refs_to_swap: nil, expected_path_to_replacement_path_tuples: nil) ⇒ Object


101
102
103
104
105
106
107
108
109
110
111
112
# File 'fastlane/lib/fastlane/swift_lane_manager.rb', line 101

def self.swap_paths_in_target(target: nil, file_refs_to_swap: nil, expected_path_to_replacement_path_tuples: nil)
  made_project_updates = false
  file_refs_to_swap.each do |file_ref|
    expected_path_to_replacement_path_tuples.each do |preinstalled_config_relative_path, user_config_relative_path|
      next unless file_ref.path == preinstalled_config_relative_path

      file_ref.path = user_config_relative_path
      made_project_updates = true
    end
  end
  return made_project_updates
end

.target_for_fastlane_runner_project(runner_project: nil) ⇒ Object

return the FastlaneRunner build target


147
148
149
150
151
152
153
154
155
# File 'fastlane/lib/fastlane/swift_lane_manager.rb', line 147

def self.target_for_fastlane_runner_project(runner_project: nil)
  fastlane_runner_array = runner_project.targets.select do |target|
    target.name == "FastlaneRunner"
  end

  # get runner target
  runner_target = fastlane_runner_array.first
  return runner_target
end

.target_source_file_refs(target: nil) ⇒ Object


157
158
159
# File 'fastlane/lib/fastlane/swift_lane_manager.rb', line 157

def self.target_source_file_refs(target: nil)
  return target.source_build_phase.files.to_a.map(&:file_ref)
end