omudid 1.0.3

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/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'cupertino'#, :git => 'git://github.com/davidlawson/cupertino.git', :branch => 'omudid'
4
+ gem 'spinning_cursor'
5
+ gem 'rainbow'
6
+
7
+ # Specify your gem's dependencies in OneMoreUDID.gemspec
8
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 David Lawson
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require 'OneMoreUDID'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "omudid"
9
+ spec.version = OneMoreUDID::VERSION
10
+ spec.authors = ["David Lawson"]
11
+ spec.email = ["tech.lawson@gmail.com"]
12
+ spec.description = 'Conveniently add a UDID to the iOS Developer Portal, refresh a provisioning profile and download it. Uploading a new provisioning profile to TestFlight is also supported.'
13
+ spec.summary = 'Add one UDID to iOS Dev Portal & TestFlight'
14
+ spec.homepage = 'https://github.com/Papercloud/OneMoreUDID'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "rake"
24
+
25
+ spec.add_dependency "cupertino"
26
+
27
+ spec.add_dependency "spinning_cursor"
28
+ spec.add_dependency "rainbow"
29
+
30
+ spec.add_dependency "commander", "~> 4.1.2"
31
+ end
data/README.md ADDED
@@ -0,0 +1,19 @@
1
+ OneMoreUDID
2
+ ===========
3
+
4
+ Conveniently add a UDID to the iOS Developer Portal, refresh a provisioning profile and download it.
5
+
6
+ Uploading a new provisioning profile to TestFlight is also supported.
7
+
8
+ Installation
9
+ -
10
+ ```
11
+ $ gem install OneMoreUDID
12
+ ```
13
+
14
+ Usage
15
+ -
16
+ ```
17
+ $ omudid add
18
+ $ omudid testflight upload
19
+ ```
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/omudid ADDED
@@ -0,0 +1,297 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'commander/import'
5
+
6
+ require 'OneMoreUDID'
7
+
8
+ require 'terminal-table'
9
+ require 'term/ansicolor'
10
+
11
+ require 'cupertino_compatibility'
12
+
13
+ require 'shellwords'
14
+
15
+ program :name, 'OneMoreUDID'
16
+ program :version, OneMoreUDID::VERSION
17
+ program :description, '''Uses the developer portal to add one device to a provisioning profile,
18
+ then downloads and installs the new profile.
19
+ Also uploads provisioning profiles to TestFlight.'''
20
+
21
+ HighLine.track_eof = false # Fix for built-in Ruby
22
+
23
+ default_command :help
24
+
25
+ command :'list-teams' do |c|
26
+ c.syntax = 'omudid list-teams [username] [password]'
27
+ c.summary = 'Lists teams on the iOS developer portal'
28
+ c.description = ''
29
+
30
+ c.action do |args, options|
31
+ username = (args[0] or ask 'Apple Username:')
32
+ password = (args[1] or pw 'Apple Password:')
33
+
34
+ if !args[1]
35
+ puts
36
+ say_warning 'Note: you can repeat this process by running:'
37
+ say_warning ' omudid list-teams ' + Shellwords.escape(username) + ' [password]'
38
+ puts
39
+ end
40
+
41
+ agent = OneMoreUDID::PortalAgent.new
42
+
43
+ teams = agent.get_teams(username, password)
44
+
45
+ table = Terminal::Table.new do |t|
46
+ t << ['Team ID', 'Team Name']
47
+ t << :separator
48
+
49
+ teams.each do |id, name|
50
+ t << [id, name]
51
+ end
52
+
53
+ end
54
+
55
+ puts table
56
+ end
57
+ end
58
+
59
+ command :'list-profiles' do |c|
60
+ c.syntax = 'omudid list-profiles [username] [password] ([team ID])'
61
+ c.summary = 'Lists provisioning profiles on the iOS developer portal'
62
+ c.description = ''
63
+
64
+ c.action do |args, options|
65
+
66
+ username = (args[0] or ask 'Apple Username:')
67
+ password = (args[1] or pw 'Apple Password:')
68
+ team_name = args[2] rescue ''
69
+
70
+ if !args[1]
71
+ puts
72
+ end
73
+
74
+ agent = OneMoreUDID::PortalAgent.new
75
+
76
+ agent.setup_cupertino(username, password, team_name)
77
+ profiles = agent.list_profiles()
78
+
79
+ if !args[1]
80
+ puts
81
+ say_warning 'Note: you can repeat this process by running:'
82
+
83
+ if agent.agent.instance_variable_get('@team')
84
+ say_warning ' omudid list-profiles ' + Shellwords.escape(username) + ' [password] ' + agent.agent.instance_variable_get('@team')
85
+ else
86
+ say_warning ' omudid list-profiles ' + Shellwords.escape(username) + ' [password]'
87
+ end
88
+
89
+ puts
90
+ end
91
+
92
+ table = Terminal::Table.new do |t|
93
+ t << ['Name', 'Type', 'App ID', 'Status']
94
+ t << :separator
95
+
96
+ profiles.each do |profile|
97
+ t << [profile.name, profile.type, profile.app_id, profile.status]
98
+ end
99
+ end
100
+
101
+ puts table
102
+ end
103
+ end
104
+
105
+ command :add do |c|
106
+ c.syntax = 'omudid add [username] [password] [profile name] [device name] [UDID] ([team ID])'
107
+ c.summary = 'Adds a device to the provisioning portal, then downloads and installs the new profile'
108
+ c.description = ''
109
+
110
+ c.action do |args, options|
111
+
112
+ username = (args[0] or ask 'Apple Username:')
113
+ password = (args[1] or pw 'Apple Password:')
114
+
115
+ profile_name = (args[2] or ask 'Profile name (leave blank to select from available profiles):')
116
+ device_name = (args[3] or ask 'Device name:')
117
+ udid = (args[4] or ask 'UDID:')
118
+ team_name = args[5] rescue ''
119
+
120
+ show_hint = false
121
+
122
+ if !args[4]
123
+ show_hint = true
124
+ puts
125
+ end
126
+
127
+ agent = OneMoreUDID::PortalAgent.new
128
+ agent.setup_cupertino(username, password, team_name)
129
+
130
+ agent.add_device(device_name, udid)
131
+
132
+ if profile_name == ''
133
+ puts
134
+ profile_name = choose "\nSelect a profile:", *agent.list_profiles().collect { |profile| profile.name }
135
+ puts
136
+ end
137
+
138
+ if show_hint
139
+ say_warning 'Note: you can repeat this process by running:'
140
+
141
+ if agent.agent.instance_variable_get('@team')
142
+ say_warning ' omudid add ' + Shellwords.escape(username) + ' [password] ' + Shellwords.escape(profile_name) + ' ' + Shellwords.escape(device_name) + ' ' + Shellwords.escape(udid) + ' ' + agent.agent.instance_variable_get('@team')
143
+ else
144
+ say_warning ' omudid add ' + Shellwords.escape(username) + ' [password] ' + Shellwords.escape(profile_name) + ' ' + Shellwords.escape(device_name) + ' ' + Shellwords.escape(udid)
145
+ end
146
+
147
+ puts
148
+ end
149
+
150
+ agent.update_profile(profile_name)
151
+
152
+ filename = agent.download_new_profile(profile_name)
153
+
154
+ local_agent = OneMoreUDID::LocalAgent.new
155
+ local_agent.install_profile(profile_name, filename)
156
+ end
157
+ end
158
+
159
+ command :'testflight list-apps' do |c|
160
+ c.syntax = 'omudid testflight list-apps [username] [password]'
161
+ c.summary = 'Lists apps on TestFlight'
162
+ c.description = ''
163
+
164
+ c.action do |args, options|
165
+
166
+ username = (args[0] or ask 'TestFlight Username:')
167
+ password = (args[1] or pw 'TestFlight Password:')
168
+
169
+ if !args[1]
170
+ puts
171
+ say_warning 'Note: you can repeat this process by running:'
172
+ say_warning ' omudid testflight list-apps ' + Shellwords.escape(username) + ' [password]'
173
+ puts
174
+ end
175
+
176
+ agent = OneMoreUDID::TestFlightAgent.new(username, password)
177
+ apps = agent.get_apps()
178
+
179
+ table = Terminal::Table.new do |t|
180
+ t << ['App ID', 'App Name']
181
+ t << :separator
182
+
183
+ apps.each do |id, name|
184
+ t << [id, name]
185
+ end
186
+
187
+ end
188
+
189
+ puts table
190
+ end
191
+ end
192
+
193
+ command :'testflight list-builds' do |c|
194
+ c.syntax = 'omudid testflight list-builds [username] [password] [app ID]'
195
+ c.summary = 'Lists builds of an app on TestFlight'
196
+ c.description = ''
197
+
198
+ c.action do |args, options|
199
+
200
+ username = (args[0] or ask 'TestFlight Username:')
201
+ password = (args[1] or pw 'TestFlight Password:')
202
+ app_id = (args[2] or ask 'App ID (leave blank to select from available IDs):')
203
+
204
+ show_hint = false
205
+
206
+ if !args[2]
207
+ show_hint = true
208
+ end
209
+
210
+ agent = OneMoreUDID::TestFlightAgent.new(username, password)
211
+
212
+ if app_id == ''
213
+ puts
214
+ choice = choose 'Select an app ID:', *agent.get_apps().collect{ |id, name| id + ' ('+name+')' }
215
+ puts
216
+ regex = /^[^ ]*/
217
+ app_id = (choice.match regex)[0]
218
+ end
219
+
220
+ say_warning 'Note: you can repeat this process by running:'
221
+ say_warning ' omudid testflight list-builds ' + Shellwords.escape(username) + ' [password] ' + Shellwords.escape(app_id)
222
+ puts
223
+
224
+ builds = agent.get_builds(app_id)
225
+
226
+ table = Terminal::Table.new do |t|
227
+ t << ['Build ID', 'Build Name']
228
+ t << :separator
229
+
230
+ builds.each do |id, name|
231
+ t << [id, name]
232
+ end
233
+
234
+ end
235
+
236
+ puts table
237
+ end
238
+ end
239
+
240
+ command :'testflight upload' do |c|
241
+ c.syntax = 'omudid testflight upload [username] [password] [build ID] [profile name]'
242
+ c.summary = 'Uploads a provisioning profile to TestFlight'
243
+ c.description = ''
244
+
245
+ c.action do |args, options|
246
+
247
+ username = (args[0] or ask 'TestFlight Username:')
248
+ password = (args[1] or pw 'TestFlight Password:')
249
+ build_id = (args[2] or ask 'Build ID (leave blank to select from available IDs):')
250
+ profile_name = (args[3] or ask 'Profile name (leave blank to select from available local profiles):')
251
+
252
+ show_hint = false
253
+
254
+ if !args[3]
255
+ show_hint = true
256
+ end
257
+
258
+ agent = OneMoreUDID::TestFlightAgent.new(username, password)
259
+
260
+ if build_id == ''
261
+ app_id = ask 'Enter an App ID to list builds (or leave blank to list App IDs):'
262
+
263
+ if app_id == ''
264
+ puts
265
+ choice = choose 'Select an app ID:', *agent.get_apps().collect{ |id, name| id + ' ('+name+')' }
266
+ puts
267
+ regex = /^[^ ]*/
268
+ app_id = (choice.match regex)[0]
269
+ end
270
+
271
+ puts
272
+ choice = choose 'Select a build ID:', *agent.get_builds(app_id).collect{ |id, name| id + ' ('+name+')' }
273
+ puts
274
+ regex = /^[^ ]*/
275
+ build_id = (choice.match regex)[0]
276
+
277
+ show_hint = true
278
+ end
279
+
280
+ if profile_name == ''
281
+ local_agent = OneMoreUDID::LocalAgent.new
282
+ profile_name = choose 'Select a profile:', *local_agent.get_profiles()
283
+ puts
284
+
285
+ show_hint = true
286
+ end
287
+
288
+ if show_hint
289
+ say_warning 'Note: you can repeat this process by running:'
290
+ say_warning ' omudid testflight list-builds ' + Shellwords.escape(username) + ' [password] ' + Shellwords.escape(build_id) + ' ' + Shellwords.escape(profile_name)
291
+ puts
292
+ end
293
+
294
+ agent.upload(build_id, profile_name)
295
+ end
296
+ end
297
+
@@ -0,0 +1,345 @@
1
+ require 'mechanize'
2
+ require 'spinning_cursor'
3
+ require 'rainbow'
4
+ require 'iconv' unless String.method_defined?(:encode)
5
+
6
+ module OneMoreUDID
7
+ VERSION = "1.0.3"
8
+
9
+ class PortalAgent
10
+
11
+ attr_accessor :agent
12
+
13
+ def get_teams(username, password)
14
+ agent = Cupertino::ProvisioningPortal::Agent.new
15
+
16
+ agent.instance_eval do
17
+ def get(uri, parameters = [], referer = nil, headers = {})
18
+ uri = ::File.join("https://#{Cupertino::HOSTNAME}", uri) unless /^https?/ === uri
19
+
20
+ 3.times do
21
+ SpinningCursor.start do
22
+ banner 'Loading page...'
23
+ action do
24
+ ::Mechanize.instance_method(:get).bind(self).call(uri, parameters, referer, headers)
25
+ end
26
+ message 'Loading page... '+'done'.color(:green)
27
+ end
28
+
29
+ return page unless page.respond_to?(:title)
30
+
31
+ case page.title
32
+ when /Sign in with your Apple ID/
33
+ login! and next
34
+ else
35
+ return page
36
+ end
37
+ end
38
+
39
+ raise UnsuccessfulAuthenticationError
40
+ end
41
+ end
42
+
43
+ agent.username = username
44
+ agent.password = password
45
+
46
+ agent.get('https://developer.apple.com/account/selectTeam.action')
47
+
48
+ teams = agent.page.form_with(:name => 'saveTeamSelection').radiobuttons
49
+
50
+ formatted_teams = {}
51
+ teams.each do |team|
52
+ labels = agent.page.search("label[for=\"#{team.dom_id}\"]")
53
+ formatted_teams[team.value] = labels[0].text.strip
54
+ formatted_teams[team.value] += ' - ' + labels[1].text.strip if labels[1].text.strip != ''
55
+ end
56
+
57
+ formatted_teams
58
+ end
59
+
60
+ def setup_cupertino(username, password, team_name = '')
61
+ agent = Cupertino::ProvisioningPortal::Agent.new
62
+
63
+ agent.instance_eval do
64
+ def get(uri, parameters = [], referer = nil, headers = {})
65
+ uri = ::File.join("https://#{Cupertino::HOSTNAME}", uri) unless /^https?/ === uri
66
+
67
+ 3.times do
68
+ SpinningCursor.start do
69
+ banner 'Loading page...'
70
+ action do
71
+ ::Mechanize.instance_method(:get).bind(self).call(uri, parameters, referer, headers)
72
+ end
73
+ message 'Loading page... '+'done'.color(:green)
74
+ end
75
+
76
+ return page unless page.respond_to?(:title)
77
+
78
+ case page.title
79
+ when /Sign in with your Apple ID/
80
+ login! and next
81
+ when /Select Team/
82
+ select_team! and next
83
+ else
84
+ return page
85
+ end
86
+ end
87
+
88
+ raise UnsuccessfulAuthenticationError
89
+ end
90
+
91
+ def team
92
+ teams = page.form_with(:name => 'saveTeamSelection').radiobuttons
93
+
94
+ formatted_teams = {}
95
+ teams.each do |team|
96
+ labels = page.search("label[for=\"#{team.dom_id}\"]")
97
+ formatted_teams[team.value] = labels[0].text.strip
98
+ formatted_teams[team.value] += ' - ' + labels[1].text.strip if labels[1].text.strip != ''
99
+ end
100
+
101
+ if formatted_teams[@teamName]
102
+ @team = @teamName
103
+ else
104
+ puts
105
+ say_warning 'Note: you can specify the team with a command-line argument'
106
+ puts
107
+ chosen_team = choose 'Please select one of the following teams:', *formatted_teams.collect { |id, name| id + ': ' + name }
108
+ puts
109
+ regex = /^[^:]*/
110
+ @team = (chosen_team.match regex)[0]
111
+ end
112
+
113
+ @team
114
+ end
115
+ end
116
+
117
+ agent.username = username
118
+ agent.password = password
119
+ agent.instance_variable_set(:@teamName, team_name)
120
+
121
+ @agent = agent
122
+
123
+ self
124
+ end
125
+
126
+ def add_device(device_name, udid)
127
+ device = Device.new
128
+ device.name = device_name
129
+ device.udid = udid
130
+
131
+ try {@agent.add_devices(*[device])}
132
+
133
+ say_ok 'Device ' + device.name + ' (' + device.udid + ') added'
134
+ end
135
+
136
+ def list_profiles()
137
+ try{@agent.list_profiles(:distribution)}
138
+ end
139
+
140
+ def update_profile(profile_name)
141
+ profiles = try{@agent.list_profiles(:distribution)}
142
+ profile = (profiles.select { |profile| profile.name == profile_name }).first
143
+
144
+ if !profile
145
+ say_error 'Provisioning profile not found, profiles available:'
146
+
147
+ #modularise this?
148
+ table = Terminal::Table.new do |t|
149
+ t << ['Name', 'Type', 'App ID', 'Status']
150
+ t << :separator
151
+
152
+ profiles.each do |profile|
153
+ t << [profile.name, profile.type, profile.app_id, profile.status]
154
+ end
155
+ end
156
+
157
+ puts table
158
+
159
+ abort
160
+ end
161
+
162
+ @agent.manage_devices_for_profile(profile) do |on, off|
163
+ #enable all devices
164
+ on + off
165
+ end
166
+ end
167
+
168
+ def download_new_profile(profile_name)
169
+ profiles = try{@agent.list_profiles(:distribution)}
170
+ profile = (profiles.select { |profile| profile.name == profile_name }).first
171
+
172
+ if !profile
173
+ say_error 'New provisioning profile not found, profiles available:'
174
+ puts profiles
175
+ abort
176
+ end
177
+
178
+ filename = ''
179
+ 5.times do
180
+ begin
181
+ sleep 5
182
+ if filename = @agent.download_profile(profile)
183
+ say_ok 'Downloaded new profile (' + Dir.pwd + '/' + filename + ')'
184
+ break
185
+ else
186
+ say_error 'Could not download profile'
187
+ end
188
+ rescue
189
+ say_error 'Could not download profile'
190
+ end
191
+ end
192
+
193
+ if filename == ''
194
+ abort
195
+ end
196
+
197
+ filename
198
+ end
199
+ end
200
+
201
+ class LocalAgent
202
+ def install_profile(profile_name, filename)
203
+
204
+ Dir.glob(File.expand_path('~') + '/Library/MobileDevice/Provisioning Profiles/*.mobileprovision') do |file|
205
+
206
+ delete_file = false
207
+
208
+ File.open(file, "r") do |_file|
209
+
210
+ file_contents = _file.read
211
+ if String.method_defined?(:encode)
212
+ file_contents.encode!('UTF-8', 'UTF-8', :invalid => :replace)
213
+ end
214
+ matches = /<key>Name<\/key>\s+<string>([^<]+)<\/string>/.match file_contents
215
+
216
+ if matches[1] == profile_name
217
+ delete_file = true
218
+ end
219
+ end
220
+
221
+ if delete_file
222
+ say_warning 'Old profile deleted ('+ file +')'
223
+ File.delete(file)
224
+ break
225
+ end
226
+
227
+ end
228
+
229
+ new_path = File.expand_path('~') + '/Library/MobileDevice/Provisioning Profiles/' + filename
230
+ File.rename(Dir.pwd + '/' + filename, new_path)
231
+
232
+ say_ok 'New profile installed ('+new_path+')'
233
+ end
234
+
235
+ def get_profiles
236
+
237
+ profiles = []
238
+
239
+ Dir.glob(File.expand_path('~') + '/Library/MobileDevice/Provisioning Profiles/*.mobileprovision') do |file|
240
+
241
+ File.open(file, "r") do |_file|
242
+
243
+ file_contents = _file.read
244
+ if String.method_defined?(:encode)
245
+ file_contents.encode!('UTF-8', 'UTF-8', :invalid => :replace)
246
+ end
247
+
248
+ matches = /<key>Name<\/key>\s+<string>([^<]+)<\/string>/.match file_contents
249
+ profiles << matches[1]
250
+ end
251
+
252
+ end
253
+
254
+ profiles
255
+
256
+ end
257
+ end
258
+
259
+ class TestFlightAgent < ::Mechanize
260
+
261
+ attr_accessor :username, :password
262
+
263
+ def initialize(username, password)
264
+ super()
265
+ @username = username
266
+ @password = password
267
+ self
268
+ end
269
+
270
+ def get(uri, parameters = [], referer = nil, headers = {})
271
+ 3.times do
272
+ SpinningCursor.start do
273
+ banner 'Loading page...'
274
+ action do
275
+ super(uri, parameters, referer, headers)
276
+ end
277
+ message 'Loading page... '+'done'.color(:green)
278
+ end
279
+
280
+ return page unless page.respond_to?(:body)
281
+
282
+ case page.body
283
+ when /Login to TestFlight and FlightPath/
284
+ login! and next
285
+ else
286
+ return page
287
+ end
288
+ end
289
+
290
+ raise UnsuccessfulAuthenticationError
291
+ end
292
+
293
+ def get_apps
294
+ get('https://testflightapp.com/dashboard/applications/')
295
+
296
+ apps = page.search('tr.goapp')
297
+ regex = /([0-9]+)/
298
+ apps.collect { |app| [(app['id'].match regex)[1], app.search('h2 small').text.strip] }
299
+ end
300
+
301
+ def get_builds(app_id)
302
+ get("https://testflightapp.com/dashboard/applications/#{app_id}/builds/")
303
+
304
+ builds = page.search('tr.goversion')
305
+ regex = /([0-9]+)/
306
+ builds.collect { |build| [(build['id'].match regex)[1], build.search('td:first')[0].text] }
307
+ end
308
+
309
+ def upload(build_id, profile_name)
310
+ get("https://testflightapp.com/dashboard/builds/update/#{build_id}/")
311
+
312
+ profile_file = get_profile_file(profile_name)
313
+
314
+ form = page.form_with(:id => 'provision-form')
315
+ upload = form.file_uploads.first
316
+ upload.file_name = profile_file.path
317
+ form.submit
318
+
319
+ say_ok 'Submitted '+profile_file.path
320
+ end
321
+
322
+ def get_profile_file(profile_name)
323
+ Dir.glob(::File.expand_path('~') + '/Library/MobileDevice/Provisioning Profiles/*.mobileprovision') do |file|
324
+
325
+ ::File.open(file, "r") do |_file|
326
+ matches = /<key>Name<\/key>\s+<string>([^<]+)<\/string>/.match _file.read
327
+ if matches[1] == profile_name
328
+ return _file
329
+ end
330
+ end
331
+
332
+ end
333
+ end
334
+
335
+ private
336
+
337
+ def login!
338
+ form = page.forms[0]
339
+ form.username = self.username
340
+ form.password = self.password
341
+ form.submit
342
+ end
343
+ end
344
+
345
+ end
@@ -0,0 +1,54 @@
1
+ require 'cupertino'
2
+ require 'cupertino/provisioning_portal/helpers'
3
+ require 'cupertino/provisioning_portal/agent'
4
+
5
+ require 'mechanize'
6
+ require 'certified'
7
+
8
+ include Cupertino::ProvisioningPortal
9
+ include Cupertino::ProvisioningPortal::Helpers
10
+
11
+ module Cupertino
12
+ module ProvisioningPortal
13
+ HOST = 'developer.apple.com'
14
+
15
+ class UnsuccessfulAuthenticationError < RuntimeError; end
16
+ class UnexpectedContentError < RuntimeError; end
17
+
18
+ class Device < Struct.new(:name, :udid)
19
+ def to_s
20
+ "#{self.name} #{self.udid}"
21
+ end
22
+ end
23
+
24
+ class Certificate < Struct.new(:name, :type, :expiration_date, :status, :download_url) #:provisioning_profiles,
25
+ def to_s
26
+ "#{self.name}"
27
+ end
28
+ end
29
+
30
+ class AppID < Struct.new(:bundle_seed_id, :description, :development_properties, :distribution_properties)
31
+ def to_s
32
+ "#{self.bundle_seed_id}"
33
+ end
34
+ end
35
+
36
+ class ProvisioningProfile < Struct.new(:name, :type, :app_id, :status, :download_url, :edit_url)
37
+ def to_s
38
+ "#{self.name}"
39
+ end
40
+ end
41
+
42
+ class PassTypeID < Struct.new(:description, :id, :pass_certificates, :card_id)
43
+ def to_s
44
+ "#{self.id} #{self.description}"
45
+ end
46
+ end
47
+
48
+ class PassCertificate < Struct.new(:name, :status, :expiration_date, :certificate_id)
49
+ def to_s
50
+ "#{self.certificate_id}"
51
+ end
52
+ end
53
+ end
54
+ end
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omudid
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 3
9
+ version: 1.0.3
10
+ platform: ruby
11
+ authors:
12
+ - David Lawson
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2013-05-10 00:00:00 +10:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: bundler
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 3
30
+ version: "1.3"
31
+ type: :development
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: rake
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 0
42
+ version: "0"
43
+ type: :development
44
+ version_requirements: *id002
45
+ - !ruby/object:Gem::Dependency
46
+ name: cupertino
47
+ prerelease: false
48
+ requirement: &id003 !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ segments:
53
+ - 0
54
+ version: "0"
55
+ type: :runtime
56
+ version_requirements: *id003
57
+ - !ruby/object:Gem::Dependency
58
+ name: spinning_cursor
59
+ prerelease: false
60
+ requirement: &id004 !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ type: :runtime
68
+ version_requirements: *id004
69
+ - !ruby/object:Gem::Dependency
70
+ name: rainbow
71
+ prerelease: false
72
+ requirement: &id005 !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ type: :runtime
80
+ version_requirements: *id005
81
+ - !ruby/object:Gem::Dependency
82
+ name: commander
83
+ prerelease: false
84
+ requirement: &id006 !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ~>
87
+ - !ruby/object:Gem::Version
88
+ segments:
89
+ - 4
90
+ - 1
91
+ - 2
92
+ version: 4.1.2
93
+ type: :runtime
94
+ version_requirements: *id006
95
+ description: Conveniently add a UDID to the iOS Developer Portal, refresh a provisioning profile and download it. Uploading a new provisioning profile to TestFlight is also supported.
96
+ email:
97
+ - tech.lawson@gmail.com
98
+ executables:
99
+ - omudid
100
+ extensions: []
101
+
102
+ extra_rdoc_files: []
103
+
104
+ files:
105
+ - .gitignore
106
+ - Gemfile
107
+ - LICENSE.txt
108
+ - OneMoreUDID.gemspec
109
+ - README.md
110
+ - Rakefile
111
+ - bin/omudid
112
+ - lib/OneMoreUDID.rb
113
+ - lib/cupertino_compatibility.rb
114
+ has_rdoc: true
115
+ homepage: https://github.com/Papercloud/OneMoreUDID
116
+ licenses:
117
+ - MIT
118
+ post_install_message:
119
+ rdoc_options: []
120
+
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ segments:
128
+ - 0
129
+ version: "0"
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ segments:
135
+ - 0
136
+ version: "0"
137
+ requirements: []
138
+
139
+ rubyforge_project:
140
+ rubygems_version: 1.3.6
141
+ signing_key:
142
+ specification_version: 3
143
+ summary: Add one UDID to iOS Dev Portal & TestFlight
144
+ test_files: []
145
+