Class: Fastlane::SwiftRunnerUpgrader

Inherits:
Object
  • Object
show all
Defined in:
fastlane/lib/fastlane/swift_runner_upgrader.rb

Overview

build project

Constant Summary collapse

API_VERSION_REGEX =

also used by SwiftFastlaneAPIGenerator

/FastlaneRunnerAPIVersion\s*\[\s*([0-9]+.[0-9]+.[0-9]+)\s*\]/
RELATIVE_SOURCE_FILE_PATH =
"../"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeSwiftRunnerUpgrader

Returns a new instance of SwiftRunnerUpgrader.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'fastlane/lib/fastlane/swift_runner_upgrader.rb', line 28

def initialize
  @source_swift_code_file_folder_path = File.expand_path(File.join(Fastlane::ROOT, "/swift"))
  @target_swift_code_file_folder_path = FastlaneCore::FastlaneFolder.swift_folder_path

  Fastlane::Setup.setup_swift_support

  manifest_file = File.join(@source_swift_code_file_folder_path, "/upgrade_manifest.json")
  UI.success("loading manifest: #{manifest_file}")
  @manifest_hash = JSON.parse(File.read(manifest_file))
  @manifest_groups = @manifest_hash.values.uniq

  runner_project_path = FastlaneCore::FastlaneFolder.swift_runner_project_path
  @target_project = Xcodeproj::Project.open(runner_project_path)

  @root_group = @target_project.groups.select { |group| group.name == "Fastlane Runner" }.first

  @fastlane_runner_target = @target_project.targets.select { |target| target.name == "FastlaneRunner" }.first
end

Instance Attribute Details

#fastlane_runner_targetObject

FastlaneRunner xcodeproj target



20
21
22
# File 'fastlane/lib/fastlane/swift_runner_upgrader.rb', line 20

def fastlane_runner_target
  @fastlane_runner_target
end

#manifest_groupsObject

unique list of group names that came from the manifest



22
23
24
# File 'fastlane/lib/fastlane/swift_runner_upgrader.rb', line 22

def manifest_groups
  @manifest_groups
end

#manifest_hashObject

hash of file names to group names they belong to



21
22
23
# File 'fastlane/lib/fastlane/swift_runner_upgrader.rb', line 21

def manifest_hash
  @manifest_hash
end

#source_swift_code_file_folder_pathObject

source location of where we’re copying file from during the upgrade process



24
25
26
# File 'fastlane/lib/fastlane/swift_runner_upgrader.rb', line 24

def source_swift_code_file_folder_path
  @source_swift_code_file_folder_path
end

#target_projectObject

project we’ll be updating



19
20
21
# File 'fastlane/lib/fastlane/swift_runner_upgrader.rb', line 19

def target_project
  @target_project
end

#target_swift_code_file_folder_pathObject

location in filesystem where all swift files should exist when we’re done



23
24
25
# File 'fastlane/lib/fastlane/swift_runner_upgrader.rb', line 23

def target_swift_code_file_folder_path
  @target_swift_code_file_folder_path
end

Instance Method Details

#add_missing_copy_phase!(dry_run: false) ⇒ Object

adds new copy files build phase to fastlane_runner_target



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'fastlane/lib/fastlane/swift_runner_upgrader.rb', line 233

def add_missing_copy_phase!(dry_run: false)
  # Check if upgrade is needed

  # Check if Copy Files build phase contains FastlaneRunner target.
  phase_copy_sign = self.fastlane_runner_target.copy_files_build_phases.map(&:files).flatten.select { |file| file.display_name == "FastlaneRunner" }.first

  # If fastlane copy files build phase exists already, we don't need any more changes to the Xcode project
  phase_copy_sign = self.fastlane_runner_target.copy_files_build_phases.select { |phase_copy| phase_copy.name == "FastlaneRunnerCopySigned" }.first unless phase_copy_sign

  return true if dry_run && phase_copy_sign.nil?

  return false if dry_run

  # Proceed to upgrade
  old_phase_copy_sign = self.fastlane_runner_target.shell_script_build_phases.select { |phase_copy| phase_copy.shell_script == "cd \"${SRCROOT}\"\ncd ../..\ncp \"${TARGET_BUILD_DIR}/${EXECUTABLE_PATH}\" .\n" }.first
  old_phase_copy_sign.remove_from_project unless old_phase_copy_sign.nil?

  unless phase_copy_sign
    # Create a copy files build phase
    phase_copy_sign = self.fastlane_runner_target.new_copy_files_build_phase("FastlaneRunnerCopySigned")
    phase_copy_sign.dst_path = "$SRCROOT/../.."
    phase_copy_sign.dst_subfolder_spec = "0"
    phase_copy_sign.run_only_for_deployment_postprocessing = "0"
    targetBinaryReference = self.fastlane_runner_target.product_reference
    phase_copy_sign.add_file_reference(targetBinaryReference)

    # Set "Code sign on copy" flag on Xcode for fastlane_runner_target
    targetBinaryReference.build_files.each { |target_binary_build_file_reference|
      target_binary_build_file_reference.settings = { "ATTRIBUTES": ["CodeSignOnCopy"] }
    }
  end

  target_project.save
