sigh 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|