ios-portal 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+