sigh 0.4.10 → 0.5.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 +14 -0
- data/bin/sigh +16 -1
- data/lib/assets/resign.sh +1 -1
- data/lib/sigh.rb +0 -1
- data/lib/sigh/developer_center.rb +83 -37
- data/lib/sigh/local_manage.rb +121 -0
- data/lib/sigh/manager.rb +9 -2
- data/lib/sigh/options.rb +1 -1
- data/lib/sigh/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a8e062edc21ba3bd0acf86486468c8baab7c1403
|
4
|
+
data.tar.gz: 4de0a9955ef3f4dfe42da0859d3fdd3929d26ac0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8af5e581a46bf4d30d366924faba7b4ffb473fbaa1cb8b8ba43cf1527f1542210c508da12c7b9a118cada96ac2b931e04a57da452e42e04cbe60728965c8196f
|
7
|
+
data.tar.gz: 0ebf33635e3efa1e5c51ffbd425d64ab5a5a8a604d63d1e62e9ce4959c8c0f137525578c2bb06aaa0eaa889f0382adc09d1058901130ff46f470886f8f08e95f
|
data/README.md
CHANGED
@@ -147,6 +147,20 @@ You can pass more information using the command line:
|
|
147
147
|
|
148
148
|
sigh resign ./path/app.ipa -i "iPhone Distribution: Felix Krause" -p "my.mobileprovision"
|
149
149
|
|
150
|
+
# Manage
|
151
|
+
|
152
|
+
With `sigh manage` you can list all provisioning profiles installed locally.
|
153
|
+
|
154
|
+
sigh manage
|
155
|
+
|
156
|
+
Delete all expired provisioning profiles
|
157
|
+
|
158
|
+
sigh manage -e
|
159
|
+
|
160
|
+
Or delete all `iOS Team Provisioning Profile` by using a regular expression
|
161
|
+
|
162
|
+
sigh manage -p "iOS\ ?Team Provisioning Profile:"
|
163
|
+
|
150
164
|
## Environment Variables
|
151
165
|
In case you prefer environment variables:
|
152
166
|
|
data/bin/sigh
CHANGED
@@ -8,6 +8,7 @@ require 'credentials_manager/password_manager'
|
|
8
8
|
require 'credentials_manager/appfile_config'
|
9
9
|
require 'sigh/options'
|
10
10
|
require 'sigh/manager'
|
11
|
+
require 'sigh/local_manage'
|
11
12
|
|
12
13
|
HighLine.track_eof = false
|
13
14
|
|
@@ -20,7 +21,7 @@ class SighApplication
|
|
20
21
|
program :help, 'Author', 'Felix Krause <sigh@krausefx.com>'
|
21
22
|
program :help, 'Website', 'https://fastlane.tools'
|
22
23
|
program :help, 'GitHub', 'https://github.com/krausefx/sigh'
|
23
|
-
program :help_formatter, :compact
|
24
|
+
# program :help_formatter, :compact # https://github.com/commander-rb/commander/issues/12
|
24
25
|
|
25
26
|
always_trace!
|
26
27
|
|
@@ -47,6 +48,20 @@ class SighApplication
|
|
47
48
|
end
|
48
49
|
end
|
49
50
|
|
51
|
+
command :manage do |c|
|
52
|
+
c.syntax = 'sigh manage'
|
53
|
+
c.description = 'Manage installed provisioning profiles on your system.'
|
54
|
+
|
55
|
+
c.option '-e', '--clean_expired', 'Remove all expired provisioning profiles.'
|
56
|
+
|
57
|
+
c.option '-p', '--clean_pattern STRING', String, 'Remove any provisioning profiles that matches the regular expression.'
|
58
|
+
c.example 'Remove all "iOS Team Provisioning" provisioning profiles', 'sigh manage -p "iOS\ ?Team Provisioning Profile"'
|
59
|
+
|
60
|
+
c.action do |args, options|
|
61
|
+
Sigh::LocalManage.start(options, args)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
50
65
|
default_command :renew
|
51
66
|
|
52
67
|
run!
|
data/lib/assets/resign.sh
CHANGED
@@ -293,7 +293,7 @@ then
|
|
293
293
|
echo "Resigning embedded frameworks using certificate: '$CERTIFICATE'" >&2
|
294
294
|
for framework in "$FRAMEWORKS_DIR"/*
|
295
295
|
do
|
296
|
-
if [[ "$framework" == *.framework ]]
|
296
|
+
if [[ "$framework" == *.framework || "$framework" == *.dylib ]]
|
297
297
|
then
|
298
298
|
/usr/bin/codesign -f -s "$CERTIFICATE" "$framework"
|
299
299
|
checkStatus
|
data/lib/sigh.rb
CHANGED
@@ -17,8 +17,13 @@ module Sigh
|
|
17
17
|
|
18
18
|
cert = maintain_app_certificate # create/download the certificate
|
19
19
|
|
20
|
-
|
21
|
-
|
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
|
22
27
|
cert_name ||= "#{type_name}_#{Sigh.config[:app_identifier]}.mobileprovision" # default name
|
23
28
|
cert_name += '.mobileprovision' unless cert_name.include?'mobileprovision'
|
24
29
|
|
@@ -48,41 +53,63 @@ module Sigh
|
|
48
53
|
@list_certs_url = wait_for_variable('profileDataURL')
|
49
54
|
# 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"
|
50
55
|
Helper.log.info "Fetching all available provisioning profiles..."
|
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
|
-
|
56
|
+
|
57
|
+
has_all_profiles = false
|
58
|
+
page_index = 1
|
59
|
+
page_size = 500
|
60
|
+
|
61
|
+
until has_all_profiles do
|
62
|
+
bundle_id = Sigh.config[:app_identifier]
|
63
|
+
|
64
|
+
certs = post_ajax(@list_certs_url, "{pageNumber: #{page_index}, pageSize: #{page_size}, sort: 'name%3dasc', search: 'name%3D#{bundle_id}%26type%3D#{bundle_id}%26status%3D#{bundle_id}%26appId%3D#{bundle_id}'}")
|
65
|
+
|
66
|
+
if certs
|
67
|
+
profile_name = Sigh.config[:provisioning_name]
|
68
|
+
|
69
|
+
profile_count = certs['provisioningProfiles'].count
|
70
|
+
|
71
|
+
Helper.log.info "Checking if profile is available. (#{profile_count} profiles found on page #{page_index})"
|
72
|
+
required_cert_types = (@type == DEVELOPMENT ? ['iOS Development'] : ['iOS Distribution', 'iOS UniversalDistribution'])
|
73
|
+
certs['provisioningProfiles'].each do |current_cert|
|
74
|
+
next unless required_cert_types.include?(current_cert['type'])
|
75
|
+
|
76
|
+
details = profile_details(current_cert['provisioningProfileId'])
|
77
|
+
|
78
|
+
if details['provisioningProfile']['appId']['identifier'] == bundle_id
|
79
|
+
|
80
|
+
next if profile_name && details['provisioningProfile']['name'] != profile_name
|
81
|
+
|
82
|
+
# that's an Ad Hoc profile. I didn't find a better way to detect if it's one ... skipping it
|
83
|
+
next if @type == APPSTORE && details['provisioningProfile']['deviceCount'] > 0
|
84
|
+
|
85
|
+
# that's an App Store profile ... skipping it
|
86
|
+
next if @type != APPSTORE && details['provisioningProfile']['deviceCount'] == 0
|
87
|
+
|
88
|
+
# We found the correct certificate
|
89
|
+
if force
|
90
|
+
renew_profile(current_cert['provisioningProfileId']) # This one needs to be forcefully renewed
|
91
|
+
return maintain_app_certificate(false) # recursive
|
92
|
+
elsif current_cert['status'] == 'Active'
|
93
|
+
return download_profile(details['provisioningProfile']['provisioningProfileId']) # this one is already finished. Just download it.
|
94
|
+
elsif ['Expired', 'Invalid'].include? current_cert['status']
|
95
|
+
# Broken profile
|
96
|
+
begin
|
97
|
+
renew_profile(current_cert['provisioningProfileId']) # This one needs to be renewed
|
98
|
+
return maintain_app_certificate(false) # recursive
|
99
|
+
rescue
|
100
|
+
# Something went wrong, just create a new one instead
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
break
|
105
|
+
end
|
83
106
|
end
|
84
107
|
|
85
|
-
|
108
|
+
if page_size <= profile_count
|
109
|
+
page_index += 1
|
110
|
+
else
|
111
|
+
has_all_profiles = true
|
112
|
+
end
|
86
113
|
end
|
87
114
|
end
|
88
115
|
|
@@ -181,6 +208,12 @@ module Sigh
|
|
181
208
|
profile_name ||= [app_identifier, @type].join(' ')
|
182
209
|
fill_in "provisioningProfileName", with: profile_name
|
183
210
|
click_next
|
211
|
+
|
212
|
+
if page.has_content?"Multiple profiles found with the name"
|
213
|
+
fill_in "provisioningProfileName", with: (profile_name + " sigh")
|
214
|
+
click_next
|
215
|
+
end
|
216
|
+
|
184
217
|
wait_for_elements('.row-details')
|
185
218
|
end
|
186
219
|
|
@@ -212,8 +245,21 @@ module Sigh
|
|
212
245
|
wait_for_elements('.row-details')
|
213
246
|
click_on "Done"
|
214
247
|
else
|
215
|
-
|
216
|
-
|
248
|
+
if @type != APPSTORE
|
249
|
+
# Add all devices
|
250
|
+
wait_for_elements('.selectAll.column')
|
251
|
+
sleep 3
|
252
|
+
unless all(:xpath, "//div[@class='selectAll column']/input").last["checked"]
|
253
|
+
all(:xpath, "//div[@class='selectAll column']/input").last.click # select all the devices
|
254
|
+
end
|
255
|
+
click_next
|
256
|
+
|
257
|
+
wait_for_elements('.row-details')
|
258
|
+
click_on "Done"
|
259
|
+
else
|
260
|
+
Helper.log.info "Looking for certificate: #{certificate}."
|
261
|
+
raise "Could not find certificate in the list of available certificates."
|
262
|
+
end
|
217
263
|
end
|
218
264
|
end
|
219
265
|
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Sigh
|
2
|
+
class LocalManage
|
3
|
+
LIST = "list"
|
4
|
+
CLEANUP = "cleanup"
|
5
|
+
|
6
|
+
def self.start(options, args)
|
7
|
+
command, clean_expired, clean_pattern = get_inputs(options, args)
|
8
|
+
if command == LIST
|
9
|
+
list_profiles
|
10
|
+
elsif command == CLEANUP
|
11
|
+
cleanup_profiles(clean_expired, clean_pattern)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.install_profile(profile)
|
16
|
+
Helper.log.info "Installing provisioning profile..."
|
17
|
+
profile_path = File.expand_path("~") + "/Library/MobileDevice/Provisioning Profiles/"
|
18
|
+
profile_filename = ENV["SIGH_UDID"] + ".mobileprovision"
|
19
|
+
destination = profile_path + profile_filename
|
20
|
+
|
21
|
+
# If the directory doesn't exist, make it first
|
22
|
+
unless File.directory?(profile_path)
|
23
|
+
FileUtils.mkdir_p(profile_path)
|
24
|
+
end
|
25
|
+
|
26
|
+
# copy to Xcode provisioning profile directory
|
27
|
+
FileUtils.copy profile, destination
|
28
|
+
|
29
|
+
if File.exists? destination
|
30
|
+
Helper.log.info "Profile installed at \"#{destination}\""
|
31
|
+
else
|
32
|
+
raise "Failed installation of provisioning profile at location: #{destination}".red
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.get_inputs(options, args)
|
37
|
+
clean_expired = options.clean_expired
|
38
|
+
clean_pattern = /#{options.clean_pattern}/ if options.clean_pattern
|
39
|
+
command = (clean_expired != nil || clean_pattern != nil) ? CLEANUP : LIST
|
40
|
+
return command, clean_expired, clean_pattern
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.list_profiles
|
44
|
+
profiles = load_profiles
|
45
|
+
|
46
|
+
now = DateTime.now
|
47
|
+
soon = (Date.today + 30).to_datetime
|
48
|
+
|
49
|
+
profiles_valid = profiles.select { |profile| profile["ExpirationDate"] > now && profile["ExpirationDate"] > soon }
|
50
|
+
if profiles_valid.count > 0
|
51
|
+
Helper.log.info "Provisioning profiles installed"
|
52
|
+
Helper.log.info "Valid:"
|
53
|
+
profiles_valid.each do |profile|
|
54
|
+
Helper.log.info profile["Name"].green
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
profiles_soon = profiles.select { |profile| profile["ExpirationDate"] > now && profile["ExpirationDate"] < soon }
|
59
|
+
if profiles_soon.count > 0
|
60
|
+
Helper.log.info ""
|
61
|
+
Helper.log.info "Expiring within 30 day:"
|
62
|
+
profiles_soon.each do |profile|
|
63
|
+
Helper.log.info profile["Name"].yellow
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
profiles_expired = profiles.select { |profile| profile["ExpirationDate"] < now }
|
68
|
+
if profiles_expired.count > 0
|
69
|
+
Helper.log.info ""
|
70
|
+
Helper.log.info "Expired:"
|
71
|
+
profiles_expired.each do |profile|
|
72
|
+
Helper.log.info profile["Name"].red
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
Helper.log.info ""
|
77
|
+
Helper.log.info "Summary"
|
78
|
+
Helper.log.info "#{profiles.count} installed profiles"
|
79
|
+
Helper.log.info "#{profiles_expired.count} are expired".red
|
80
|
+
Helper.log.info "#{profiles_soon.count} are valid but will expire within 30 days".yellow
|
81
|
+
Helper.log.info "#{profiles_valid.count} are valid".green
|
82
|
+
|
83
|
+
Helper.log.info "You can remove all expired profiles using `sigh manage -e`" if profiles_expired.count > 0
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.cleanup_profiles(expired = false, pattern = nil)
|
87
|
+
now = DateTime.now
|
88
|
+
|
89
|
+
profiles = load_profiles.select { |profile| (expired && profile["ExpirationDate"] < now) || (pattern != nil && profile["Name"] =~ pattern) }
|
90
|
+
|
91
|
+
Helper.log.info "The following provisioning profiles are either expired or matches your pattern:"
|
92
|
+
profiles.each do |profile|
|
93
|
+
Helper.log.info profile["Name"].red
|
94
|
+
end
|
95
|
+
|
96
|
+
if agree("Delete these provisioning profiles #{profiles.length}? (y/n) ", true)
|
97
|
+
profiles.each do |profile|
|
98
|
+
File.delete profile["Path"]
|
99
|
+
end
|
100
|
+
Helper.log.info "\n\nDeleted #{profiles.length} profiles".green
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.load_profiles
|
105
|
+
Helper.log.info "Loading Provisioning profiles from ~/Library/MobileDevice/Provisioning Profiles/"
|
106
|
+
profiles_path = File.expand_path("~") + "/Library/MobileDevice/Provisioning Profiles/*.mobileprovision"
|
107
|
+
profile_paths = Dir[profiles_path]
|
108
|
+
|
109
|
+
profiles = []
|
110
|
+
profile_paths.each do |profile_path|
|
111
|
+
profile = Plist::parse_xml(`security cms -D -i '#{profile_path}'`)
|
112
|
+
profile['Path'] = profile_path
|
113
|
+
profiles << profile
|
114
|
+
end
|
115
|
+
|
116
|
+
profiles = profiles.sort_by {|profile| profile["Name"].downcase}
|
117
|
+
|
118
|
+
return profiles
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
data/lib/sigh/manager.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'plist'
|
2
|
+
|
1
3
|
module Sigh
|
2
4
|
class Manager
|
3
5
|
def self.start
|
@@ -27,8 +29,13 @@ module Sigh
|
|
27
29
|
profile_filename = ENV["SIGH_UDID"] + ".mobileprovision"
|
28
30
|
destination = profile_path + profile_filename
|
29
31
|
|
32
|
+
# If the directory doesn't exist, make it first
|
33
|
+
unless File.directory?(profile_path)
|
34
|
+
FileUtils.mkdir_p(profile_path)
|
35
|
+
end
|
36
|
+
|
30
37
|
# copy to Xcode provisioning profile directory
|
31
|
-
FileUtils.copy profile, destination
|
38
|
+
(FileUtils.copy profile, destination rescue nil) # if the directory doesn't exist yet
|
32
39
|
|
33
40
|
if File.exists? destination
|
34
41
|
Helper.log.info "Profile installed at \"#{destination}\""
|
@@ -37,4 +44,4 @@ module Sigh
|
|
37
44
|
end
|
38
45
|
end
|
39
46
|
end
|
40
|
-
end
|
47
|
+
end
|
data/lib/sigh/options.rb
CHANGED
@@ -18,7 +18,7 @@ module Sigh
|
|
18
18
|
FastlaneCore::ConfigItem.new(key: :development,
|
19
19
|
env_name: "SIGH_DEVELOPMENT",
|
20
20
|
description: "Renew the development certificate instead of the production one",
|
21
|
-
is_string: false,
|
21
|
+
is_string: false,
|
22
22
|
default_value: false),
|
23
23
|
FastlaneCore::ConfigItem.new(key: :force,
|
24
24
|
env_name: "SIGH_FORCE",
|
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.5.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-05-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fastlane_core
|
@@ -153,6 +153,7 @@ files:
|
|
153
153
|
- lib/sigh/dependency_checker.rb
|
154
154
|
- lib/sigh/developer_center.rb
|
155
155
|
- lib/sigh/developer_center_signing.rb
|
156
|
+
- lib/sigh/local_manage.rb
|
156
157
|
- lib/sigh/manager.rb
|
157
158
|
- lib/sigh/options.rb
|
158
159
|
- lib/sigh/profile_analyser.rb
|