cupertino 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/README.md +8 -2
- data/bin/ios +0 -2
- data/lib/cupertino/provisioning_portal/agent.rb +86 -5
- data/lib/cupertino/provisioning_portal/commands/certificates.rb +20 -0
- data/lib/cupertino/provisioning_portal/commands/profiles.rb +38 -0
- data/lib/cupertino/provisioning_portal/commands.rb +6 -8
- data/lib/cupertino/provisioning_portal.rb +28 -11
- data/lib/cupertino.rb +1 -1
- metadata +73 -120
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -51,6 +51,12 @@ $ ios profiles:list
|
|
51
51
|
+----------------------------------+--------------+---------+
|
52
52
|
```
|
53
53
|
|
54
|
+
```sh
|
55
|
+
$ ios profiles:manage:devices
|
56
|
+
```
|
57
|
+
|
58
|
+
_Opens an editor with a list of devices, each of which can be commented or uncommented to turn them off or on for that provisioning profile._
|
59
|
+
|
54
60
|
### App IDs
|
55
61
|
|
56
62
|
```sh
|
@@ -92,12 +98,12 @@ _Crossed out commands are not yet implemented_
|
|
92
98
|
- ~~`devices:remove`~~
|
93
99
|
|
94
100
|
- `profiles:list`
|
101
|
+
- `profiles:manage:devices`
|
95
102
|
- ~~`profiles:add`~~
|
96
103
|
- ~~`profiles:edit`~~
|
97
104
|
|
98
105
|
- `certificates:list [development|distribution]`
|
99
|
-
-
|
100
|
-
- ~~`certificates:download CERTIFICATE_NAME`~~
|
106
|
+
- `certificates:download CERTIFICATE_NAME`
|
101
107
|
- ~~`certificates:revoke CERTIFICATE_NAME`~~
|
102
108
|
|
103
109
|
- `app_ids:list`
|
data/bin/ios
CHANGED
@@ -10,13 +10,17 @@ module Cupertino
|
|
10
10
|
super
|
11
11
|
self.user_agent_alias = 'Mac Safari'
|
12
12
|
|
13
|
-
@username, @password = Netrc.read[
|
13
|
+
@username, @password = Netrc.read[Cupertino::HOSTNAME]
|
14
14
|
end
|
15
15
|
|
16
16
|
def get(uri, parameters = [], referer = nil, headers = {})
|
17
|
+
uri = ::File.join("https://#{Cupertino::HOSTNAME}", uri) unless /^https?/ === uri
|
18
|
+
|
17
19
|
3.times do
|
18
20
|
super(uri, parameters, referer, headers)
|
19
21
|
|
22
|
+
return page unless page.respond_to?(:title)
|
23
|
+
|
20
24
|
case page.title
|
21
25
|
when %r{Sign in with your Apple ID}
|
22
26
|
login! and redo
|
@@ -46,6 +50,7 @@ module Cupertino
|
|
46
50
|
page.parser.xpath('//div[@class="nt_multi"]/table/tbody/tr').each do |row|
|
47
51
|
certificate = Certificate.new
|
48
52
|
certificate.name = row.at_xpath('td[@class="name"]//p/text()').to_s.strip rescue nil
|
53
|
+
certificate.type = type
|
49
54
|
certificate.provisioning_profiles = row.at_xpath('td[@class="profiles"]/text()').to_s.strip.split(/\n+/) rescue []
|
50
55
|
certificate.expiration_date = row.at_xpath('td[@class="date"]/text()').to_s.strip rescue nil
|
51
56
|
certificate.status = row.at_xpath('td[@class="status"]/text()').to_s.strip rescue nil
|
@@ -54,6 +59,25 @@ module Cupertino
|
|
54
59
|
certificates
|
55
60
|
end
|
56
61
|
|
62
|
+
def download_certificate(certificate)
|
63
|
+
list_certificates(certificate.type)
|
64
|
+
|
65
|
+
page.parser.xpath('//div[@class="nt_multi"]/table/tbody/tr').each do |row|
|
66
|
+
name = row.at_xpath('td[@class="name"]//p/text()').to_s.strip rescue nil
|
67
|
+
|
68
|
+
if name == certificate.name
|
69
|
+
download_url = row.at_xpath('td[@class="last"]/a')['href'].to_s.strip rescue nil
|
70
|
+
|
71
|
+
self.pluggable_parser.default = Mechanize::Download
|
72
|
+
download = get(download_url)
|
73
|
+
download.save
|
74
|
+
return download.filename
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
return nil
|
79
|
+
end
|
80
|
+
|
57
81
|
def list_devices
|
58
82
|
get("https://developer.apple.com/ios/manage/devices/index.action")
|
59
83
|
|
@@ -65,7 +89,6 @@ module Cupertino
|
|
65
89
|
devices << device
|
66
90
|
end
|
67
91
|
|
68
|
-
|
69
92
|
if message = page.parser.at_xpath('//p[@class="devicesannounce"]/strong/text()').to_s.strip rescue nil
|
70
93
|
number_of_devices_available = message.scan(/\d{1,3}/).first.to_i
|
71
94
|
number_of_devices_available.times do
|
@@ -114,7 +137,8 @@ module Cupertino
|
|
114
137
|
profiles = []
|
115
138
|
page.parser.xpath('//fieldset[@id="fs-0"]/table/tbody/tr').each do |row|
|
116
139
|
profile = ProvisioningProfile.new
|
117
|
-
profile.name = row.at_xpath('td[@class="profile"]
|
140
|
+
profile.name = row.at_xpath('td[@class="profile"]//text()').to_s.strip rescue nil
|
141
|
+
profile.type = type
|
118
142
|
profile.app_id = row.at_xpath('td[@class="appid"]/text()').to_s.strip rescue nil
|
119
143
|
profile.status = row.at_xpath('td[@class="statusXcode"]/text()').to_s.strip rescue nil
|
120
144
|
profiles << profile
|
@@ -122,6 +146,63 @@ module Cupertino
|
|
122
146
|
profiles
|
123
147
|
end
|
124
148
|
|
149
|
+
def manage_devices_for_profile(profile)
|
150
|
+
raise ArgumentError unless block_given?
|
151
|
+
|
152
|
+
list_profiles(profile.type)
|
153
|
+
|
154
|
+
modify_url = nil
|
155
|
+
page.parser.xpath('//fieldset[@id="fs-0"]/table/tbody/tr').each do |row|
|
156
|
+
break if modify_url
|
157
|
+
|
158
|
+
name = row.at_xpath('td[@class="profile"]//text()').to_s.strip rescue nil
|
159
|
+
|
160
|
+
if name == profile.name
|
161
|
+
row.css('td.action a').each do |a|
|
162
|
+
if /edit\.action/ === a['href']
|
163
|
+
modify_url = a['href']
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
if modify_url
|
170
|
+
get(modify_url)
|
171
|
+
|
172
|
+
on, off = [], []
|
173
|
+
page.parser.css('.checkboxlist.last td').each do |td|
|
174
|
+
checkbox = td.at_xpath('input[@type="checkbox"]')
|
175
|
+
|
176
|
+
device = Device.new
|
177
|
+
device.name = td.at_xpath('label/text()').to_s.strip rescue nil
|
178
|
+
device.udid = checkbox['value'].to_s.strip rescue nil
|
179
|
+
|
180
|
+
if checkbox['checked']
|
181
|
+
on << device
|
182
|
+
else
|
183
|
+
off << device
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
devices = yield on, off
|
188
|
+
|
189
|
+
form = page.form_with(:name => 'save')
|
190
|
+
form.checkboxes_with(:name => "selectedDevices").each do |checkbox|
|
191
|
+
checkbox.check
|
192
|
+
if devices.detect{|device| device.udid == checkbox['value']}
|
193
|
+
checkbox.check
|
194
|
+
else
|
195
|
+
checkbox.uncheck
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
button = form.button_with(:name => 'submit')
|
200
|
+
form.click_button(button)
|
201
|
+
end
|
202
|
+
|
203
|
+
return nil
|
204
|
+
end
|
205
|
+
|
125
206
|
def list_app_ids
|
126
207
|
get("https://developer.apple.com/ios/manage/bundles/index.action")
|
127
208
|
|
@@ -159,8 +240,8 @@ module Cupertino
|
|
159
240
|
team_option = team_list.option_with(:text => self.team)
|
160
241
|
team_option.select
|
161
242
|
|
162
|
-
|
163
|
-
form.click_button(
|
243
|
+
button = form.button_with(:name => 'action:saveTeamSelection!save')
|
244
|
+
form.click_button(button)
|
164
245
|
end
|
165
246
|
end
|
166
247
|
end
|
@@ -29,3 +29,23 @@ command :'certificates:list' do |c|
|
|
29
29
|
end
|
30
30
|
|
31
31
|
alias_command :certificates, :'certificates:list'
|
32
|
+
|
33
|
+
command :'certificates:download' do |c|
|
34
|
+
c.syntax = 'ios certificates:download [development|distribution]'
|
35
|
+
c.summary = 'Downloads the Certificates'
|
36
|
+
c.description = ''
|
37
|
+
|
38
|
+
c.action do |args, options|
|
39
|
+
type = args.first.downcase.to_sym rescue nil
|
40
|
+
certificates = try{agent.list_certificates(type ||= :development)}
|
41
|
+
|
42
|
+
say_warning "No #{type} certificates found." and abort if certificates.empty?
|
43
|
+
|
44
|
+
certificate = choose "Select a certificate to download:", *certificates
|
45
|
+
if filename = agent.download_certificate(certificate)
|
46
|
+
say_ok "Successfully downloaded: '#{filename}'"
|
47
|
+
else
|
48
|
+
say_error "Could not download certificate"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -29,3 +29,41 @@ command :'profiles:list' do |c|
|
|
29
29
|
end
|
30
30
|
|
31
31
|
alias_command :profiles, :'profiles:list'
|
32
|
+
|
33
|
+
command :'profiles:manage:devices' do |c|
|
34
|
+
c.syntax = 'ios profiles:manage:devices'
|
35
|
+
c.summary = 'Manage active devices for a development provisioning profile'
|
36
|
+
c.description = ''
|
37
|
+
|
38
|
+
c.action do |args, options|
|
39
|
+
type = args.first.downcase.to_sym rescue nil
|
40
|
+
profiles = try{agent.list_profiles(:development)}
|
41
|
+
|
42
|
+
say_warning "No #{type} provisioning profiles found." and abort if profiles.empty?
|
43
|
+
|
44
|
+
profile = choose "Select a provisioning profile to manage:", *profiles
|
45
|
+
|
46
|
+
agent.manage_devices_for_profile(profile) do |on, off|
|
47
|
+
lines = ["# Comment / Uncomment Devices to Turn Off / On for Provisioning Profile"]
|
48
|
+
lines += on.collect{|device| "#{device}"}
|
49
|
+
lines += off.collect{|device| "# #{device}"}
|
50
|
+
result = ask_editor lines.join("\n")
|
51
|
+
|
52
|
+
devices = []
|
53
|
+
result.split(/\n+/).each do |line|
|
54
|
+
next if /^\#/ === line
|
55
|
+
components = line.split(/\s+/)
|
56
|
+
device = Device.new
|
57
|
+
device.udid = components.pop
|
58
|
+
device.name = components.join(" ")
|
59
|
+
devices << device
|
60
|
+
end
|
61
|
+
|
62
|
+
devices
|
63
|
+
end
|
64
|
+
|
65
|
+
say_ok "Successfully managed devices"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
alias_command :'profiles:manage', :'profiles:manage:devices'
|
@@ -1,11 +1,9 @@
|
|
1
1
|
include Cupertino::ProvisioningPortal
|
2
2
|
include Cupertino::ProvisioningPortal::Helpers
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
require 'commands/
|
7
|
-
require 'commands/
|
8
|
-
require 'commands/
|
9
|
-
require 'commands/
|
10
|
-
require 'commands/login'
|
11
|
-
require 'commands/logout'
|
4
|
+
require 'cupertino/provisioning_portal/commands/certificates'
|
5
|
+
require 'cupertino/provisioning_portal/commands/devices'
|
6
|
+
require 'cupertino/provisioning_portal/commands/profiles'
|
7
|
+
require 'cupertino/provisioning_portal/commands/app_ids'
|
8
|
+
require 'cupertino/provisioning_portal/commands/login'
|
9
|
+
require 'cupertino/provisioning_portal/commands/logout'
|
@@ -1,18 +1,35 @@
|
|
1
1
|
require 'mechanize'
|
2
2
|
|
3
3
|
module Cupertino
|
4
|
-
module ProvisioningPortal
|
5
|
-
class Device < Struct.new(:name, :udid)
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
module ProvisioningPortal
|
5
|
+
class Device < Struct.new(:name, :udid)
|
6
|
+
def to_s
|
7
|
+
"#{self.name} #{self.udid}"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Certificate < Struct.new(:name, :type, :provisioning_profiles, :expiration_date, :status)
|
12
|
+
def to_s
|
13
|
+
"#{self.name}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class AppID < Struct.new(:bundle_seed_id, :description, :development_properties, :distribution_properties)
|
18
|
+
def to_s
|
19
|
+
"#{self.bundle_seed_id}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class ProvisioningProfile < Struct.new(:name, :type, :app_id, :status)
|
24
|
+
def to_s
|
25
|
+
"#{self.name}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
10
29
|
class UnsuccessfulAuthenticationError < RuntimeError; end
|
11
30
|
end
|
12
31
|
end
|
13
32
|
|
14
|
-
|
15
|
-
|
16
|
-
require 'provisioning_portal/
|
17
|
-
require 'provisioning_portal/agent'
|
18
|
-
require 'provisioning_portal/commands'
|
33
|
+
require 'cupertino/provisioning_portal/helpers'
|
34
|
+
require 'cupertino/provisioning_portal/agent'
|
35
|
+
require 'cupertino/provisioning_portal/commands'
|
data/lib/cupertino.rb
CHANGED
metadata
CHANGED
@@ -1,143 +1,100 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: cupertino
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 3
|
9
|
-
- 3
|
10
|
-
version: 0.3.3
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Mattt Thompson
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
type: :development
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
12
|
+
date: 2012-07-15 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &70144971248520 !ruby/object:Gem::Requirement
|
24
17
|
none: false
|
25
|
-
requirements:
|
18
|
+
requirements:
|
26
19
|
- - ~>
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
hash: 5
|
29
|
-
segments:
|
30
|
-
- 0
|
31
|
-
- 6
|
32
|
-
- 1
|
20
|
+
- !ruby/object:Gem::Version
|
33
21
|
version: 0.6.1
|
34
|
-
name: rspec
|
35
|
-
version_requirements: *id001
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
-
prerelease: false
|
38
22
|
type: :development
|
39
|
-
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70144971248520
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rake
|
27
|
+
requirement: &70144972093640 !ruby/object:Gem::Requirement
|
40
28
|
none: false
|
41
|
-
requirements:
|
29
|
+
requirements:
|
42
30
|
- - ~>
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
hash: 63
|
45
|
-
segments:
|
46
|
-
- 0
|
47
|
-
- 9
|
48
|
-
- 2
|
31
|
+
- !ruby/object:Gem::Version
|
49
32
|
version: 0.9.2
|
50
|
-
|
51
|
-
version_requirements: *id002
|
52
|
-
- !ruby/object:Gem::Dependency
|
33
|
+
type: :development
|
53
34
|
prerelease: false
|
54
|
-
|
55
|
-
|
35
|
+
version_requirements: *70144972093640
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: commander
|
38
|
+
requirement: &70144974129360 !ruby/object:Gem::Requirement
|
56
39
|
none: false
|
57
|
-
requirements:
|
40
|
+
requirements:
|
58
41
|
- - ~>
|
59
|
-
- !ruby/object:Gem::Version
|
60
|
-
hash: 63
|
61
|
-
segments:
|
62
|
-
- 4
|
63
|
-
- 1
|
64
|
-
- 2
|
42
|
+
- !ruby/object:Gem::Version
|
65
43
|
version: 4.1.2
|
66
|
-
name: commander
|
67
|
-
version_requirements: *id003
|
68
|
-
- !ruby/object:Gem::Dependency
|
69
|
-
prerelease: false
|
70
44
|
type: :runtime
|
71
|
-
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70144974129360
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: terminal-table
|
49
|
+
requirement: &70144974134820 !ruby/object:Gem::Requirement
|
72
50
|
none: false
|
73
|
-
requirements:
|
51
|
+
requirements:
|
74
52
|
- - ~>
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
hash: 13
|
77
|
-
segments:
|
78
|
-
- 1
|
79
|
-
- 4
|
80
|
-
- 5
|
53
|
+
- !ruby/object:Gem::Version
|
81
54
|
version: 1.4.5
|
82
|
-
name: terminal-table
|
83
|
-
version_requirements: *id004
|
84
|
-
- !ruby/object:Gem::Dependency
|
85
|
-
prerelease: false
|
86
55
|
type: :runtime
|
87
|
-
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70144974134820
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: term-ansicolor
|
60
|
+
requirement: &70144975372120 !ruby/object:Gem::Requirement
|
88
61
|
none: false
|
89
|
-
requirements:
|
62
|
+
requirements:
|
90
63
|
- - ~>
|
91
|
-
- !ruby/object:Gem::Version
|
92
|
-
hash: 25
|
93
|
-
segments:
|
94
|
-
- 1
|
95
|
-
- 0
|
96
|
-
- 7
|
64
|
+
- !ruby/object:Gem::Version
|
97
65
|
version: 1.0.7
|
98
|
-
name: term-ansicolor
|
99
|
-
version_requirements: *id005
|
100
|
-
- !ruby/object:Gem::Dependency
|
101
|
-
prerelease: false
|
102
66
|
type: :runtime
|
103
|
-
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70144975372120
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mechanize
|
71
|
+
requirement: &70144976016960 !ruby/object:Gem::Requirement
|
104
72
|
none: false
|
105
|
-
requirements:
|
73
|
+
requirements:
|
106
74
|
- - ~>
|
107
|
-
- !ruby/object:Gem::Version
|
108
|
-
hash: 25
|
109
|
-
segments:
|
110
|
-
- 2
|
111
|
-
- 5
|
112
|
-
- 1
|
75
|
+
- !ruby/object:Gem::Version
|
113
76
|
version: 2.5.1
|
114
|
-
name: mechanize
|
115
|
-
version_requirements: *id006
|
116
|
-
- !ruby/object:Gem::Dependency
|
117
|
-
prerelease: false
|
118
77
|
type: :runtime
|
119
|
-
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *70144976016960
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: netrc
|
82
|
+
requirement: &70144976088360 !ruby/object:Gem::Requirement
|
120
83
|
none: false
|
121
|
-
requirements:
|
84
|
+
requirements:
|
122
85
|
- - ~>
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
hash: 7
|
125
|
-
segments:
|
126
|
-
- 0
|
127
|
-
- 7
|
128
|
-
- 2
|
86
|
+
- !ruby/object:Gem::Version
|
129
87
|
version: 0.7.2
|
130
|
-
|
131
|
-
|
88
|
+
type: :runtime
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *70144976088360
|
132
91
|
description: A command-line interface for the iOS Provisioning Portal
|
133
92
|
email: m@mattt.me
|
134
|
-
executables:
|
93
|
+
executables:
|
135
94
|
- ios
|
136
95
|
extensions: []
|
137
|
-
|
138
96
|
extra_rdoc_files: []
|
139
|
-
|
140
|
-
files:
|
97
|
+
files:
|
141
98
|
- ./cupertino.gemspec
|
142
99
|
- ./Gemfile
|
143
100
|
- ./Gemfile.lock
|
@@ -158,36 +115,32 @@ files:
|
|
158
115
|
- bin/ios
|
159
116
|
homepage: http://mattt.me
|
160
117
|
licenses: []
|
161
|
-
|
162
118
|
post_install_message:
|
163
119
|
rdoc_options: []
|
164
|
-
|
165
|
-
require_paths:
|
120
|
+
require_paths:
|
166
121
|
- lib
|
167
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
168
123
|
none: false
|
169
|
-
requirements:
|
170
|
-
- -
|
171
|
-
- !ruby/object:Gem::Version
|
172
|
-
|
173
|
-
segments:
|
124
|
+
requirements:
|
125
|
+
- - ! '>='
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0'
|
128
|
+
segments:
|
174
129
|
- 0
|
175
|
-
|
176
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
hash: 3169719571408403656
|
131
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
177
132
|
none: false
|
178
|
-
requirements:
|
179
|
-
- -
|
180
|
-
- !ruby/object:Gem::Version
|
181
|
-
|
182
|
-
segments:
|
133
|
+
requirements:
|
134
|
+
- - ! '>='
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
segments:
|
183
138
|
- 0
|
184
|
-
|
139
|
+
hash: 3169719571408403656
|
185
140
|
requirements: []
|
186
|
-
|
187
141
|
rubyforge_project:
|
188
142
|
rubygems_version: 1.8.15
|
189
143
|
signing_key:
|
190
144
|
specification_version: 3
|
191
145
|
summary: Cupertino
|
192
146
|
test_files: []
|
193
|
-
|