sigh 0.5.2 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -0
- data/lib/sigh.rb +0 -1
- data/lib/sigh/manager.rb +4 -2
- data/lib/sigh/options.rb +15 -3
- data/lib/sigh/profile_analyser.rb +1 -1
- data/lib/sigh/resign.rb +8 -1
- data/lib/sigh/spaceship/runner.rb +144 -0
- data/lib/sigh/version.rb +1 -1
- metadata +20 -7
- data/lib/sigh/developer_center.rb +0 -292
- data/lib/sigh/developer_center_signing.rb +0 -88
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab5bb66a972ec8b64c52d5ea5ae1b285c51f08f5
|
4
|
+
data.tar.gz: cb417be836c696568ad39438c1c553edacf7e653
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 36aea2553f3c24ac3638dcdf03a0ef989750fcd5a1a62304980e3582ec9120b3d21d174e633784cbe7c28bea9076c0607a11ebc17a2899146eed6ff3fbca6b11
|
7
|
+
data.tar.gz: 01f113d0f47e115cd554b04ead4842e416536bbf0cbdd377dc8553b81d6a6cb980e3110a77d19d148837059a03c4ee14900c377b173460a441fc59de5cd8312d
|
data/README.md
CHANGED
@@ -51,6 +51,9 @@ Special thanks to [Matthias Tretter](https://twitter.com/myell0w) for coming up
|
|
51
51
|
|
52
52
|
<h5 align="center"><code>sigh</code> is part of <a href="https://fastlane.tools">fastlane</a>: connect all deployment tools into one streamlined workflow.</h5>
|
53
53
|
|
54
|
+
### spaceship version
|
55
|
+
|
56
|
+
If you're feeling adventurous and want to test the new `sigh` beta with [spaceship](https://spaceship.airforce), update using `sudo gem update sigh --pre`. More information in the [release notes](https://github.com/KrauseFx/sigh/releases/tag/1.0.0.beta5).
|
54
57
|
|
55
58
|
# Features
|
56
59
|
|
@@ -175,6 +178,8 @@ Choose signing certificate to use:
|
|
175
178
|
- `SIGH_CERTIFICATE_ID` (The ID of the certificate)
|
176
179
|
- `SIGH_CERTIFICATE_EXPIRE_DATE` (The expire date of the certificate)
|
177
180
|
|
181
|
+
As always, run `sigh --help` to get a list of all variables.
|
182
|
+
|
178
183
|
If you're using [cert](https://github.com/KrauseFx/cert) in combination with [fastlane](https://github.com/KrauseFx/fastlane) the signing certificate will automatically be selected for you. (make sure to run `cert` before `sigh`)
|
179
184
|
|
180
185
|
`sigh` will store the `UDID` of the generated provisioning profile in the environment: `SIGH_UDID`.
|
data/lib/sigh.rb
CHANGED
data/lib/sigh/manager.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'plist'
|
2
|
+
require 'sigh/spaceship/runner'
|
2
3
|
|
3
4
|
module Sigh
|
4
5
|
class Manager
|
5
6
|
def self.start
|
6
|
-
|
7
|
+
start = Time.now
|
8
|
+
path = Sigh::Runner.new.run
|
7
9
|
|
8
10
|
return nil unless path
|
9
11
|
|
@@ -38,7 +40,7 @@ module Sigh
|
|
38
40
|
(FileUtils.copy profile, destination rescue nil) # if the directory doesn't exist yet
|
39
41
|
|
40
42
|
if File.exists? destination
|
41
|
-
Helper.log.info "Profile installed
|
43
|
+
Helper.log.info "Profile successfully installed".green
|
42
44
|
else
|
43
45
|
raise "Failed installation of provisioning profile at location: #{destination}".red
|
44
46
|
end
|
data/lib/sigh/options.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'fastlane_core'
|
2
|
+
require 'credentials_manager'
|
2
3
|
|
3
4
|
module Sigh
|
4
5
|
class Options
|
@@ -22,7 +23,7 @@ module Sigh
|
|
22
23
|
default_value: false),
|
23
24
|
FastlaneCore::ConfigItem.new(key: :force,
|
24
25
|
env_name: "SIGH_FORCE",
|
25
|
-
description: "Renew
|
26
|
+
description: "Renew provisioning profiles regardless of its state",
|
26
27
|
is_string: false,
|
27
28
|
default_value: false),
|
28
29
|
FastlaneCore::ConfigItem.new(key: :app_identifier,
|
@@ -34,7 +35,7 @@ module Sigh
|
|
34
35
|
short_option: "-u",
|
35
36
|
env_name: "SIGH_USERNAME",
|
36
37
|
description: "Your Apple ID Username",
|
37
|
-
default_value: CredentialsManager::AppfileConfig.try_fetch_value(:apple_id),
|
38
|
+
default_value: ENV["DELIVER_USER"] || CredentialsManager::AppfileConfig.try_fetch_value(:apple_id),
|
38
39
|
verify_block: Proc.new do |value|
|
39
40
|
CredentialsManager::PasswordManager.shared_manager(value)
|
40
41
|
end),
|
@@ -43,9 +44,19 @@ module Sigh
|
|
43
44
|
env_name: "SIGH_TEAM_ID",
|
44
45
|
description: "The ID of your team if you're in multiple teams",
|
45
46
|
optional: true,
|
47
|
+
default_value: CredentialsManager::AppfileConfig.try_fetch_value(:team_id),
|
46
48
|
verify_block: Proc.new do |value|
|
47
49
|
ENV["FASTLANE_TEAM_ID"] = value
|
48
50
|
end),
|
51
|
+
FastlaneCore::ConfigItem.new(key: :team_name,
|
52
|
+
short_option: "-l",
|
53
|
+
env_name: "SIGH_TEAM_NAME",
|
54
|
+
description: "The name of your team if you're in multiple teams",
|
55
|
+
optional: true,
|
56
|
+
default_value: CredentialsManager::AppfileConfig.try_fetch_value(:team_name),
|
57
|
+
verify_block: Proc.new do |value|
|
58
|
+
ENV["FASTLANE_TEAM_NAME"] = value
|
59
|
+
end),
|
49
60
|
FastlaneCore::ConfigItem.new(key: :provisioning_name,
|
50
61
|
short_option: "-n",
|
51
62
|
env_name: "SIGH_PROVISIONING_PROFILE_NAME",
|
@@ -62,7 +73,7 @@ module Sigh
|
|
62
73
|
FastlaneCore::ConfigItem.new(key: :cert_id,
|
63
74
|
short_option: "-i",
|
64
75
|
env_name: "SIGH_CERTIFICATE_ID",
|
65
|
-
description: "The ID of the certificate to use",
|
76
|
+
description: "The ID of the code signing certificate to use (e.g. 78ADL6LVAA) ",
|
66
77
|
optional: true),
|
67
78
|
FastlaneCore::ConfigItem.new(key: :cert_owner_name,
|
68
79
|
short_option: "-c",
|
@@ -82,6 +93,7 @@ module Sigh
|
|
82
93
|
verify_block: Proc.new do |value|
|
83
94
|
raise "The output name must end with .mobileprovision".red unless value.end_with?".mobileprovision"
|
84
95
|
end)
|
96
|
+
|
85
97
|
]
|
86
98
|
end
|
87
99
|
end
|
@@ -5,7 +5,7 @@ module Sigh
|
|
5
5
|
def self.run(path)
|
6
6
|
plist = Plist::parse_xml(`security cms -D -i '#{path}'`)
|
7
7
|
if plist.count > 10
|
8
|
-
Helper.log.info("Provisioning profile of app '#{plist['AppIDName']}' with the name '#{plist['Name']}' successfully
|
8
|
+
Helper.log.info("Provisioning profile of app '#{plist['AppIDName']}' with the name '#{plist['Name']}' successfully analysed.".green)
|
9
9
|
return plist["UUID"]
|
10
10
|
else
|
11
11
|
Helper.log.error("Error parsing provisioning profile at path '#{path}'".red)
|
data/lib/sigh/resign.rb
CHANGED
@@ -33,7 +33,14 @@ module Sigh
|
|
33
33
|
puts command.magenta
|
34
34
|
output = `#{command}`
|
35
35
|
puts output
|
36
|
-
|
36
|
+
|
37
|
+
if signing_identity =~ /^iPhone Developer:*/
|
38
|
+
ptn = 'Assuming Development Identity'
|
39
|
+
else
|
40
|
+
ptn = 'Assuming Distribution Identity'
|
41
|
+
end
|
42
|
+
|
43
|
+
if output.include?(ptn)
|
37
44
|
Helper.log.info "Successfully signed #{ipa}!".green
|
38
45
|
true
|
39
46
|
else
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'spaceship'
|
2
|
+
|
3
|
+
module Sigh
|
4
|
+
class Runner
|
5
|
+
attr_accessor :spaceship
|
6
|
+
|
7
|
+
# Uses the spaceship to create or download a provisioning profile
|
8
|
+
# returns the path the newly created provisioning profile (in /tmp usually)
|
9
|
+
def run
|
10
|
+
Helper.log.info "Starting login"
|
11
|
+
Spaceship.login(Sigh.config[:username], nil)
|
12
|
+
Spaceship.select_team
|
13
|
+
Helper.log.info "Successfully logged in"
|
14
|
+
|
15
|
+
profiles = fetch_profiles # download the profile if it's there
|
16
|
+
|
17
|
+
if profiles.count > 0
|
18
|
+
Helper.log.info "Found #{profiles.count} matching profile(s)".yellow
|
19
|
+
profile = profiles.first
|
20
|
+
|
21
|
+
if Sigh.config[:force]
|
22
|
+
unless profile_type == Spaceship.provisioning_profile::AppStore
|
23
|
+
Helper.log.info "Updating the profile to include all devices".yellow
|
24
|
+
profile.devices = Spaceship.device.all
|
25
|
+
else
|
26
|
+
Helper.log.info "Updating the provisioning profile".yellow
|
27
|
+
end
|
28
|
+
|
29
|
+
profile = profile.update! # assign it, as it's a new profile
|
30
|
+
end
|
31
|
+
else
|
32
|
+
Helper.log.info "No existing profiles found, creating a new one for you".yellow
|
33
|
+
profile = create_profile!
|
34
|
+
end
|
35
|
+
|
36
|
+
raise "Something went wrong fetching the latest profile".red unless profile
|
37
|
+
|
38
|
+
path = download_profile(profile)
|
39
|
+
store_provisioning_id_in_environment(path)
|
40
|
+
|
41
|
+
return path
|
42
|
+
end
|
43
|
+
|
44
|
+
# The kind of provisioning profile we're interested in
|
45
|
+
def profile_type
|
46
|
+
return @profile_type if @profile_type
|
47
|
+
|
48
|
+
@profile_type = Spaceship.provisioning_profile.app_store
|
49
|
+
@profile_type = Spaceship.provisioning_profile.ad_hoc if Sigh.config[:adhoc]
|
50
|
+
@profile_type = Spaceship.provisioning_profile.development if Sigh.config[:development]
|
51
|
+
@profile_type = Spaceship.provisioning_profile.in_house if Spaceship.client.in_house?
|
52
|
+
|
53
|
+
@profile_type
|
54
|
+
end
|
55
|
+
|
56
|
+
# Fetches a profile matching the user's search requirements
|
57
|
+
def fetch_profiles
|
58
|
+
profile_type.find_by_bundle_id(Sigh.config[:app_identifier])
|
59
|
+
end
|
60
|
+
|
61
|
+
# Create a new profile and return it
|
62
|
+
def create_profile!
|
63
|
+
cert = certificate_to_use
|
64
|
+
bundle_id = Sigh.config[:app_identifier]
|
65
|
+
name = Sigh.config[:provisioning_name] || [bundle_id, profile_type.pretty_type].join(' ')
|
66
|
+
|
67
|
+
if Spaceship.provisioning_profile.all.find { |p| p.name == name }
|
68
|
+
Helper.log.error "The name '#{name}' is already taken, using another one."
|
69
|
+
name += " #{Time.now.to_i}"
|
70
|
+
end
|
71
|
+
|
72
|
+
Helper.log.info "Creating new provisioning profile for '#{Sigh.config[:app_identifier]}' with name '#{name}'".yellow
|
73
|
+
profile = profile_type.create!(name: name,
|
74
|
+
bundle_id: bundle_id,
|
75
|
+
certificate: cert)
|
76
|
+
profile
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
# Certificate to use based on the current distribution mode
|
81
|
+
def certificate_to_use
|
82
|
+
if profile_type == Spaceship.provisioning_profile.Development
|
83
|
+
certificates = Spaceship.certificate.development.all
|
84
|
+
elsif profile_type == Spaceship.provisioning_profile.InHouse
|
85
|
+
certificates = Spaceship.certificate.in_house.all
|
86
|
+
else
|
87
|
+
certificates = Spaceship.certificate.production.all # Ad hoc or App Store
|
88
|
+
end
|
89
|
+
|
90
|
+
# Filter them
|
91
|
+
certificates = certificates.find_all do |c|
|
92
|
+
if Sigh.config[:cert_id]
|
93
|
+
next unless (c.id == Sigh.config[:cert_id].strip)
|
94
|
+
end
|
95
|
+
|
96
|
+
if Sigh.config[:cert_owner_name]
|
97
|
+
next unless (c.owner_name.strip == Sigh.config[:cert_owner_name].strip)
|
98
|
+
end
|
99
|
+
|
100
|
+
true
|
101
|
+
end
|
102
|
+
|
103
|
+
if certificates.count > 1
|
104
|
+
Helper.log.info "Found more than one code signing identity. Choosing the first one. Check out `sigh --help` to see all available options.".yellow
|
105
|
+
Helper.log.info "Available Code Signing Identities for current filters:".green
|
106
|
+
certificates.each do |c|
|
107
|
+
Helper.log.info ("\t- Name: " + c.owner_name + " - ID: " + c.id + " - Expires: " + c.expires.strftime("%d/%m/%Y")).green
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
if certificates.count == 0
|
112
|
+
filters = ""
|
113
|
+
filters << "Owner Name: '#{Sigh.config[:cert_owner_name]}' " if Sigh.config[:cert_owner_name]
|
114
|
+
filters << "Certificate ID: '#{Sigh.config[:cert_id]}' " if Sigh.config[:cert_id]
|
115
|
+
Helper.log.info "No certificates for filter: #{filters}".yellow if filters.length > 0
|
116
|
+
raise "Could not find a matching code signing identity for #{profile_type}. You can use cert to generate one (https://github.com/fastlane/cert)".red
|
117
|
+
end
|
118
|
+
|
119
|
+
return certificates.first
|
120
|
+
end
|
121
|
+
|
122
|
+
# Downloads and stores the provisioning profile
|
123
|
+
def download_profile(profile)
|
124
|
+
Helper.log.info "Downloading provisioning profile...".yellow
|
125
|
+
profile_name ||= "#{profile.class.pretty_type}_#{Sigh.config[:app_identifier]}.mobileprovision" # default name
|
126
|
+
profile_name += '.mobileprovision' unless profile_name.include?'mobileprovision'
|
127
|
+
|
128
|
+
output_path = File.join('/tmp', profile_name)
|
129
|
+
dataWritten = File.open(output_path, "wb") do |f|
|
130
|
+
f.write(profile.download)
|
131
|
+
end
|
132
|
+
|
133
|
+
Helper.log.info "Successfully downloaded provisioning profile...".green
|
134
|
+
return output_path
|
135
|
+
end
|
136
|
+
|
137
|
+
# Store the profile ID into the environment
|
138
|
+
def store_provisioning_id_in_environment(path)
|
139
|
+
require 'sigh/profile_analyser'
|
140
|
+
udid = Sigh::ProfileAnalyser.run(path)
|
141
|
+
ENV["SIGH_UDID"] = udid if udid
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
data/lib/sigh/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sigh
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Felix Krause
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-06-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fastlane_core
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.7.
|
19
|
+
version: 0.7.6
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.7.
|
26
|
+
version: 0.7.6
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: plist
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ~>
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 3.1.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: spaceship
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.0.8
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.0.8
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: bundler
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -151,13 +165,12 @@ files:
|
|
151
165
|
- lib/assets/resign.sh
|
152
166
|
- lib/sigh.rb
|
153
167
|
- lib/sigh/dependency_checker.rb
|
154
|
-
- lib/sigh/developer_center.rb
|
155
|
-
- lib/sigh/developer_center_signing.rb
|
156
168
|
- lib/sigh/local_manage.rb
|
157
169
|
- lib/sigh/manager.rb
|
158
170
|
- lib/sigh/options.rb
|
159
171
|
- lib/sigh/profile_analyser.rb
|
160
172
|
- lib/sigh/resign.rb
|
173
|
+
- lib/sigh/spaceship/runner.rb
|
161
174
|
- lib/sigh/version.rb
|
162
175
|
homepage: https://fastlane.tools
|
163
176
|
licenses:
|
@@ -179,7 +192,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
179
192
|
version: '0'
|
180
193
|
requirements: []
|
181
194
|
rubyforge_project:
|
182
|
-
rubygems_version: 2.
|
195
|
+
rubygems_version: 2.4.7
|
183
196
|
signing_key:
|
184
197
|
specification_version: 4
|
185
198
|
summary: Because you would rather spend your time building stuff than fighting provisioning
|
@@ -1,292 +0,0 @@
|
|
1
|
-
require 'fastlane_core/developer_center/developer_center'
|
2
|
-
require 'sigh/developer_center_signing'
|
3
|
-
|
4
|
-
module Sigh
|
5
|
-
class DeveloperCenter < FastlaneCore::DeveloperCenter
|
6
|
-
# Types of certificates
|
7
|
-
APPSTORE = "AppStore"
|
8
|
-
ADHOC = "AdHoc"
|
9
|
-
DEVELOPMENT = "Development"
|
10
|
-
|
11
|
-
PROFILES_URL_DEV = "https://developer.apple.com/account/ios/profile/profileList.action?type=limited"
|
12
|
-
|
13
|
-
def run
|
14
|
-
@type = Sigh::DeveloperCenter::APPSTORE
|
15
|
-
@type = Sigh::DeveloperCenter::ADHOC if Sigh.config[:adhoc]
|
16
|
-
@type = Sigh::DeveloperCenter::DEVELOPMENT if Sigh.config[:development]
|
17
|
-
|
18
|
-
cert = maintain_app_certificate # create/download the certificate
|
19
|
-
|
20
|
-
if @type == APPSTORE # both enterprise and App Store
|
21
|
-
type_name = "Distribution"
|
22
|
-
elsif @type == ADHOC
|
23
|
-
type_name = "AdHoc"
|
24
|
-
else
|
25
|
-
type_name = "Development"
|
26
|
-
end
|
27
|
-
cert_name ||= "#{type_name}_#{Sigh.config[:app_identifier]}.mobileprovision" # default name
|
28
|
-
cert_name += '.mobileprovision' unless cert_name.include?'mobileprovision'
|
29
|
-
|
30
|
-
output_path = File.join(TMP_FOLDER, cert_name)
|
31
|
-
dataWritten = File.open(output_path, "wb") do |f|
|
32
|
-
f.write(cert)
|
33
|
-
end
|
34
|
-
|
35
|
-
store_provisioning_id_in_environment(output_path)
|
36
|
-
|
37
|
-
return output_path
|
38
|
-
end
|
39
|
-
|
40
|
-
def store_provisioning_id_in_environment(path)
|
41
|
-
require 'sigh/profile_analyser'
|
42
|
-
udid = Sigh::ProfileAnalyser.run(path)
|
43
|
-
ENV["SIGH_UDID"] = udid if udid
|
44
|
-
end
|
45
|
-
|
46
|
-
def maintain_app_certificate(force = nil)
|
47
|
-
force = Sigh.config[:force] if (force == nil)
|
48
|
-
begin
|
49
|
-
if @type == DEVELOPMENT
|
50
|
-
visit PROFILES_URL_DEV
|
51
|
-
else
|
52
|
-
visit PROFILES_URL
|
53
|
-
end
|
54
|
-
|
55
|
-
@list_certs_url = wait_for_variable('profileDataURL')
|
56
|
-
# list_certs_url will look like this: "https://developer.apple.com/services-account/..../account/ios/profile/listProvisioningProfiles.action?content-type=application/x-www-form-urlencoded&accept=application/json&requestId=id&userLocale=en_US&teamId=xy&includeInactiveProfiles=true&onlyCountLists=true"
|
57
|
-
Helper.log.info "Fetching all available provisioning profiles..."
|
58
|
-
|
59
|
-
has_all_profiles = false
|
60
|
-
page_index = 1
|
61
|
-
page_size = 500
|
62
|
-
|
63
|
-
until has_all_profiles do
|
64
|
-
bundle_id = Sigh.config[:app_identifier]
|
65
|
-
|
66
|
-
certs = post_ajax(@list_certs_url, "{pageNumber: #{page_index}, pageSize: #{page_size}, sort: 'name%3dasc', search: ''}")
|
67
|
-
|
68
|
-
if certs
|
69
|
-
profile_name = Sigh.config[:provisioning_name]
|
70
|
-
|
71
|
-
profile_count = certs['provisioningProfiles'].count
|
72
|
-
|
73
|
-
Helper.log.info "Checking if profile is available. (#{profile_count} profiles found on page #{page_index})"
|
74
|
-
required_cert_types = (@type == DEVELOPMENT ? ['iOS Development'] : ['iOS Distribution', 'iOS UniversalDistribution'])
|
75
|
-
certs['provisioningProfiles'].each do |current_cert|
|
76
|
-
next unless required_cert_types.include?(current_cert['type'])
|
77
|
-
|
78
|
-
details = profile_details(current_cert['provisioningProfileId'])
|
79
|
-
|
80
|
-
if details['provisioningProfile']['appId']['identifier'] == bundle_id
|
81
|
-
|
82
|
-
next if profile_name && details['provisioningProfile']['name'] != profile_name
|
83
|
-
|
84
|
-
# that's an Ad Hoc profile. I didn't find a better way to detect if it's one ... skipping it
|
85
|
-
next if @type == APPSTORE && details['provisioningProfile']['deviceCount'] > 0
|
86
|
-
|
87
|
-
# that's an App Store profile ... skipping it
|
88
|
-
next if @type != APPSTORE && details['provisioningProfile']['deviceCount'] == 0
|
89
|
-
|
90
|
-
# We found the correct certificate
|
91
|
-
if force
|
92
|
-
renew_profile(current_cert['provisioningProfileId']) # This one needs to be forcefully renewed
|
93
|
-
return maintain_app_certificate(false) # recursive
|
94
|
-
elsif current_cert['status'] == 'Active'
|
95
|
-
return download_profile(details['provisioningProfile']['provisioningProfileId']) # this one is already finished. Just download it.
|
96
|
-
elsif ['Expired', 'Invalid'].include? current_cert['status']
|
97
|
-
# Broken profile
|
98
|
-
begin
|
99
|
-
renew_profile(current_cert['provisioningProfileId']) # This one needs to be renewed
|
100
|
-
return maintain_app_certificate(false) # recursive
|
101
|
-
rescue
|
102
|
-
# Something went wrong, just create a new one instead
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
break
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
if page_size <= profile_count
|
111
|
-
page_index += 1
|
112
|
-
else
|
113
|
-
has_all_profiles = true
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
Helper.log.info "Could not find existing profile. Trying to create a new one."
|
119
|
-
# Certificate does not exist yet, we need to create a new one
|
120
|
-
create_profile
|
121
|
-
# After creating the profile, we need to download it
|
122
|
-
return maintain_app_certificate(false) # recursive
|
123
|
-
|
124
|
-
rescue => ex
|
125
|
-
error_occured(ex)
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
def create_profile
|
130
|
-
app_identifier = Sigh.config[:app_identifier]
|
131
|
-
Helper.log.info "Creating new profile for app '#{app_identifier}' for type '#{@type}'.".yellow
|
132
|
-
certificates = code_signing_certificates(@type)
|
133
|
-
|
134
|
-
create_url = "https://developer.apple.com/account/ios/profile/profileCreate.action"
|
135
|
-
visit create_url
|
136
|
-
|
137
|
-
# 1) Select the profile type (AppStore, Adhoc)
|
138
|
-
enterprise = false
|
139
|
-
|
140
|
-
begin
|
141
|
-
wait_for_elements('#type-production')
|
142
|
-
rescue => ex
|
143
|
-
wait_for_elements('#type-inhouse') # enterprise accounts
|
144
|
-
enterprise = true
|
145
|
-
end
|
146
|
-
|
147
|
-
value = enterprise ? 'inhouse' : 'store'
|
148
|
-
value = 'limited' if @type == DEVELOPMENT
|
149
|
-
value = 'adhoc' if @type == ADHOC
|
150
|
-
|
151
|
-
first(:xpath, "//input[@type='radio' and @value='#{value}']").click
|
152
|
-
click_next
|
153
|
-
|
154
|
-
# 2) Select the App ID
|
155
|
-
sleep 1 while !page.has_content? "Select App ID"
|
156
|
-
# example: <option value="RGAWZGXSY4">ABP (5A997XSHK2.net.sunapps.34)</option>
|
157
|
-
identifiers = all(:xpath, "//option[contains(text(), '.#{app_identifier})')]")
|
158
|
-
if identifiers.count == 0
|
159
|
-
puts "Couldn't find App ID '#{app_identifier}'\nonly found the following bundle identifiers:".red
|
160
|
-
all(:xpath, "//option").each do |current|
|
161
|
-
puts "\t- #{current.text}".yellow
|
162
|
-
end
|
163
|
-
raise "Could not find App ID '#{app_identifier}'.".red
|
164
|
-
else
|
165
|
-
identifiers.first.select_option
|
166
|
-
end
|
167
|
-
click_next
|
168
|
-
|
169
|
-
# 3) Select the certificate
|
170
|
-
sleep 1 while !page.has_content? "Select certificates"
|
171
|
-
sleep 3
|
172
|
-
Helper.log.info "Using certificates: #{certificates.map { |c| "#{c['ownerName']} (#{c['certificateId']})" } }"
|
173
|
-
|
174
|
-
# example: <input type="radio" name="certificateIds" class="validate" value="[XC5PH8D47H]"> (production)
|
175
|
-
|
176
|
-
clicked = false
|
177
|
-
certificates.each do |cert|
|
178
|
-
cert_id = cert['certificateId']
|
179
|
-
input = if @type == DEVELOPMENT
|
180
|
-
# development uses a checkbox and has no [] around the value
|
181
|
-
first(:xpath, "//input[@type='checkbox' and @value='#{cert_id}']")
|
182
|
-
else
|
183
|
-
break if clicked
|
184
|
-
# production uses radio and has a [] around the value
|
185
|
-
first(:xpath, "//input[@type='radio' and @value='[#{cert_id}]']")
|
186
|
-
end
|
187
|
-
if input
|
188
|
-
input.click
|
189
|
-
clicked = true
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
if !clicked
|
194
|
-
raise "Could not find certificate in the list of available certificates."
|
195
|
-
end
|
196
|
-
click_next
|
197
|
-
|
198
|
-
if @type != APPSTORE
|
199
|
-
# 4) Devices selection
|
200
|
-
wait_for_elements('.selectAll.column')
|
201
|
-
sleep 3
|
202
|
-
|
203
|
-
first(:xpath, "//div[@class='selectAll column']/input").click # select all the devices
|
204
|
-
click_next
|
205
|
-
end
|
206
|
-
|
207
|
-
# 5) Choose a profile name
|
208
|
-
wait_for_elements('.distributionType')
|
209
|
-
profile_name = Sigh.config[:provisioning_name]
|
210
|
-
profile_name ||= [app_identifier, @type].join(' ')
|
211
|
-
fill_in "provisioningProfileName", with: profile_name
|
212
|
-
click_next
|
213
|
-
|
214
|
-
if page.has_content?"Multiple profiles found with the name"
|
215
|
-
fill_in "provisioningProfileName", with: (profile_name + " sigh")
|
216
|
-
click_next
|
217
|
-
end
|
218
|
-
|
219
|
-
wait_for_elements('.row-details')
|
220
|
-
end
|
221
|
-
|
222
|
-
def renew_profile(profile_id)
|
223
|
-
certificate = code_signing_certificates(@type).first
|
224
|
-
|
225
|
-
details_url = "https://developer.apple.com/account/ios/profile/profileEdit.action?type=&provisioningProfileId=#{profile_id}"
|
226
|
-
Helper.log.info "Renewing provisioning profile '#{profile_id}' using URL '#{details_url}'"
|
227
|
-
visit details_url
|
228
|
-
|
229
|
-
Helper.log.info "Using certificate ID '#{certificate['certificateId']}' from '#{certificate['ownerName']}'"
|
230
|
-
wait_for_elements('.selectCertificates')
|
231
|
-
|
232
|
-
certs = all(:xpath, "//input[@type='radio' and @value='#{certificate["certificateId"]}']")
|
233
|
-
if certs.count == 1
|
234
|
-
certs.first.click
|
235
|
-
|
236
|
-
if @type != APPSTORE
|
237
|
-
# Add all devices
|
238
|
-
wait_for_elements('.selectAll.column')
|
239
|
-
sleep 3
|
240
|
-
unless first(:xpath, "//div[@class='selectAll column']/input")["checked"]
|
241
|
-
first(:xpath, "//div[@class='selectAll column']/input").click # select all the devices
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
click_next
|
246
|
-
|
247
|
-
wait_for_elements('.row-details')
|
248
|
-
click_on "Done"
|
249
|
-
else
|
250
|
-
if @type != APPSTORE
|
251
|
-
# Add all devices
|
252
|
-
wait_for_elements('.selectAll.column')
|
253
|
-
sleep 3
|
254
|
-
unless all(:xpath, "//div[@class='selectAll column']/input").last["checked"]
|
255
|
-
all(:xpath, "//div[@class='selectAll column']/input").last.click # select all the devices
|
256
|
-
end
|
257
|
-
click_next
|
258
|
-
|
259
|
-
wait_for_elements('.row-details')
|
260
|
-
click_on "Done"
|
261
|
-
else
|
262
|
-
Helper.log.info "Looking for certificate: #{certificate}."
|
263
|
-
raise "Could not find certificate in the list of available certificates."
|
264
|
-
end
|
265
|
-
end
|
266
|
-
end
|
267
|
-
|
268
|
-
def download_profile(profile_id)
|
269
|
-
download_cert_url = "/account/ios/profile/profileContentDownload.action?displayId=#{profile_id}"
|
270
|
-
|
271
|
-
return download_file(download_cert_url)
|
272
|
-
end
|
273
|
-
|
274
|
-
|
275
|
-
private
|
276
|
-
def profile_details(profile_id)
|
277
|
-
# We need to build the URL to get the App ID for a specific certificate
|
278
|
-
current_profile_url = @list_certs_url.gsub('listProvisioningProfiles', 'getProvisioningProfile')
|
279
|
-
current_profile_url += "&provisioningProfileId=#{profile_id}"
|
280
|
-
# Helper.log.debug "Fetching URL: '#{current_profile_url}'"
|
281
|
-
|
282
|
-
result = post_ajax(current_profile_url)
|
283
|
-
# Example response, see bottom of file
|
284
|
-
|
285
|
-
if result['resultCode'] == 0
|
286
|
-
return result
|
287
|
-
else
|
288
|
-
raise "Error fetching details for provisioning profile '#{profile_id}'".red
|
289
|
-
end
|
290
|
-
end
|
291
|
-
end
|
292
|
-
end
|
@@ -1,88 +0,0 @@
|
|
1
|
-
module Sigh
|
2
|
-
class DeveloperCenter < FastlaneCore::DeveloperCenter
|
3
|
-
# Returns a array of hashes, that contains information about the iOS certificate
|
4
|
-
# @example
|
5
|
-
# [{"certRequestId"=>"B23Q2P396B",
|
6
|
-
# "name"=>"SunApps GmbH",
|
7
|
-
# "statusString"=>"Issued",
|
8
|
-
# "expirationDate"=>"2015-11-25T22:45:50Z",
|
9
|
-
# "expirationDateString"=>"Nov 25, 2015",
|
10
|
-
# "ownerType"=>"team",
|
11
|
-
# "ownerName"=>"SunApps GmbH",
|
12
|
-
# "ownerId"=>"....",
|
13
|
-
# "canDownload"=>true,
|
14
|
-
# "canRevoke"=>true,
|
15
|
-
# "certificateId"=>"....",
|
16
|
-
# "certificateStatusCode"=>0,
|
17
|
-
# "certRequestStatusCode"=>4,
|
18
|
-
# "certificateTypeDisplayId"=>"...",
|
19
|
-
# "serialNum"=>"....",
|
20
|
-
# "typeString"=>"iOS Distribution"},
|
21
|
-
# {another sertificate...}]
|
22
|
-
def code_signing_certificates(type)
|
23
|
-
certs_url = "https://developer.apple.com/account/ios/certificate/certificateList.action?type="
|
24
|
-
certs_url << (type == DEVELOPMENT ? 'development' : 'distribution')
|
25
|
-
visit certs_url
|
26
|
-
|
27
|
-
certificateDataURL = wait_for_variable('certificateDataURL')
|
28
|
-
certificateRequestTypes = wait_for_variable('certificateRequestTypes')
|
29
|
-
certificateStatuses = wait_for_variable('certificateStatuses')
|
30
|
-
|
31
|
-
url = [certificateDataURL, certificateRequestTypes, certificateStatuses].join('')
|
32
|
-
|
33
|
-
# https://developer.apple.com/services-account/.../account/ios/certificate/listCertRequests.action?content-type=application/x-www-form-urlencoded&accept=application/json&requestId=...&userLocale=en_US&teamId=...&types=...&status=4&certificateStatus=0&type=distribution
|
34
|
-
|
35
|
-
certs = post_ajax(url, "{pageNumber: 1, pageSize: 500, sort: 'name%3dasc'}")['certRequests']
|
36
|
-
|
37
|
-
ret_certs = []
|
38
|
-
|
39
|
-
# Select certificate
|
40
|
-
certificate_name = Sigh.config[:cert_owner_name]
|
41
|
-
cert_date = Sigh.config[:cert_date]
|
42
|
-
cert_id = Sigh.config[:cert_id]
|
43
|
-
|
44
|
-
# The other profiles are push profiles
|
45
|
-
certificate_type = type == DEVELOPMENT ? 'iOS Development' : 'iOS Distribution'
|
46
|
-
|
47
|
-
# New profiles first
|
48
|
-
certs.sort! do |a, b|
|
49
|
-
Time.parse(b['expirationDate']) <=> Time.parse(a['expirationDate'])
|
50
|
-
end
|
51
|
-
|
52
|
-
certs.each do |current_cert|
|
53
|
-
next unless current_cert['typeString'] == certificate_type
|
54
|
-
|
55
|
-
if cert_date || certificate_name || cert_id
|
56
|
-
if current_cert['expirationDateString'] == cert_date
|
57
|
-
Helper.log.info "Certificate ID '#{current_cert['certificateId']}' with expiry date '#{current_cert['expirationDateString']}' located".green
|
58
|
-
ret_certs << current_cert
|
59
|
-
end
|
60
|
-
|
61
|
-
if current_cert['name'] == certificate_name
|
62
|
-
Helper.log.info "Certificate ID '#{current_cert['certificateId']}' with name '#{certificate_name}' located".green
|
63
|
-
ret_certs << current_cert
|
64
|
-
end
|
65
|
-
|
66
|
-
if current_cert['certificateId'] == cert_id
|
67
|
-
Helper.log.info "Certificate ID '#{current_cert['certificateId']}' with name '#{current_cert['name']}' located".green
|
68
|
-
ret_certs << current_cert
|
69
|
-
end
|
70
|
-
else
|
71
|
-
ret_certs << current_cert
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
return ret_certs unless ret_certs.empty?
|
76
|
-
|
77
|
-
predicates = []
|
78
|
-
predicates << "name: #{certificate_name}" if certificate_name
|
79
|
-
predicates << "expiry date: #{cert_date}" if cert_date
|
80
|
-
predicates << "certificate ID: #{cert_id}" if cert_id
|
81
|
-
predicates << "type: #{(type == DEVELOPMENT ? 'development' : 'distribution')}"
|
82
|
-
|
83
|
-
predicates_str = " with #{predicates.join(', ')}"
|
84
|
-
|
85
|
-
raise "Could not find a Certificate#{predicates_str}. Please open #{current_url} and make sure you have a signing profile created, which matches the given filters".red
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|