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 CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cupertino (0.3.3)
4
+ cupertino (0.4.0)
5
5
  commander (~> 4.1.2)
6
6
  mechanize (~> 2.5.1)
7
7
  netrc (~> 0.7.2)
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
- - ~~`certificates:add [development|distribution]`~~
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
@@ -5,8 +5,6 @@ require 'terminal-table'
5
5
  require 'term/ansicolor'
6
6
  require 'netrc'
7
7
 
8
- $:.push File.expand_path("../../lib", __FILE__)
9
-
10
8
  require 'cupertino'
11
9
 
12
10
  HighLine.track_eof = false # Fix for built-in Ruby
@@ -10,13 +10,17 @@ module Cupertino
10
10
  super
11
11
  self.user_agent_alias = 'Mac Safari'
12
12
 
13
- @username, @password = Netrc.read[::Cupertino::HOSTNAME]
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"]/text()').to_s.strip rescue nil
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
- btn = form.button_with(:name => 'action:saveTeamSelection!save')
163
- form.click_button(btn)
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
- $:.push File.expand_path('../', __FILE__)
5
-
6
- require 'commands/certificates'
7
- require 'commands/devices'
8
- require 'commands/profiles'
9
- require 'commands/app_ids'
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); end
6
- class Certificate < Struct.new(:name, :provisioning_profiles, :expiration_date, :status); end
7
- class AppID < Struct.new(:bundle_seed_id, :description, :development_properties, :distribution_properties); end
8
- class ProvisioningProfile < Struct.new(:name, :app_id, :status); end
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
- $:.push File.expand_path('../', __FILE__)
15
-
16
- require 'provisioning_portal/helpers'
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
@@ -1,4 +1,4 @@
1
1
  module Cupertino
2
- VERSION = '0.3.3'
2
+ VERSION = '0.4.0'
3
3
  HOSTNAME = "developer.apple.com"
4
4
  end
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
- hash: 21
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
- date: 2012-07-10 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
21
- prerelease: false
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
- requirement: &id002 !ruby/object:Gem::Requirement
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
- name: rake
51
- version_requirements: *id002
52
- - !ruby/object:Gem::Dependency
33
+ type: :development
53
34
  prerelease: false
54
- type: :runtime
55
- requirement: &id003 !ruby/object:Gem::Requirement
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
- requirement: &id004 !ruby/object:Gem::Requirement
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
- requirement: &id005 !ruby/object:Gem::Requirement
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
- requirement: &id006 !ruby/object:Gem::Requirement
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
- requirement: &id007 !ruby/object:Gem::Requirement
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
- name: netrc
131
- version_requirements: *id007
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
- hash: 3
173
- segments:
124
+ requirements:
125
+ - - ! '>='
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ segments:
174
129
  - 0
175
- version: "0"
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
- hash: 3
182
- segments:
133
+ requirements:
134
+ - - ! '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ segments:
183
138
  - 0
184
- version: "0"
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
-