Skip to content

Commit ebe4695

Browse files
committed
Merge pull request #11 from DeployGate/feature/v0.0.3
v0.0.3
2 parents 1b7749c + 3636cb2 commit ebe4695

File tree

9 files changed

+170
-80
lines changed

9 files changed

+170
-80
lines changed

deploygate.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ POST_INSTALL_MESSAGE
3232
spec.add_dependency 'xcodeproj', '~> 0.28.2'
3333
spec.add_dependency 'github_issue_request', '~> 0.0.2'
3434
spec.add_dependency 'highline', '~> 1.7.8'
35+
spec.add_dependency 'uuid', '~> 2.3.8'
3536

3637
# ios build
3738
spec.add_dependency 'gym', '~> 1.0.0'

lib/deploygate.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
require "find"
1010
require "github_issue_request"
1111
require "highline"
12+
require "uuid"
1213

1314
# ios build
1415
require "gym"

lib/deploygate/builds/ios/analyze.rb

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,38 @@ def initialize(workspaces)
3030

3131
# @return [String]
3232
def target_bundle_identifier
33-
scheme_file = find_xcschemes
34-
xs = Xcodeproj::XCScheme.new(scheme_file)
35-
target_name = xs.profile_action.buildable_product_runnable.buildable_reference.target_name
36-
37-
project = Xcodeproj::Project.open(@xcodeproj)
38-
target = project.native_targets.reject{|target| target.name != target_name}.first
39-
product_name = target.product_name
40-
conf = target.build_configuration_list.build_configurations.reject{|conf| conf.name != BUILD_CONFIGRATION}.first
41-
identifier = conf.build_settings['PRODUCT_BUNDLE_IDENTIFIER']
33+
product_name = target_product_name
34+
identifier = target_build_configration.build_settings['PRODUCT_BUNDLE_IDENTIFIER']
4235
identifier.gsub!(/\$\(PRODUCT_NAME:.+\)/, product_name)
4336

4437
identifier
4538
end
4639

40+
# @return [String]
41+
def target_xcode_setting_provisioning_profile_uuid
42+
uuid = target_build_configration.build_settings['PROVISIONING_PROFILE']
43+
UUID.validate(uuid) ? uuid : nil
44+
end
45+
4746
private
4847

48+
def target_build_configration
49+
target_project_setting.build_configuration_list.build_configurations.reject{|conf| conf.name != BUILD_CONFIGRATION}.first
50+
end
51+
52+
def target_product_name
53+
target_project_setting.product_name
54+
end
55+
56+
def target_project_setting
57+
scheme_file = find_xcschemes
58+
xs = Xcodeproj::XCScheme.new(scheme_file)
59+
target_name = xs.profile_action.buildable_product_runnable.buildable_reference.target_name
60+
61+
project = Xcodeproj::Project.open(@xcodeproj)
62+
project.native_targets.reject{|target| target.name != target_name}.first
63+
end
64+
4965
def find_xcschemes
5066
shared_schemes = Dir[File.join(@xcodeproj, 'xcshareddata', 'xcschemes', '*.xcscheme')].reject do |scheme|
5167
@scheme != File.basename(scheme, '.xcscheme')

lib/deploygate/builds/ios/export.rb

Lines changed: 55 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,29 @@ class Export
99

