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 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
+