Class: Match::Nuke
- Inherits:
-
Object
- Object
- Match::Nuke
- Defined in:
- match/lib/match/nuke.rb
Overview
rubocop:disable Metrics/ClassLength
Instance Attribute Summary collapse
-
#certs ⇒ Object
Returns the value of attribute certs.
-
#encryption ⇒ Object
Returns the value of attribute encryption.
-
#files ⇒ Object
Returns the value of attribute files.
-
#params ⇒ Object
Returns the value of attribute params.
-
#profiles ⇒ Object
Returns the value of attribute profiles.
-
#safe_remove_certs ⇒ Object
Returns the value of attribute safe_remove_certs.
-
#storage ⇒ Object
Returns the value of attribute storage.
-
#type ⇒ Object
Returns the value of attribute type.
Instance Method Summary collapse
- #filter_by_cert ⇒ Object
- #nuke_it_now! ⇒ Object
-
#prepare_list ⇒ Object
Collect all the certs/profiles.
-
#print_tables ⇒ Object
Print tables to ask the user.
- #run(params, type: nil) ⇒ Object
- #spaceship_login ⇒ Object
-
#update_optional_values_depending_on_storage_type(params) ⇒ Object
Be smart about optional values here Depending on the storage mode, different values are required.
Instance Attribute Details
#certs ⇒ Object
Returns the value of attribute certs.
22 23 24 |
# File 'match/lib/match/nuke.rb', line 22 def certs @certs end |
#encryption ⇒ Object
Returns the value of attribute encryption.
27 28 29 |
# File 'match/lib/match/nuke.rb', line 27 def encryption @encryption end |
#files ⇒ Object
Returns the value of attribute files.
24 25 26 |
# File 'match/lib/match/nuke.rb', line 24 def files @files end |
#params ⇒ Object
Returns the value of attribute params.
18 19 20 |
# File 'match/lib/match/nuke.rb', line 18 def params @params end |
#profiles ⇒ Object
Returns the value of attribute profiles.
23 24 25 |
# File 'match/lib/match/nuke.rb', line 23 def profiles @profiles end |
#safe_remove_certs ⇒ Object
Returns the value of attribute safe_remove_certs.
21 22 23 |
# File 'match/lib/match/nuke.rb', line 21 def safe_remove_certs @safe_remove_certs end |
#storage ⇒ Object
Returns the value of attribute storage.
26 27 28 |
# File 'match/lib/match/nuke.rb', line 26 def storage @storage end |
#type ⇒ Object
Returns the value of attribute type.
19 20 21 |
# File 'match/lib/match/nuke.rb', line 19 def type @type end |
Instance Method Details
#filter_by_cert ⇒ Object
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 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'match/lib/match/nuke.rb', line 169 def filter_by_cert # Force will continue to revoke and delete all certificates and profiles return if self.params[:force] || !UI.interactive? return if self.certs.count < 2 # Print table showing certificates that can be revoked puts("") rows = self.certs.each_with_index.collect do |cert, i| cert_expiration = cert.expiration_date.nil? ? "Unknown" : Time.parse(cert.expiration_date).strftime("%Y-%m-%d") [i + 1, cert.name, cert.id, cert.class.to_s.split("::").last, cert_expiration] end puts(Terminal::Table.new({ title: "Certificates that can be #{}".green, headings: ["Option", "Name", "ID", "Type", "Expires"], rows: FastlaneCore::PrintTable.transform_output(rows) })) puts("") UI.important("By default, all listed certificates and profiles will be nuked") if UI.confirm("Do you want to only nuke specific certificates and their associated profiles?") input_indexes = UI.input("Enter the \"Option\" number(s) from the table above? (comma-separated)").split(',') # Get certificates from option indexes self.certs = input_indexes.map do |index| self.certs[index.to_i - 1] end.compact if self.certs.empty? UI.user_error!("No certificates were selected based on option number(s) entered") end # Do profile selection logic cert_ids = self.certs.map(&:id) self.profiles = self.profiles.select do |profile| profile_cert_ids = profile.certificates.map(&:id) (cert_ids & profile_cert_ids).any? end # Do file selection logic self.files = self.files.select do |f| found = false ext = File.extname(f) filename = File.basename(f, ".*") # Attempt to find cert based on filename if ext == ".cer" || ext == ".p12" found ||= self.certs.any? do |cert| filename == cert.id.to_s end end # Attempt to find profile matched on UUIDs in profile if ext == ".mobileprovision" || ext == ".provisionprofile" storage_uuid = FastlaneCore::ProvisioningProfile.uuid(f) found ||= self.profiles.any? do |profile| tmp_file = Tempfile.new tmp_file.write(Base64.decode64(profile.profile_content)) tmp_file.close # Compare profile uuid in storage to profile uuid on developer portal portal_uuid = FastlaneCore::ProvisioningProfile.uuid(tmp_file.path) storage_uuid == portal_uuid end end found end end end |
#nuke_it_now! ⇒ Object
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'match/lib/match/nuke.rb', line 292 def nuke_it_now! UI.header("Deleting #{self.profiles.count} provisioning profiles...") unless self.profiles.count == 0 self.profiles.each do |profile| UI.("Deleting profile '#{profile.name}' (#{profile.id})...") begin profile.delete! rescue => ex UI.(ex.to_s) end UI.success("Successfully deleted profile") end = self.safe_remove_certs ? "Removing" : "Revoking" UI.header("#{} #{self.certs.count} certificates...") unless self.certs.count == 0 self.certs.each do |cert| if self.safe_remove_certs UI.("Certificate '#{cert.name}' (#{cert.id}) will be removed from repository without revoking it") next end UI.("Revoking certificate '#{cert.name}' (#{cert.id})...") begin cert.delete! rescue => ex UI.(ex.to_s) end UI.success("Successfully deleted certificate") end files_to_delete = delete_files! if self.files.count > 0 files_to_delete ||= [] self.encryption.encrypt_files if self.encryption if files_to_delete.count > 0 # Now we need to save all this to the storage too, if needed = ["[fastlane]", "Nuked", "files", "for", type.to_s].join(" ") self.storage.save_changes!(files_to_commit: [], files_to_delete: files_to_delete, custom_message: ) else UI.("Your storage had no files to be deleted. This happens when you run `nuke` with an empty storage. Nothing to be worried about!") end end |
#prepare_list ⇒ Object
Collect all the certs/profiles
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 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 |
# File 'match/lib/match/nuke.rb', line 121 def prepare_list UI.("Fetching certificates and profiles...") cert_type = Match.cert_type_sym(type) cert_types = [cert_type] prov_types = [] prov_types = [:development] if cert_type == :development prov_types = [:appstore, :adhoc, :developer_id] if cert_type == :distribution prov_types = [:enterprise] if cert_type == :enterprise # Get all iOS and macOS profile self.profiles = [] prov_types.each do |prov_type| types = Match.profile_types(prov_type) self.profiles += Spaceship::ConnectAPI::Profile.all(filter: { profileType: types.join(",") }, includes: "certificates") end # Gets the main and additional cert types cert_types += (params[:additional_cert_types] || []).map do |ct| Match.cert_type_sym(ct) end # Gets all the certs form the cert types self.certs = [] self.certs += cert_types.map do |ct| certificate_type(ct).flat_map do |cert| Spaceship::ConnectAPI::Certificate.all(filter: { certificateType: cert }) end end.flatten # Finds all the .cer and .p12 files in the file storage certs = [] keys = [] cert_types.each do |ct| certs += self.storage.list_files(file_name: ct.to_s, file_ext: "cer") keys += self.storage.list_files(file_name: ct.to_s, file_ext: "p12") end # Finds all the iOS and macOS profiles in the file storage profiles = [] prov_types.each do |prov_type| profiles += self.storage.list_files(file_name: prov_type.to_s, file_ext: "mobileprovision") profiles += self.storage.list_files(file_name: prov_type.to_s, file_ext: "provisionprofile") end self.files = certs + keys + profiles end |
#print_tables ⇒ Object
Print tables to ask the user
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 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'match/lib/match/nuke.rb', line 242 def print_tables puts("") if self.certs.count > 0 rows = self.certs.collect do |cert| cert_expiration = cert.expiration_date.nil? ? "Unknown" : Time.parse(cert.expiration_date).strftime("%Y-%m-%d") [cert.name, cert.id, cert.class.to_s.split("::").last, cert_expiration] end puts(Terminal::Table.new({ title: "Certificates that are going to be #{}".green, headings: ["Name", "ID", "Type", "Expires"], rows: FastlaneCore::PrintTable.transform_output(rows) })) puts("") end if self.profiles.count > 0 rows = self.profiles.collect do |p| status = p.valid? ? p.profile_state.green : p.profile_state.red # Expires is sometimes nil expires = p.expiration_date ? Time.parse(p.expiration_date).strftime("%Y-%m-%d") : nil [p.name, p.id, status, p.profile_type, expires] end puts(Terminal::Table.new({ title: "Provisioning Profiles that are going to be revoked".green, headings: ["Name", "ID", "Status", "Type", "Expires"], rows: FastlaneCore::PrintTable.transform_output(rows) })) puts("") end if self.files.count > 0 rows = self.files.collect do |f| components = f.split(File::SEPARATOR)[-3..-1] # from "...1o7xtmh/certs/distribution/8K38XUY3AY.cer" to "distribution cert" file_type = components[0..1].reverse.join(" ")[0..-2] [file_type, components[2]] end puts(Terminal::Table.new({ title: "Files that are going to be deleted".green + "\n" + self.storage.human_readable_description, headings: ["Type", "File Name"], rows: rows })) puts("") end end |
#run(params, type: nil) ⇒ Object
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 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'match/lib/match/nuke.rb', line 29 def run(params, type: nil) self.params = params self.type = type update_optional_values_depending_on_storage_type(params) spaceship_login self.storage = Storage.from_params(params) self.storage.download # After the download was complete self.encryption = Encryption.for_storage_mode(params[:storage_mode], { git_url: params[:git_url], s3_bucket: params[:s3_bucket], s3_skip_encryption: params[:s3_skip_encryption], working_directory: storage.working_directory }) self.encryption.decrypt_files if self.encryption had_app_identifier = self.params.fetch(:app_identifier, ask: false) self.params[:app_identifier] = '' # we don't really need a value here FastlaneCore::PrintTable.print_values(config: params, hide_keys: [:app_identifier], title: "Summary for match nuke #{Fastlane::VERSION}") self.safe_remove_certs = params[:safe_remove_certs] || false prepare_list filter_by_cert print_tables if params[:readonly] UI.user_error!("`fastlane match nuke` doesn't delete anything when running with --readonly enabled") end if (self.certs + self.profiles + self.files).count > 0 unless params[:skip_confirmation] UI.error("---") = self.safe_remove_certs ? "remove" : "revoke" UI.error("Are you sure you want to completely delete and #{} all the") UI.error("certificates and delete provisioning profiles listed above? (y/n)") UI.error("Warning: By nuking distribution, both App Store and Ad Hoc profiles will be deleted") if type == "distribution" UI.error("Warning: The :app_identifier value will be ignored - this will delete all profiles for all your apps!") if had_app_identifier UI.error("---") print_safe_remove_certs_hint end if params[:skip_confirmation] || UI.confirm("Do you really want to nuke everything listed above?") nuke_it_now! UI.success("Successfully cleaned your account ♻️") else UI.success("Cancelled nuking #thanks 🏠 👨 👩 👧") end else UI.success("No relevant certificates or provisioning profiles found, nothing to nuke here :)") end ensure self.storage.clear_changes if self.storage end |
#spaceship_login ⇒ Object
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'match/lib/match/nuke.rb', line 97 def spaceship_login if (api_token = Spaceship::ConnectAPI::Token.from(hash: params[:api_key], filepath: params[:api_key_path])) UI.("Creating authorization token for App Store Connect API") Spaceship::ConnectAPI.token = api_token elsif !Spaceship::ConnectAPI.token.nil? UI.("Using existing authorization token for App Store Connect API") else Spaceship::ConnectAPI.login(params[:username], use_portal: true, use_tunes: false, portal_team_id: params[:team_id], team_name: params[:team_name]) end if Spaceship::ConnectAPI.client.in_house? && (type == "distribution" || type == "enterprise") UI.error("---") UI.error("⚠️ Warning: This seems to be an Enterprise account!") unless self.safe_remove_certs UI.error("By nuking your account's distribution, all your apps deployed via ad-hoc will stop working!") if type == "distribution" UI.error("By nuking your account's enterprise, all your in-house apps will stop working!") if type == "enterprise" end UI.error("---") print_safe_remove_certs_hint UI.user_error!("Enterprise account nuke cancelled") unless UI.confirm("Do you really want to nuke your Enterprise account?") end end |
#update_optional_values_depending_on_storage_type(params) ⇒ Object
Be smart about optional values here Depending on the storage mode, different values are required
91 92 93 94 95 |
# File 'match/lib/match/nuke.rb', line 91 def update_optional_values_depending_on_storage_type(params) if params[:storage_mode] != "git" params.option_for_key(:git_url).optional = true end end |