1010
class << self
1111
# @param [String] bundle_identifier
12+
# @param [String] uuid
1213
# @return [Hash]
13-
def find_local_data(bundle_identifier)
14+
def find_local_data(bundle_identifier, uuid = nil)
1415
result_profiles = {}
1516
teams = {}
16-
profiles.each do |profile_path|
17-
plist = analyze_profile(profile_path)
18-
entities = plist['Entitlements']
17+
profile_paths = load_profile_paths
18+
profiles = profile_paths.map{|p| profile_to_plist(p)}
19+
profiles.reject! {|profile| profile['UUID'] != uuid} unless uuid.nil?
20+
21+
profiles.each do |profile|
22+
entities = profile['Entitlements']
1923
unless entities['get-task-allow']
2024
team = entities['com.apple.developer.team-identifier']
2125
application_id = entities['application-identifier']
2226
application_id.slice!(/^#{team}\./)
2327
application_id = '.' + application_id if application_id == '*'
2428
if bundle_identifier.match(application_id) &&
25-
DateTime.now < plist['ExpirationDate'] &&
26-
installed_certificate?(profile_path)
29+
DateTime.now < profile['ExpirationDate'] &&
30+
installed_certificate?(profile['Path'])
2731

28-
teams[team] = plist['TeamName'] if teams[team].nil?
32+
teams[team] = profile['TeamName'] if teams[team].nil?
2933
result_profiles[team] = [] if result_profiles[team].nil?
30-
result_profiles[team].push(profile_path)
34+
result_profiles[team].push(profile['Path'])
3135
end
3236
end
3337
end
@@ -41,11 +45,14 @@ def find_local_data(bundle_identifier)
4145
# @param [String] profile_path
4246
# @return [Boolean]
4347
def installed_certificate?(profile_path)
44-
plist = analyze_profile(profile_path)
45-
certificate_str = plist['DeveloperCertificates'].first.read
46-
certificate = OpenSSL::X509::Certificate.new certificate_str
47-
id = OpenSSL::Digest::SHA1.new(certificate.to_der).to_s.upcase!
48-
installed_identies.include?(id)
48+
profile = profile_to_plist(profile_path)
49+
certs = profile['DeveloperCertificates'].map do |cert|
50+
certificate_str = cert.read
51+
certificate = OpenSSL::X509::Certificate.new certificate_str
52+
id = OpenSSL::Digest::SHA1.new(certificate.to_der).to_s.upcase!
53+
installed_identies.include?(id)
54+
end
55+
certs.include?(true)
4956
end
5057

5158
# @return [Array]
@@ -64,25 +71,39 @@ def installed_identies
6471
ids
6572
end
6673

67-
# @param [Array] profiles
74+
# @param [Array] profile_paths
6875
# @return [String]
69-
def select_profile(profiles)
76+
def select_profile(profile_paths)
7077
select = nil
7178

72-
profiles.each do |profile|
73-
select = profile if adhoc?(profile) && select.nil?
74-
select = profile if inhouse?(profile)
79+
profile_paths.each do |path|
80+
select = path if adhoc?(path) && select.nil?
81+
select = path if inhouse?(path)
7582
end
7683
select
7784
end
7885

7986
# @param [String] profile_path
8087
# @return [String]
8188
def codesigning_identity(profile_path)
82-
plist = analyze_profile(profile_path)
83-
method = method(profile_path)
84-
identity = "iPhone Distribution: #{plist['TeamName']}"
85-
identity += " (#{plist['Entitlements']['com.apple.developer.team-identifier']})" if method == AD_HOC
89+
profile = profile_to_plist(profile_path)
90+
identity = nil
91+
92+
profile['DeveloperCertificates'].each do |cert|
93+
certificate_str = cert.read
94+
certificate = OpenSSL::X509::Certificate.new certificate_str
95+
id = OpenSSL::Digest::SHA1.new(certificate.to_der).to_s.upcase!
96+
97+
available = `security find-identity -v -p codesigning`
98+
available.split("\n").each do |current|
99+
next if current.include? "REVOKED"
100+
begin
101+
search = current.match(/.*\) (.*) \"(.*)\"/)
102+
identity = search[2] if id == search[1]
103+
rescue
104+
end
105+
end
106+
end
86107

87108
identity
88109
end
@@ -96,46 +117,32 @@ def method(profile_path)
96117
# @param [String] profile_path
97118
# @return [Boolean]
98119
def adhoc?(profile_path)
99-
plist = analyze_profile(profile_path)
100-
!plist['Entitlements']['get-task-allow'] && plist['ProvisionsAllDevices'].nil?
120+
profile = profile_to_plist(profile_path)
121+
!profile['Entitlements']['get-task-allow'] && profile['ProvisionsAllDevices'].nil?
101122
end
102123

103124
# @param [String] profile_path
104125
# @return [Boolean]
105126
def inhouse?(profile_path)
106-
plist = analyze_profile(profile_path)
107-
!plist['Entitlements']['get-task-allow'] && !plist['ProvisionsAllDevices'].nil?
127+
profile = profile_to_plist(profile_path)
128+
!profile['Entitlements']['get-task-allow'] && !profile['ProvisionsAllDevices'].nil?
129+
end
130+
131+
def load_profile_paths
132+
profiles_path = File.expand_path("~") + "/Library/MobileDevice/Provisioning Profiles/*.mobileprovision"
133+
Dir[profiles_path]
108134
end
109135

110136
# @param [String] profile_path
111137
# @return [Hash]
112-
def analyze_profile(profile_path)
113-
plist = nil
138+
def profile_to_plist(profile_path)
114139
File.open(profile_path) do |profile|
115140
asn1 = OpenSSL::ASN1.decode(profile.read)
116141
plist_str = asn1.value[1].value[0].value[2].value[1].value[0].value
117142
plist = Plist.parse_xml plist_str.force_encoding('UTF-8')
143+
plist['Path'] = profile_path
144+
return plist
118145
end
119-
plist
120-
end
121-
122-
# @return [Array]
123-
def profiles
124-
profiles = []
125-
Find.find(profile_dir_path) do |path|
126-
next if path == profile_dir_path
127-
Find.prune if FileTest.directory?(path)
128-
if File.extname(path) == PROFILE_EXTNAME
129-
profiles.push(path)
130-
end
131-
end
132-
133-
profiles
134-
end
135-
136-
# @return [String]
137-
def profile_dir_path
138-
File.join(ENV['HOME'], 'Library/MobileDevice/Provisioning Profiles')
139146
end
140147
end
141148
end

lib/deploygate/builds/ios/set_profile.rb

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,54 @@ def app_id_create
3939
false
4040
end
4141

42+
# @param [String] uuid
4243
# @return [Array]
43-
def create_provisioning
44+
def create_provisioning(uuid)
45+
FileUtils.mkdir_p(OUTPUT_PATH)
46+
47+
if uuid.nil?
48+
return install_provisioning
49+
else
50+
return select_uuid_provisioning(uuid)
51+
end
52+
end
53+
54+
private
55+
56+
def select_uuid_provisioning(uuid)
57+
adhoc_profiles = Spaceship.provisioning_profile.ad_hoc.all
58+
inhouse_profiles = Spaceship.provisioning_profile.in_house.all
59+
60+
adhoc_profiles.reject!{|p| p.uuid != uuid}
61+
inhouse_profiles.reject!{|p| p.uuid != uuid}
62+
select_profile = nil
63+
method = nil
64+
unless adhoc_profiles.empty?
65+
select_profile = adhoc_profiles.first
66+
method = Export::AD_HOC
67+
end
68+
unless inhouse_profiles.empty?
69+
select_profile = inhouse_profiles.first
70+
method = Export::ENTERPRISE
71+
end
72+
raise 'Not Xcode selected Provisioning Profile' if select_profile.nil?
73+
74+
values = {
75+
:adhoc => method == Export::AD_HOC ? true : false,
76+
:app_identifier => @identifier,
77+
:username => @username,
78+
:output_path => OUTPUT_PATH,
79+
:provisioning_name => select_profile.name,
80+
:team_id => Spaceship.client.team_id
81+
}
82+
v = FastlaneCore::Configuration.create(Sigh::Options.available_options, values)
83+
Sigh.config = v
84+
download_profile_path = Sigh::Manager.start
85+
86+
[download_profile_path]
87+
end
88+
89+
def install_provisioning
4490
if @method == Export::AD_HOC
4591
prod_certs = Spaceship.certificate.production.all
4692
else
@@ -58,7 +104,6 @@ def create_provisioning
58104
end
59105
raise 'Not local install certificate' if distribution_cert_ids.empty?
60106

61-
FileUtils.mkdir_p(OUTPUT_PATH)
62107
provisionings = []
63108
distribution_cert_ids.each do |cert_id|
64109
values = {
@@ -71,7 +116,8 @@ def create_provisioning
71116
}
72117
v = FastlaneCore::Configuration.create(Sigh::Options.available_options, values)
73118
Sigh.config = v
74-
provisionings.push(Sigh::Manager.start)
119+
download_profile_path = Sigh::Manager.start
120+
provisionings.push(download_profile_path)
75121
end
76122

77123
provisionings

lib/deploygate/command_builder.rb

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def run
1717
begin
1818
Commands::Init.run
1919
rescue => e
20-
error_handling("Commands::Init Error: #{e.class}", e.message, ['bug', 'Init'])
20+
error_handling("Commands::Init Error: #{e.class}", create_error_issue_body(e), ['bug', 'Init'])
2121
raise e
2222
end
2323
end
@@ -35,7 +35,7 @@ def run
3535
begin
3636
Commands::Deploy.run(args, options)
3737
rescue => e
38-
error_handling("Commands::Deploy Error: #{e.class}", e.message, ['bug', 'Deploy'])
38+
error_handling("Commands::Deploy Error: #{e.class}", create_error_issue_body(e), ['bug', 'Deploy'])
3939
raise e
4040
end
4141
end
@@ -49,7 +49,7 @@ def run
4949
begin
5050
Commands::Logout.run
5151
rescue => e
52-
error_handling("Commands::Logout Error: #{e.class}", e.message, ['bug', 'Logout'])
52+
error_handling("Commands::Logout Error: #{e.class}", create_error_issue_body(e), ['bug', 'Logout'])
5353
raise e
5454
end
5555
end
@@ -58,6 +58,23 @@ def run
5858
run!
5959
end
6060

61+
# @param [Exception] error
62+
# @return [String]
63+
def create_error_issue_body(error)
64+
return <<EOF
65+
# Error message
66+
#{error.message}
67+
68+
# Backtrace
69+
```
70+
#{error.backtrace.join("\n")}
71+
```
72+
EOF
73+
end
74+
75+
# @param [String] title
76+
# @param [String] body
77+
# @param [Array] labels
6178
def error_handling(title, body, labels)
6279
options = {
6380
:title => title,

lib/deploygate/commands/deploy/build.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,15 @@ def ios(workspaces, options)
3434
puts 'Example: com.example.ios'
3535
identifier = input_bundle_identifier
3636
end
37+
uuid = analyze.target_xcode_setting_provisioning_profile_uuid
3738

38-
data = DeployGate::Builds::Ios::Export.find_local_data(identifier)
39+
data = DeployGate::Builds::Ios::Export.find_local_data(identifier, uuid)
3940
profiles = data[:profiles]
4041
teams = data[:teams]
4142

4243
target_provisioning_profile = nil
4344
if teams.empty?
44-
target_provisioning_profile = create_provisioning(identifier)
45+
target_provisioning_profile = create_provisioning(identifier, uuid)
4546
elsif teams.count == 1
4647
target_provisioning_profile = DeployGate::Builds::Ios::Export.select_profile(profiles[teams.keys.first])
4748
elsif teams.count >= 2
@@ -98,8 +99,9 @@ def select_teams(teams, profiles)
9899
end
99100

100101
# @param [String] identifier
102+
# @param [String] uuid
101103
# @return [String]
102-
def create_provisioning(identifier)
104+
def create_provisioning(identifier, uuid)
103105
puts <<EOF
104106
105107
No suitable provisioning profile found to export the app.
@@ -131,7 +133,7 @@ def create_provisioning(identifier)
131133
end
132134

133135
begin
134-
provisioning_profiles = set_profile.create_provisioning
136+
provisioning_profiles = set_profile.create_provisioning(uuid)
135137
rescue => e
136138
DeployGate::Message::Error.print("Error: Failed to create provisioning profile")
137139
raise e

lib/deploygate/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module DeployGate
2-
VERSION = "0.0.2"
2+
VERSION = "0.0.3"
33
end

0 commit comments

Comments
 (0)