/
resign.rb
204 lines (174 loc) 路 8.04 KB
/
resign.rb
1
2
3
4
5
6
7
8
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
74
75
76
77
78
79
80
81
82
83
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
116
117
118
119
120
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
168
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
require 'shellwords'
require 'fastlane_core/globals'
require_relative 'module'
module Sigh
# Resigns an existing ipa file
class Resign
def run(options, args)
# get the command line inputs and parse those into the vars we need...
ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path = get_inputs(options, args)
# ... then invoke our programmatic interface with these vars
unless resign(ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path)
UI.user_error!("Failed to re-sign .ipa")
end
end
def self.resign(ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path)
self.new.resign(ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path)
end
def resign(ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path)
resign_path = find_resign_path
signing_identity = find_signing_identity(signing_identity)
unless provisioning_profiles.kind_of?(Enumerable)
provisioning_profiles = [provisioning_profiles]
end
# validate that we have valid values for all these params, we don't need to check signing_identity because `find_signing_identity` will only ever return a valid value
validate_params(resign_path, ipa, provisioning_profiles)
entitlements = "-e #{entitlements.shellescape}" if entitlements
provisioning_options = create_provisioning_options(provisioning_profiles)
version = "-n #{version}" if version
display_name = "-d #{display_name.shellescape}" if display_name
short_version = "--short-version #{short_version}" if short_version
bundle_version = "--bundle-version #{bundle_version}" if bundle_version
verbose = "-v" if FastlaneCore::Globals.verbose?
bundle_id = "-b '#{new_bundle_id}'" if new_bundle_id
use_app_entitlements_flag = "--use-app-entitlements" if use_app_entitlements
specific_keychain = "--keychain-path #{keychain_path.shellescape}" if keychain_path
command = [
resign_path.shellescape,
ipa.shellescape,
signing_identity.shellescape,
provisioning_options, # we are aleady shellescaping this above, when we create the provisioning_options from the provisioning_profiles
entitlements,
version,
display_name,
short_version,
bundle_version,
use_app_entitlements_flag,
verbose,
bundle_id,
specific_keychain,
ipa.shellescape # Output path must always be last argument
].join(' ')
puts(command.magenta)
puts(`#{command}`)
if $?.to_i == 0
UI.success("Successfully signed #{ipa}!")
true
else
UI.error("Something went wrong while code signing #{ipa}")
false
end
end
def get_inputs(options, args)
ipa = args.first || find_ipa || UI.input('Path to ipa file: ')
signing_identity = options.signing_identity || ask_for_signing_identity
provisioning_profiles = options.provisioning_profile || find_provisioning_profile || UI.input('Path to provisioning file: ')
entitlements = options.entitlements || nil
version = options.version_number || nil
display_name = options.display_name || nil
short_version = options.short_version || nil
bundle_version = options.bundle_version || nil
new_bundle_id = options.new_bundle_id || nil
use_app_entitlements = options.use_app_entitlements || nil
keychain_path = options.keychain_path || nil
if options.provisioning_name
UI.important("The provisioning_name (-n) option is not applicable to resign. You should use provisioning_profile (-p) instead")
end
return ipa, signing_identity, provisioning_profiles, entitlements, version, display_name, short_version, bundle_version, new_bundle_id, use_app_entitlements, keychain_path
end
def find_resign_path
File.join(Sigh::ROOT, 'lib', 'assets', 'resign.sh')
end
def find_ipa
Dir[File.join(Dir.pwd, '*.ipa')].sort { |a, b| File.mtime(a) <=> File.mtime(b) }.first
end
def find_provisioning_profile
Dir[File.join(Dir.pwd, '*.mobileprovision')].sort { |a, b| File.mtime(a) <=> File.mtime(b) }.first
end
def find_signing_identity(signing_identity)
until (signing_identity = sha1_for_signing_identity(signing_identity))
UI.error("Couldn't find signing identity '#{signing_identity}'.")
signing_identity = ask_for_signing_identity
end
signing_identity
end
def sha1_for_signing_identity(signing_identity)
identities = installed_identities
return signing_identity if identities.keys.include?(signing_identity)
identities.key(signing_identity)
end
def create_provisioning_options(provisioning_profiles)
# provisioning_profiles is passed either a hash (to be able to resign extensions/nested apps):
# (in that case the underlying resign.sh expects values given as "-p at.fastlane=/folder/mobile.mobileprovision -p at.fastlane.today=/folder/mobile.mobileprovision")
# {
# "at.fastlane" => "/folder/mobile.mobileprovision",
# "at.fastlane.today" => "/folder/mobile.mobileprovision"
# }
# or an array
# (resign.sh also takes "-p /folder/mobile.mobileprovision" as a param)
# [
# "/folder/mobile.mobileprovision"
# ]
provisioning_profiles.map do |app_id, app_id_prov|
if app_id_prov
app_id_prov = File.expand_path(app_id_prov)
else
app_id = File.expand_path(app_id)
end
"-p #{[app_id, app_id_prov].compact.map(&:shellescape).join('=')}"
end.join(' ')
end
def validate_params(resign_path, ipa, provisioning_profiles)
validate_resign_path(resign_path)
validate_ipa_file(ipa)
provisioning_profiles.each { |fst, snd| validate_provisioning_file(snd || fst) }
end
def validate_resign_path(resign_path)
UI.user_error!('Could not find resign.sh file. Please try re-installing the gem') unless File.exist?(resign_path)
end
def validate_ipa_file(ipa)
UI.user_error!("ipa file could not be found or is not an ipa file (#{ipa})") unless File.exist?(ipa) && ipa.end_with?('.ipa')
end
def validate_provisioning_file(provisioning_profile)
unless File.exist?(provisioning_profile) && provisioning_profile.end_with?('.mobileprovision')
UI.user_error!("Provisioning profile file could not be found or is not a .mobileprovision file (#{provisioning_profile})")
end
end
def print_available_identities
UI.message("Available identities: \n\t#{installed_identity_descriptions.join("\n\t")}\n")
end
def ask_for_signing_identity
print_available_identities
UI.input('Signing Identity: ')
end
# Hash of available signing identities
def installed_identities
available = request_valid_identities
ids = {}
available.split("\n").each do |current|
begin
sha1 = current.match(/[a-zA-Z0-9]{40}/).to_s
name = current.match(/.*\"(.*)\"/)[1]
ids[sha1] = name
rescue
nil
end # the last line does not match
end
ids
end
def request_valid_identities
`security find-identity -v -p codesigning`
end
def installed_identity_descriptions
descriptions = []
installed_identities.group_by { |sha1, name| name }.each do |name, identities|
descriptions << name
# Show SHA-1 for homonymous identities
descriptions += identities.map do |sha1, _|
"\t#{sha1}"
end
end
descriptions
end
end
end