end

#add_missing_flags!(dry_run: false) ⇒ Object

adds build_settings flags to fastlane_runner_target



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'fastlane/lib/fastlane/swift_runner_upgrader.rb', line 215

def add_missing_flags!(dry_run: false)
  # Check if upgrade is needed
  # If fastlane build settings exists already, we don't need any more changes to the Xcode project
  self.fastlane_runner_target.build_configurations.each { |config|
    return true if dry_run && config.build_settings["CODE_SIGN_IDENTITY"].nil?
    return true if dry_run && config.build_settings["MACOSX_DEPLOYMENT_TARGET"].nil?
  }
  return false if dry_run

  # Proceed to upgrade
  self.fastlane_runner_target.build_configurations.each { |config|
    config.build_settings["CODE_SIGN_IDENTITY"] = "-"
    config.build_settings["MACOSX_DEPLOYMENT_TARGET"] = "10.12"
  }
  target_project.save
end

#add_missing_groups_and_files!(dry_run: false) ⇒ Object

adds new groups, and the files inside those groups Note: this does not add new files to existing groups, that is in add_new_files_to_groups!



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
# File 'fastlane/lib/fastlane/swift_runner_upgrader.rb', line 186

def add_missing_groups_and_files!(dry_run: false)
  missing_groups = self.find_missing_groups.to_set
  unless missing_groups.length > 0
    UI.verbose("No missing groups found, so we don't need to worry about adding new groups")
    return false
  end

  # well, we know we have some changes to make, so if this is a dry run,
  # don't bother doing anything and just return true
  return true if dry_run

  missing_groups.each do |missing_group_name|
    new_group = @root_group.new_group(missing_group_name)

    # find every file in the manifest that belongs to the new group, and add it to the new group
    self.manifest_hash.each do |filename, group|
      next unless group.casecmp(missing_group_name.downcase).zero?
      # assumes this is a new file, we don't handle moving files between groups
      new_file_reference = new_group.new_file("#{RELATIVE_SOURCE_FILE_PATH}#{filename}")

      # add references to the target, and make sure they are added to the build phase to
      self.fastlane_runner_target.source_build_phase.add_file_reference(new_file_reference)
    end
  end

  return true # yup, we definitely updated groups
end

#add_new_files_to_groups!Object



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'fastlane/lib/fastlane/swift_runner_upgrader.rb', line 138

def add_new_files_to_groups!
  inverted_hash = {}

  # need {group => [file1, file2, etc..]} instead of: {file1 => group, file2 => group, etc...}
  self.manifest_hash.each do |filename, group_name|
    group_name = group_name.downcase

    files_in_group = inverted_hash[group_name]
    if files_in_group.nil?
      files_in_group = []
      inverted_hash[group_name] = files_in_group
    end
    files_in_group << filename
  end

  # this helps us signal to the user that we made changes
  updated_project = false
  # iterate through the groups and collect all the swift files in each
  @root_group.groups.each do |group|
    # current group's filenames
    existing_group_files_set = group.files
                                    .select { |file| !file.name.nil? && file.name.end_with?(".swift") }
                                    .map(&:name)
                                    .to_set

    group_name = group.name.downcase
    manifest_group_filenames = inverted_hash[group_name]

    # compare the current group files to what the manifest says should minimally be there
    manifest_group_filenames.each do |filename|
      # current group is missing a file from the manifest, need to add it
      next if existing_group_files_set.include?(filename)

      UI.verbose("Adding new file #{filename} to group: `#{group.name}`")
      new_file_reference = group.new_file("#{RELATIVE_SOURCE_FILE_PATH}#{filename}")

      # add references to the target, and make sure they are added to the build phase to
      self.fastlane_runner_target.source_build_phase.add_file_reference(new_file_reference)

      updated_project = true
    end
  end

  return updated_project
