ios-portal 0.0.1
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.
- data/bin/ios-portal +108 -0
- data/lib/ios-portal/client.rb +154 -0
- data/lib/ios-portal.rb +26 -0
- metadata +153 -0
data/bin/ios-portal
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# 1.9 adds realpath to resolve symlinks; 1.8 doesn't
|
3
|
+
# have this method, so we add it so we get resolved symlinks
|
4
|
+
# and compatibility
|
5
|
+
unless File.respond_to? :realpath
|
6
|
+
class File #:nodoc:
|
7
|
+
def self.realpath path
|
8
|
+
return realpath(File.readlink(path)) if symlink?(path)
|
9
|
+
path
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
$: << File.expand_path(File.dirname(File.realpath(__FILE__)) + '/../lib')
|
14
|
+
require 'rubygems'
|
15
|
+
require 'gli'
|
16
|
+
require 'ios-portal'
|
17
|
+
require 'highline/import'
|
18
|
+
require 'terminal-table'
|
19
|
+
|
20
|
+
include GLI
|
21
|
+
|
22
|
+
program_desc 'CLI to view iOS Provisioning Profiles and Devices in Member Center'
|
23
|
+
|
24
|
+
version IOSPortal::VERSION
|
25
|
+
|
26
|
+
desc 'Apple ID'
|
27
|
+
default_value nil
|
28
|
+
arg_name 'Your Apple ID. Generally your email address.'
|
29
|
+
flag [:a,:appleid]
|
30
|
+
|
31
|
+
desc 'Apple ID Password'
|
32
|
+
default_value nil
|
33
|
+
arg_name 'Your Apple ID password'
|
34
|
+
flag [:p,:password]
|
35
|
+
|
36
|
+
desc 'Account Select'
|
37
|
+
default_value nil
|
38
|
+
arg_name 'The full name of your Apple Account'
|
39
|
+
flag [:s,"select-account"]
|
40
|
+
|
41
|
+
desc 'Displays formatted table of provisioning profiles'
|
42
|
+
arg_name 'Apple ID and Password are required'
|
43
|
+
command :profiles do |c|
|
44
|
+
c.switch :development
|
45
|
+
c.switch :distribution
|
46
|
+
c.action do |global_options,options,args|
|
47
|
+
username = global_options[:a]
|
48
|
+
password = global_options[:p]
|
49
|
+
account = global_options[:s]
|
50
|
+
|
51
|
+
opt = options
|
52
|
+
opt[:all] = true if !opt[:development] and !opt[:distribution]
|
53
|
+
|
54
|
+
all_profiles = IOSPortal.client({:username=>username,:password=>password, :account=>account}).profiles(opt)
|
55
|
+
raise "No profiles found" unless all_profiles
|
56
|
+
|
57
|
+
all_profiles.each do |k,profiles|
|
58
|
+
table = Terminal::Table.new :title => k.to_s.capitalize, :headings => ['Name', 'ID', 'App ID', 'Editable'] do |t|
|
59
|
+
profiles.each do |profile|
|
60
|
+
t << [profile[:name],profile[:id],profile[:app_id],profile[:editable]]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
puts table
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
desc 'Displays formatted table of devices'
|
69
|
+
arg_name 'Apple ID and Password are required'
|
70
|
+
command :devices do |c|
|
71
|
+
c.action do |global_options,options,args|
|
72
|
+
username = global_options[:a]
|
73
|
+
password = global_options[:p]
|
74
|
+
account = global_options[:s]
|
75
|
+
|
76
|
+
devices = IOSPortal.client({:username=>username,:password=>password, :account=>account}).devices()
|
77
|
+
raise "No devices found" unless devices
|
78
|
+
|
79
|
+
|
80
|
+
table = Terminal::Table.new :title => "Devices (#{devices.length})", :headings => ['Name', 'UDID', 'ID'] do |t|
|
81
|
+
devices.each do |device|
|
82
|
+
name = device[:name].gsub("’","") # remove ’, they mess with the table view
|
83
|
+
t << [name,device[:udid],device[:id]]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
puts table
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
pre do |global,command,options,args|
|
93
|
+
if command
|
94
|
+
unless global[:a]
|
95
|
+
global[:a] = ask("Enter your Apple ID: ")
|
96
|
+
end
|
97
|
+
unless global[:p]
|
98
|
+
global[:p] = ask("Enter your password: "){ |q| q.echo = "*" }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
true
|
102
|
+
end
|
103
|
+
|
104
|
+
on_error do |exception|
|
105
|
+
true
|
106
|
+
end
|
107
|
+
|
108
|
+
exit GLI.run(ARGV)
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "mechanize"
|
3
|
+
require "highline/import"
|
4
|
+
|
5
|
+
module IOSPortal
|
6
|
+
class Client
|
7
|
+
attr_accessor :username, :password, :account
|
8
|
+
|
9
|
+
# Creates a new API
|
10
|
+
def initialize(options={})
|
11
|
+
self.username = options[:username]
|
12
|
+
self.password = options[:password]
|
13
|
+
self.account = options[:account]
|
14
|
+
|
15
|
+
@@agent = Mechanize.new
|
16
|
+
@@agent.follow_meta_refresh = true
|
17
|
+
end
|
18
|
+
|
19
|
+
def profiles(options={})
|
20
|
+
if _authenticate
|
21
|
+
|
22
|
+
profiles = {}
|
23
|
+
|
24
|
+
all = options.has_key?(:all) ? options[:all] : true
|
25
|
+
|
26
|
+
if options[:development] or all
|
27
|
+
profiles[:development] = development_provisioning_profiles
|
28
|
+
end
|
29
|
+
|
30
|
+
if options[:distribution] or all
|
31
|
+
profiles[:distribution] = distribution_provisioning_profiles
|
32
|
+
end
|
33
|
+
return profiles
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def devices(options={})
|
38
|
+
if _authenticate
|
39
|
+
page = @@agent.get(IOSPortal::DEVICES_URL)
|
40
|
+
#puts page.parser.xpath('/').to_html
|
41
|
+
profile_devices = []
|
42
|
+
|
43
|
+
devices = page.at("div[@class='nt_multi']")
|
44
|
+
raise "No devices" unless devices
|
45
|
+
|
46
|
+
|
47
|
+
hidden_name = ""
|
48
|
+
checkbox_name = ""
|
49
|
+
devices.at("tbody").children.each{|row|
|
50
|
+
device = {}
|
51
|
+
row.children.each{|column|
|
52
|
+
case column['class']
|
53
|
+
when "checkbox"
|
54
|
+
device[:id] = column.children[1]['value'].strip
|
55
|
+
when "name"
|
56
|
+
device[:name] = column.children[1].text.strip
|
57
|
+
when "id"
|
58
|
+
device[:udid] = column.text.strip
|
59
|
+
end
|
60
|
+
}
|
61
|
+
|
62
|
+
profile_devices.push(device) if device.length > 0
|
63
|
+
|
64
|
+
}
|
65
|
+
return profile_devices
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def provisioning_profiles(url)
|
72
|
+
page = @@agent.get(url)
|
73
|
+
table = page.at("//table/tbody")
|
74
|
+
profiles = []
|
75
|
+
return profiles unless table
|
76
|
+
|
77
|
+
table.children.each{|tr|
|
78
|
+
children = tr.children
|
79
|
+
cert_key = children[0].children[0]['value'].strip
|
80
|
+
cert_name = children[2].content.strip
|
81
|
+
app_id = children[4].content.strip
|
82
|
+
editable = false
|
83
|
+
children[8].search("a").each{|link|
|
84
|
+
if link.inner_html == "Edit"
|
85
|
+
editable = true
|
86
|
+
break
|
87
|
+
end
|
88
|
+
}
|
89
|
+
profiles.push({:name => cert_name, :id => cert_key, :app_id => app_id, :editable => editable })
|
90
|
+
}
|
91
|
+
return profiles
|
92
|
+
end
|
93
|
+
|
94
|
+
def development_provisioning_profiles
|
95
|
+
provisioning_profiles(IOSPortal::DEV_PROVISIONING_PROFILE_URL)
|
96
|
+
end
|
97
|
+
|
98
|
+
def distribution_provisioning_profiles
|
99
|
+
|
100
|
+
provisioning_profiles(IOSPortal::DIST_PROVISIONING_PROFILE_URL)
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
def _authenticate
|
105
|
+
agent = Mechanize.new
|
106
|
+
agent.follow_meta_refresh = true
|
107
|
+
# Get login page
|
108
|
+
page = @@agent.get(IOSPortal::LOGIN_URL)
|
109
|
+
|
110
|
+
# Submit form to login
|
111
|
+
login_form = page.forms.first
|
112
|
+
login_form['theAccountName']=self.username
|
113
|
+
login_form['theAccountPW']=self.password
|
114
|
+
page = @@agent.submit(login_form)
|
115
|
+
|
116
|
+
# Fail if it looks like we ended up back at a login page
|
117
|
+
if page.form('appleConnectForm')
|
118
|
+
return nil
|
119
|
+
else
|
120
|
+
select_form = page.form('saveTeamSelection')
|
121
|
+
if select_form
|
122
|
+
options = []
|
123
|
+
selection = -1
|
124
|
+
select_form.fields.first.options.each{|option|
|
125
|
+
if self.account == option.text
|
126
|
+
selection = options.length + 1
|
127
|
+
end
|
128
|
+
options.push([option.text,option.value])
|
129
|
+
|
130
|
+
}
|
131
|
+
|
132
|
+
if selection < 0
|
133
|
+
options.each_index { |i|
|
134
|
+
puts "#{i+1}. #{options[i][0]}"
|
135
|
+
}
|
136
|
+
|
137
|
+
selection = ask "Enter the # for the account you wish to login to: "
|
138
|
+
if selection.to_i > (options.length )
|
139
|
+
error "Invalid selection"
|
140
|
+
return nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
team = options[selection.to_i - 1]
|
144
|
+
select_form['memberDisplayId'] = team[1]
|
145
|
+
page = @@agent.submit(select_form, select_form.buttons[1])
|
146
|
+
return page
|
147
|
+
else
|
148
|
+
return page
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
end
|
data/lib/ios-portal.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path('../ios-portal/client', __FILE__)
|
2
|
+
|
3
|
+
module IOSPortal
|
4
|
+
VERSION = '0.0.1'
|
5
|
+
|
6
|
+
BASE_URL = "https://developer.apple.com"
|
7
|
+
LOGIN_URL = BASE_URL + "/ios/manage/overview/index.action"
|
8
|
+
DEVICES_URL = BASE_URL + "/ios/manage/devices/index.action"
|
9
|
+
DEVICE_BULK_UPLOAD_URL = "/ios/manage/devices/saveupload.action"
|
10
|
+
SAVE_TEAM_URL = BASE_URL + "/membercenter/saveTeamSelection.action"
|
11
|
+
|
12
|
+
DEV_PROVISIONING_PROFILE_URL = BASE_URL + "/ios/manage/provisioningprofiles/index.action"
|
13
|
+
DIST_PROVISIONING_PROFILE_URL = BASE_URL + "/ios/manage/provisioningprofiles/viewDistributionProfiles.action"
|
14
|
+
|
15
|
+
CREATE_ADHOC_PROFILE_URL = BASE_URL + "/ios/manage/provisioningprofiles/create.action?type=2"
|
16
|
+
DOWNLOAD_PROFILE_URL_WO_ID = BASE_URL + "/ios/manage/provisioningprofiles/download.action?blobId="
|
17
|
+
EDIT_PROFILE_URL_WO_ID = BASE_URL + "/ios/manage/provisioningprofiles/edit.action?provDisplayId="
|
18
|
+
|
19
|
+
SAVE_PROFILE_URL = BASE_URL + "/ios/manage/provisioningprofiles/save.action"
|
20
|
+
|
21
|
+
def self.client(options={})
|
22
|
+
IOSPortal::Client.new(options)
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ios-portal
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Nolan Borwn
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-05-09 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rake
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rdoc
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: gli
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
hash: 3
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
type: :runtime
|
61
|
+
version_requirements: *id003
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: mechanize
|
64
|
+
prerelease: false
|
65
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
type: :runtime
|
75
|
+
version_requirements: *id004
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: highline
|
78
|
+
prerelease: false
|
79
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
hash: 3
|
85
|
+
segments:
|
86
|
+
- 0
|
87
|
+
version: "0"
|
88
|
+
type: :runtime
|
89
|
+
version_requirements: *id005
|
90
|
+
- !ruby/object:Gem::Dependency
|
91
|
+
name: terminal-table
|
92
|
+
prerelease: false
|
93
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
hash: 3
|
99
|
+
segments:
|
100
|
+
- 0
|
101
|
+
version: "0"
|
102
|
+
type: :runtime
|
103
|
+
version_requirements: *id006
|
104
|
+
description:
|
105
|
+
email: nolanbrown@gmail.com
|
106
|
+
executables:
|
107
|
+
- ios-portal
|
108
|
+
extensions: []
|
109
|
+
|
110
|
+
extra_rdoc_files: []
|
111
|
+
|
112
|
+
files:
|
113
|
+
- bin/ios-portal
|
114
|
+
- lib/ios-portal.rb
|
115
|
+
- lib/ios-portal/client.rb
|
116
|
+
homepage: http://github.com/nolanbrown
|
117
|
+
licenses: []
|
118
|
+
|
119
|
+
post_install_message:
|
120
|
+
rdoc_options:
|
121
|
+
- --title
|
122
|
+
- iOS Portal
|
123
|
+
- --main
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
- lib
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
none: false
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
hash: 3
|
133
|
+
segments:
|
134
|
+
- 0
|
135
|
+
version: "0"
|
136
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
hash: 3
|
142
|
+
segments:
|
143
|
+
- 0
|
144
|
+
version: "0"
|
145
|
+
requirements: []
|
146
|
+
|
147
|
+
rubyforge_project:
|
148
|
+
rubygems_version: 1.8.24
|
149
|
+
signing_key:
|
150
|
+
specification_version: 3
|
151
|
+
summary: CLI to view iOS Provisioning Profiles and Devices in Member Center
|
152
|
+
test_files: []
|
153
|
+
|