end

#copy_file_if_needed!(filename: nil, dry_run: false) ⇒ Object

currently just copies file, even if not needed.



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

def copy_file_if_needed!(filename: nil, dry_run: false)
  needs_update = file_needs_update?(filename: filename)
  UI.verbose("file #{filename} needs an update") if needs_update

  # Ok, we know if this file needs an update, can return now if it's a dry run
  return needs_update if dry_run

  unless needs_update
    # no work needed, just return
    return false
  end

  source = File.join(self.source_swift_code_file_folder_path, "/#{filename}")
  target = File.join(self.target_swift_code_file_folder_path, "/#{filename}")

  FileUtils.cp(source, target)
  UI.verbose("Copied #{source} to #{target}")
  return true
end

#file_needs_update?(filename: nil) ⇒ Boolean

compares source file against the target file’s FastlaneRunnerAPIVersion and returned ‘true` if there is a difference

Returns:



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'fastlane/lib/fastlane/swift_runner_upgrader.rb', line 84

def file_needs_update?(filename: nil)
  # looking for something like: FastlaneRunnerAPIVersion [0.9.1]
  regex_to_use = API_VERSION_REGEX

  source = File.join(self.source_swift_code_file_folder_path, "/#{filename}")
  target = File.join(self.target_swift_code_file_folder_path, "/#{filename}")

  # target doesn't have the file yet, so ya, I'd say it needs to be updated
  return true unless File.exist?(target)

  source_file_content = File.read(source)
  target_file_content = File.read(target)

  # ignore if files don't contain FastlaneRunnerAPIVersion
  return false unless source_file_content.include?("FastlaneRunnerAPIVersion")
  return false unless target_file_content.include?("FastlaneRunnerAPIVersion")

  bundled_version = source_file_content.match(regex_to_use)[1]
  target_version = target_file_content.match(regex_to_use)[1]
  file_versions_are_different = bundled_version != target_version

  UI.verbose("#{filename} FastlaneRunnerAPIVersion (bundled/target): #{bundled_version}/#{target_version}")
  files_are_different = source_file_content != target_file_content

  if files_are_different && !file_versions_are_different
    UI.verbose("File versions are the same, but the two files are not equal, so that's a problem, setting needs update to 'true'")
  end

  needs_update = file_versions_are_different || files_are_different

  return needs_update
end

#find_missing_groupsObject



71
72
73
74
75
76
77
78
79
80
81
# File 'fastlane/lib/fastlane/swift_runner_upgrader.rb', line 71

def find_missing_groups
  missing_groups = []

  existing_group_names_set = @root_group.groups.map { |group| group.name.downcase }.to_set
  self.manifest_groups.each do |group_name|
    unless existing_group_names_set.include?(group_name.downcase)
      missing_groups << group_name
    end
  end
  return missing_groups
end

#upgrade_files!(dry_run: false) ⇒ Object



63
64
65
66
67
68
69
# File 'fastlane/lib/fastlane/swift_runner_upgrader.rb', line 63

def upgrade_files!(dry_run: false)
  upgraded_anything = false
  self.manifest_hash.each do |filename, group|
    upgraded_anything = copy_file_if_needed!(filename: filename, dry_run: dry_run) || upgraded_anything
  end
  return upgraded_anything
end

#upgrade_if_needed!(dry_run: false) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'fastlane/lib/fastlane/swift_runner_upgrader.rb', line 47

def upgrade_if_needed!(dry_run: false)
  upgraded = add_missing_flags!(dry_run: dry_run)
  upgraded = add_missing_copy_phase!(dry_run: dry_run) || upgraded
  upgraded = add_missing_groups_and_files!(dry_run: dry_run) || upgraded
  upgraded = upgrade_files!(dry_run: dry_run) || upgraded
  upgraded = add_new_files_to_groups! || upgraded

  UI.verbose("FastlaneRunner project has been updated and can be written back to disk") if upgraded
  unless dry_run
    UI.verbose("FastlaneRunner project changes have been stored") if upgraded
    target_project.save if upgraded
  end

  return upgraded
end