download-profiles 1.0.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c8533cc7f76d098767baf67703a2ebba51372001
4
+ data.tar.gz: 27a68f10c07d41eff9791e3e0c64b15e6eeb4fea
5
+ SHA512:
6
+ metadata.gz: f30c46e0902a6b79af8fb671b2f988fc6dd39139c38362a3951d24efd5ca12b41b1af2c2a030a3fad81efa2fb28407fa0afdbfd20daa54987cc5fc1492d5e1f7
7
+ data.tar.gz: 8c146d5e638c1dba3e9a0c7716ceb89e1b883547217bfc4f5a90abe6e0250bc9276e9a0933f8f7878eb1286a8d759fc16acea488051b5a726c149009803cdda8
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,66 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ download-profiles (1.0.0)
5
+ certified (>= 0.1.0)
6
+ commander (~> 4.2.0)
7
+ mechanize (~> 2.7.3)
8
+ nokogiri (~> 1.6.3)
9
+ security (~> 0.1.2)
10
+ term-ansicolor (~> 1.0.7)
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ certified (1.0.0)
16
+ commander (4.2.1)
17
+ highline (~> 1.6.11)
18
+ diff-lcs (1.2.5)
19
+ domain_name (0.5.23)
20
+ unf (>= 0.0.5, < 1.0.0)
21
+ highline (1.6.21)
22
+ http-cookie (1.0.2)
23
+ domain_name (~> 0.5)
24
+ mechanize (2.7.3)
25
+ domain_name (~> 0.5, >= 0.5.1)
26
+ http-cookie (~> 1.0)
27
+ mime-types (~> 2.0)
28
+ net-http-digest_auth (~> 1.1, >= 1.1.1)
29
+ net-http-persistent (~> 2.5, >= 2.5.2)
30
+ nokogiri (~> 1.4)
31
+ ntlm-http (~> 0.1, >= 0.1.1)
32
+ webrobots (>= 0.0.9, < 0.2)
33
+ mime-types (2.4.3)
34
+ mini_portile (0.6.2)
35
+ net-http-digest_auth (1.4)
36
+ net-http-persistent (2.9.4)
37
+ nokogiri (1.6.5)
38
+ mini_portile (~> 0.6.0)
39
+ ntlm-http (0.1.1)
40
+ rake (10.4.2)
41
+ rspec (3.1.0)
42
+ rspec-core (~> 3.1.0)
43
+ rspec-expectations (~> 3.1.0)
44
+ rspec-mocks (~> 3.1.0)
45
+ rspec-core (3.1.7)
46
+ rspec-support (~> 3.1.0)
47
+ rspec-expectations (3.1.2)
48
+ diff-lcs (>= 1.2.0, < 2.0)
49
+ rspec-support (~> 3.1.0)
50
+ rspec-mocks (3.1.3)
51
+ rspec-support (~> 3.1.0)
52
+ rspec-support (3.1.2)
53
+ security (0.1.3)
54
+ term-ansicolor (1.0.7)
55
+ unf (0.1.4)
56
+ unf_ext
57
+ unf_ext (0.0.6)
58
+ webrobots (0.1.1)
59
+
60
+ PLATFORMS
61
+ ruby
62
+
63
+ DEPENDENCIES
64
+ download-profiles!
65
+ rake
66
+ rspec
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2012 Mattt Thompson (http://mattt.me/)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,182 @@
1
+ ![Cupertino](https://raw.github.com/nomad/nomad.github.io/assets/cupertino-banner.png)
2
+
3
+ Automate administrative tasks that you would normally have to do through the Apple Dev Center websites. Life's too short to manage device identifiers by hand!
4
+
5
+ > Cupertino is named after [Cupertino, CA](http://en.wikipedia.org/wiki/Cupertino,_California): home to Apple, Inc.'s world headquarters.
6
+ > It's part of a series of world-class command-line utilities for iOS development, which includes [Shenzhen](https://github.com/mattt/shenzhen) (Building & Distribution), [Houston](https://github.com/mattt/houston) (Push Notifications), [Venice](https://github.com/mattt/venice) (In-App Purchase Receipt Verification), [Dubai](https://github.com/mattt/dubai) (Passbook pass generation), and [Nashville](https://github.com/nomad/nashville) (iTunes Store API).
7
+
8
+ ## Requirements
9
+
10
+ Cupertino requires the [Xcode Command Line Tools](https://developer.apple.com/xcode/), which can be installed with the following command:
11
+
12
+ ```
13
+ $ xcode-select --install
14
+ ```
15
+
16
+ ## Installation
17
+
18
+ ```
19
+ $ gem install cupertino
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ### Authentication
25
+
26
+ ```
27
+ $ ios login
28
+ ```
29
+
30
+ _Credentials are saved in the Keychain. You will not be prompted for your username or password by commands while you are logged in. (Mac only)_
31
+
32
+ ### Devices
33
+
34
+ ```
35
+ $ ios devices:list
36
+
37
+ +------------------------------+---------------------------------------+
38
+ | Listing 2 devices. You can register 98 additional devices. |
39
+ +---------------------------+------------------------------------------+
40
+ | Device Name | Device Identifier |
41
+ +---------------------------+------------------------------------------+
42
+ | Johnny Appleseed iPad | 0123456789012345678901234567890123abcdef |
43
+ | Johnny Appleseed iPhone | abcdef0123456789012345678901234567890123 |
44
+ +---------------------------+------------------------------------------+
45
+
46
+ $ ios devices:add "iPad 1"=abc123
47
+ $ ios devices:add "iPad 2"=def456 "iPad 3"=ghi789 ...
48
+ ```
49
+
50
+ ### Provisioning Profiles
51
+
52
+ ```
53
+ $ ios profiles:list
54
+
55
+ +----------------------------------+--------------+---------+
56
+ | Profile | App ID | Status |
57
+ +----------------------------------+--------------+---------+
58
+ | iOS Team Provisioning Profile: * | ABCDEFG123.* | Valid |
59
+ +----------------------------------+--------------+---------+
60
+ ```
61
+
62
+ ---
63
+
64
+ ```
65
+ $ ios profiles:manage:devices
66
+ ```
67
+
68
+ _Opens an editor with a list of devices, each of which can be commented / uncommented to turn them off / on for that provisioning profile._
69
+
70
+ ```
71
+ # Comment / Uncomment Devices to Turn Off / On for Provisioning Profile
72
+ Johnny Appleseed iPad 0123456789012345678901234567890123abcdef
73
+ # Johnny Appleseed iPhone abcdef0123456789012345678901234567890123
74
+ ```
75
+
76
+ ```
77
+ $ ios profiles:devices:add MyApp_Development_Profile "Johnny Appleseed iPad"=0123456789012345678901234567890123abcdef "Johnny Appleseed iPhone"=abcdef0123456789012345678901234567890123
78
+ ```
79
+
80
+ _Adds (without an editor) a list of devices to a provisioning profile_
81
+
82
+ ```
83
+ $ ios profiles:devices:remove MyApp_Development_Profile "Johnny Old iPad"=0123456789012345678901234567890123abcdef "Johnny Old iPhone"=abcdef0123456789012345678901234567890123
84
+ ```
85
+
86
+ _Removes (without an editor) a list of devices from a provisioning profile_
87
+
88
+ ---
89
+
90
+ ```
91
+ $ ios profiles:devices:list MyApp_Development_Profile
92
+
93
+ +--------------------------+------------------------------------------+--------+
94
+ | Listing devices for provisioning profile MyApp_Development_Profile |
95
+ +--------------------------+------------------------------------------+--------+
96
+ | Device Name | Device Identifier | Active |
97
+ +--------------------------+------------------------------------------+--------+
98
+ | Person's iPhone 5 | 888888883e48a3e0458aab2691d565a8a63f7888 | Y |
99
+ +--------------------------+------------------------------------------+--------+
100
+
101
+ ```
102
+
103
+ ### App IDs
104
+
105
+ ```
106
+ $ ios app_ids:list
107
+
108
+ +-----------------------------+------------------------+-------------------+-------------------+
109
+ | Bundle Seed ID | Description | Development | Distribution |
110
+ +-----------------------------+------------------------+-------------------+-------------------+
111
+ | 123ABCDEFG.com.mattt.bundle | App Bundle Description | Passes | Passes |
112
+ | | | Data Protection | Data Protection |
113
+ | | | iCloud | iCloud |
114
+ | | | In-App Purchase | In-App Purchase |
115
+ | | | Game Center | Game Center |
116
+ | | | Push Notification | Push Notification |
117
+ +-----------------------------+------------------------+-------------------+-------------------+
118
+ ```
119
+
120
+ ```
121
+ $ ios app_ids:add "App Bundle Description"=123ABCDEFG.com.mattt.bundle
122
+ ```
123
+
124
+ ### Certificates
125
+
126
+ ```
127
+ $ ios certificates:list
128
+
129
+ +------------------+----------------------------------+-----------------+--------+
130
+ | Name | Provisioning Profiles | Expiration Date | Status |
131
+ +------------------+----------------------------------+-----------------+--------+
132
+ | Johnny Appleseed | iOS Team Provisioning Profile: * | Dec 23, 2012 | Issued |
133
+ +------------------+----------------------------------+-----------------+--------+
134
+ ```
135
+
136
+ ```
137
+ $ ios certificates:download
138
+ $ ios certificates:download --type distribution
139
+ $ ios certificates:download NAME
140
+ ```
141
+
142
+ ## CSV Output
143
+
144
+ The following commands will format their output as [comma-separated values](http://en.wikipedia.org/wiki/Comma-separated_values) when the `--format csv` argument is passed:
145
+
146
+ - `app_ids:list`
147
+ - `devices:list`
148
+ - `profiles:list`
149
+ - `profiles:manage:devices:list`
150
+
151
+ ## Commands
152
+
153
+ - `login`
154
+ - `logout`
155
+ - `devices:add`
156
+ - `devices:list`
157
+ - `profiles:list`
158
+ - `profiles:manage:devices`
159
+ - `profiles:manage:devices:add`
160
+ - `profiles:manage:devices:remove`
161
+ - `profiles:download`
162
+ - `profiles:download:all`
163
+ - `profiles:devices:list`
164
+ - `certificates:list`
165
+ - `certificates:download`
166
+ - `app_ids:list`
167
+
168
+ ## Proxies
169
+
170
+ Cupertino will access the provisioning portal through a proxy if the `HTTP_PROXY` environment variable is set, with optional credentials `HTTP_PROXY_USER` and `HTTP_PROXY_PASSWORD`.
171
+
172
+ ## Contact
173
+
174
+ Mattt Thompson
175
+
176
+ - http://github.com/mattt
177
+ - http://twitter.com/mattt
178
+ - m@mattt.me
179
+
180
+ ## License
181
+
182
+ Cupertino is available under the MIT license. See the LICENSE file for more info.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler"
2
+ Bundler.setup
3
+
4
+ gemspec = eval(File.read("download-profiles.gemspec"))
5
+
6
+ task :build => "#{gemspec.full_name}.gem"
7
+
8
+ file "#{gemspec.full_name}.gem" => gemspec.files + ["download-profiles.gemspec"] do
9
+ system "gem build download-profiles.gemspec"
10
+ end
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'commander/import'
4
+ require 'term/ansicolor'
5
+ require 'csv'
6
+
7
+ require 'download-profiles'
8
+
9
+ HighLine.track_eof = false # Fix for built-in Ruby
10
+ Signal.trap("INT") {} # Suppress backtrace when exiting command
11
+
12
+ program :version, Cupertino::VERSION
13
+ program :description, 'CLI tool for downloading iOS and Mac provisioning profiles'
14
+
15
+ program :help, 'Author', 'Radek Pietruszewski <this.is@radex.io>'
16
+ program :help, 'Website', 'https://github.com/radex'
17
+ program :help_formatter, :compact
18
+
19
+ default_command :download
20
+
21
+ require 'download-profiles/provisioning_portal'
22
+ require 'download-profiles/provisioning_portal/commands'
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ require "download-profiles/version"
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "download-profiles"
8
+ s.license = "MIT"
9
+ s.authors = ["Mattt Thompson", "Radek Pietruszewski"]
10
+ s.email = "this.is@radex.io"
11
+ s.homepage = 'https://github.com/radex/download-profiles'
12
+ s.version = Cupertino::VERSION
13
+ s.platform = Gem::Platform::RUBY
14
+ s.summary = "download-profiles"
15
+ s.description = "CLI tool for downloading iOS and Mac provisioning profiles"
16
+
17
+ s.add_dependency "commander", "~> 4.2.0"
18
+ s.add_dependency "term-ansicolor", "~> 1.0.7"
19
+ s.add_dependency "mechanize", "~> 2.7.3"
20
+ s.add_dependency "nokogiri", "~> 1.6.3"
21
+ s.add_dependency "security", "~> 0.1.2"
22
+ s.add_dependency "certified", ">= 0.1.0"
23
+
24
+ s.add_development_dependency "rspec"
25
+ s.add_development_dependency "rake"
26
+
27
+ s.files = Dir["./**/*"].reject { |file| file =~ /\.\/(bin|log|pkg|script|spec|test|vendor)/ }
28
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
29
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
30
+ s.require_paths = ["lib"]
31
+ end
@@ -0,0 +1,146 @@
1
+ require 'mechanize'
2
+ require 'security'
3
+ require 'uri'
4
+ require 'json'
5
+ require 'date'
6
+ require 'time'
7
+ require 'logger'
8
+
9
+ module Cupertino
10
+ module ProvisioningPortal
11
+ class Agent < ::Mechanize
12
+ attr_accessor :username, :password, :team, :team_id
13
+
14
+ def initialize
15
+ super
16
+ @profile_csrf_headers = {}
17
+ self.user_agent_alias = 'Mac Safari'
18
+
19
+ self.log ||= Logger.new(STDOUT)
20
+ self.log.level = Logger::ERROR
21
+
22
+ if ENV['HTTP_PROXY']
23
+ uri = URI.parse(ENV['HTTP_PROXY'])
24
+ user = ENV['HTTP_PROXY_USER'] if ENV['HTTP_PROXY_USER']
25
+ password = ENV['HTTP_PROXY_PASSWORD'] if ENV['HTTP_PROXY_PASSWORD']
26
+
27
+ set_proxy(uri.host, uri.port, user || uri.user, password || uri.password)
28
+ end
29
+
30
+ pw = Security::InternetPassword.find(:server => Cupertino::ProvisioningPortal::HOST)
31
+ @username, @password = pw.attributes['acct'], pw.password if pw
32
+ end
33
+
34
+ def username=(value)
35
+ @username = value
36
+
37
+ pw = Security::InternetPassword.find(:a => self.username, :server => Cupertino::ProvisioningPortal::HOST)
38
+ @password = pw.password if pw
39
+ end
40
+
41
+ def get(uri, parameters = [], referer = nil, headers = {})
42
+ uri = ::File.join("https://#{Cupertino::ProvisioningPortal::HOST}", uri) unless /^https?/ === uri
43
+
44
+ 3.times do
45
+ super(uri, parameters, referer, headers)
46
+
47
+ return page unless page.respond_to?(:title)
48
+
49
+ case page.title
50
+ when /Sign in with your Apple ID/
51
+ login!
52
+ when /Select Team/
53
+ select_team!
54
+ else
55
+ return page
56
+ end
57
+ end
58
+
59
+ raise UnsuccessfulAuthenticationError
60
+ end
61
+
62
+ def list_profiles(platform, type)
63
+ url = case type
64
+ when :development
65
+ "https://developer.apple.com/account/#{platform}/profile/profileList.action?type=limited"
66
+ when :distribution
67
+ "https://developer.apple.com/account/#{platform}/profile/profileList.action?type=production"
68
+ else
69
+ raise ArgumentError, 'Provisioning profile type must be :development or :distribution'
70
+ end
71
+
72
+ self.pluggable_parser.default = Mechanize::File
73
+ get(url)
74
+
75
+ regex = /profileDataURL = "([^"]*)"/
76
+ profile_data_url = (page.body.match regex or raise UnexpectedContentError)[1]
77
+
78
+ profile_data_url += case type
79
+ when :development
80
+ '&type=limited'
81
+ when :distribution
82
+ '&type=production'
83
+ end
84
+
85
+ post(profile_data_url)
86
+ @profile_csrf_headers = {
87
+ 'csrf' => page.response['csrf'],
88
+ 'csrf_ts' => page.response['csrf_ts']
89
+ }
90
+
91
+ @profile_csrf_headers = {
92
+ 'csrf' => page.response['csrf'],
93
+ 'csrf_ts' => page.response['csrf_ts']
94
+ }
95
+
96
+ profile_data = page.content
97
+ parsed_profile_data = JSON.parse(profile_data)
98
+
99
+ profiles = []
100
+ parsed_profile_data['provisioningProfiles'].each do |row|
101
+ profile = ProvisioningProfile.new
102
+ profile.name = row['name']
103
+ profile.type = type
104
+ profile.platform = platform
105
+ profile.status = row['status']
106
+ profile.expiration = (Time.parse(row['dateExpire']) rescue nil)
107
+ profile.download_url = "https://developer.apple.com/account/#{platform}/profile/profileContentDownload.action?displayId=#{row['provisioningProfileId']}"
108
+ profile.edit_url = "https://developer.apple.com/account/#{platform}/profile/profileEdit.action?provisioningProfileId=#{row['provisioningProfileId']}"
109
+ profile.identifier = row['UUID']
110
+ profiles << profile
111
+ end
112
+
113
+ profiles
114
+ end
115
+
116
+ def download_profile(profile)
117
+ ext = (profile.platform == :ios) ? 'mobileprovision' : 'provisionprofile'
118
+ self.pluggable_parser.default = Mechanize::Download
119
+ download = get(profile.download_url)
120
+ download.save!(::File.expand_path("~/Library/MobileDevice/Provisioning Profiles/#{profile.identifier}.#{ext}"))
121
+ download.filename
122
+ end
123
+
124
+ private
125
+
126
+ def login!
127
+ if form = page.forms.first
128
+ form.fields_with(type: 'text').first.value = self.username
129
+ form.fields_with(type: 'password').first.value = self.password
130
+
131
+ form.submit
132
+ end
133
+ end
134
+
135
+ def select_team!
136
+ if form = page.form_with(:name => 'saveTeamSelection')
137
+ team_option = form.radiobutton_with(:value => self.team_id)
138
+ team_option.check
139
+
140
+ button = form.button_with(:name => 'action:saveTeamSelection!save')
141
+ form.click_button(button)
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,17 @@
1
+ command :login do |c|
2
+ c.syntax = 'ios login'
3
+ c.summary = 'Save account credentials'
4
+
5
+ c.action do |args, options|
6
+ say_warning "You are already authenticated" if Security::InternetPassword.find(:server => Cupertino::ProvisioningPortal::HOST)
7
+
8
+ user = ask "Username:"
9
+ pass = password "Password:"
10
+
11
+ if Security::InternetPassword.add(Cupertino::ProvisioningPortal::HOST, user, pass)
12
+ say_ok "Account credentials saved"
13
+ else
14
+ say_error "Error saving credentials"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ command :logout do |c|
2
+ c.syntax = 'ios logout'
3
+ c.summary = 'Remove account credentials'
4
+
5
+ c.action do |args, options|
6
+ say_error "You are not authenticated" and abort unless Security::InternetPassword.find(:server => Cupertino::ProvisioningPortal::HOST)
7
+
8
+ if Security::InternetPassword.delete(:server => Cupertino::ProvisioningPortal::HOST)
9
+ say_ok "Account credentials removed"
10
+ else
11
+ say_error "Error removing credentials"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,41 @@
1
+ command :download do |c|
2
+ c.syntax = 'download-profiles'
3
+ c.summary = 'Download provisioning profiles'
4
+ c.description = "By default, downloads and installs all (development, distribution, iOS, Mac) provisioning profiles. Pass appropriate options to constrain."
5
+
6
+ c.option '--platform [PLATFORM]', [:ios, :mac, :all], "Platform of profile (ios, mac or all)"
7
+ c.option '--type [TYPE]', [:development, :distribution, :all], "Type of profile (development, distribution or all)"
8
+
9
+ c.action do |args, options|
10
+ # parse options
11
+ platform = options.platform ? options.platform.downcase.to_sym : :all
12
+ type = options.type ? options.type.downcase.to_sym : :all
13
+
14
+ platforms = (platform == :all) ? [:ios, :mac] : [platform]
15
+ types = (type == :all) ? [:development, :distribution] : [type]
16
+
17
+ # get list of profiles
18
+ profiles =
19
+ platforms.product(types).flat_map { |platform, type| try{agent.list_profiles(platform, type)} }
20
+ .select { |profile| profile.status == 'Active'}
21
+
22
+ say_warning "No active #{type} profiles found." and abort if profiles.empty?
23
+
24
+ # download
25
+ failure = false
26
+ profiles.each do |profile|
27
+ if filename = agent.download_profile(profile)
28
+ say "Downloaded #{filename}"
29
+ else
30
+ say_error "Could not download profile: '#{profile.name}'"
31
+ failure = true
32
+ end
33
+ end
34
+
35
+ if failure
36
+ say_warning "Not all profiles downloaded successfully"
37
+ else
38
+ say_ok "All profiles downloaded successfully"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,15 @@
1
+ include Cupertino::ProvisioningPortal
2
+
3
+ require 'download-profiles/provisioning_portal/helpers'
4
+ include Cupertino::ProvisioningPortal::Helpers
5
+
6
+ global_option('-u', '--username USER', 'Username') { |arg| agent.username = arg unless arg.nil? }
7
+ global_option('-p', '--password PASSWORD', 'Password') { |arg| agent.password = arg unless arg.nil? }
8
+ global_option('--team TEAM', 'Team') { |arg| agent.team = arg if arg }
9
+ global_option('--info', 'Set log level to INFO') { agent.log.level = Logger::INFO }
10
+ global_option('--debug', 'Set log level to DEBUG') { agent.log.level = Logger::DEBUG }
11
+ global_option('--format FORMAT', [:table, :csv], "Set output format (default: table)")
12
+
13
+ require 'download-profiles/provisioning_portal/commands/profiles'
14
+ require 'download-profiles/provisioning_portal/commands/login'
15
+ require 'download-profiles/provisioning_portal/commands/logout'
@@ -0,0 +1,65 @@
1
+ # Monkey Patch Commander::UI to alias password to avoid conflicts
2
+ module Commander::UI
3
+ alias :pw :password
4
+ end
5
+
6
+ class String
7
+ include Term::ANSIColor
8
+ end
9
+
10
+ module Cupertino
11
+ module ProvisioningPortal
12
+ module Helpers
13
+ def agent
14
+ unless @agent
15
+ @agent = Cupertino::ProvisioningPortal::Agent.new
16
+
17
+ @agent.instance_eval do
18
+ def username
19
+ @username ||= ask "Username:"
20
+ end
21
+
22
+ def password
23
+ @password ||= pw "Password:"
24
+ end
25
+
26
+ def team_id
27
+ unless @team_id
28
+ teams = []
29
+ page.form_with(:name => 'saveTeamSelection').radiobuttons.each do |radio|
30
+ name = page.search(".label-primary[for=\"#{radio.dom_id}\"]").first.text.strip
31
+ programs = page.search(".label-secondary[for=\"#{radio.dom_id}\"]").first.text.strip.split(/\,\s+/)
32
+ teams << Team.new(name, programs, radio.value)
33
+ end
34
+
35
+ unless team = teams.detect{|t| t.name == @team || t.identifier == @team}
36
+ team = choose("Select a team:", *teams)
37
+ end
38
+
39
+ @team_id = team.identifier
40
+ end
41
+
42
+ @team_id
43
+ end
44
+ end
45
+ end
46
+
47
+ @agent
48
+ end
49
+
50
+ def pluralize(n, singular, plural = nil)
51
+ n.to_i == 1 ? "1 #{singular}" : "#{n} #{plural || singular + 's'}"
52
+ end
53
+
54
+ def try
55
+ return unless block_given?
56
+
57
+ begin
58
+ yield
59
+ rescue UnsuccessfulAuthenticationError
60
+ say_error "Could not authenticate with Apple Developer Center. Check that your username & password are correct, and that your membership is valid and all pending Terms of Service & agreements are accepted. If this problem continues, try logging into https://developer.apple.com/membercenter/ from a browser to see what's going on." and abort
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,19 @@
1
+ require 'mechanize'
2
+ require 'certified'
3
+
4
+ module Cupertino
5
+ module ProvisioningPortal
6
+ HOST = "developer.apple.com"
7
+
8
+ class UnsuccessfulAuthenticationError < RuntimeError; end
9
+ class UnexpectedContentError < RuntimeError; end
10
+
11
+ class ProvisioningProfile < Struct.new(:name, :type, :platform, :app_id, :status, :expiration, :download_url, :edit_url, :identifier)
12
+ def to_s
13
+ "#{self.name}"
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ require 'download-profiles/provisioning_portal/agent'
@@ -0,0 +1,3 @@
1
+ module Cupertino
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1 @@
1
+ require 'download-profiles/version'
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: download-profiles
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Mattt Thompson
8
+ - Radek Pietruszewski
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-01-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: commander
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 4.2.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 4.2.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: term-ansicolor
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 1.0.7
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 1.0.7
42
+ - !ruby/object:Gem::Dependency
43
+ name: mechanize
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: 2.7.3
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: 2.7.3
56
+ - !ruby/object:Gem::Dependency
57
+ name: nokogiri
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: 1.6.3
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 1.6.3
70
+ - !ruby/object:Gem::Dependency
71
+ name: security
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: 0.1.2
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: 0.1.2
84
+ - !ruby/object:Gem::Dependency
85
+ name: certified
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 0.1.0
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: 0.1.0
98
+ - !ruby/object:Gem::Dependency
99
+ name: rspec
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: rake
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ description: CLI tool for downloading iOS and Mac provisioning profiles
127
+ email: this.is@radex.io
128
+ executables:
129
+ - download-profiles
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - "./Gemfile"
134
+ - "./Gemfile.lock"
135
+ - "./LICENSE"
136
+ - "./README.md"
137
+ - "./Rakefile"
138
+ - "./download-profiles.gemspec"
139
+ - "./lib/download-profiles.rb"
140
+ - "./lib/download-profiles/provisioning_portal.rb"
141
+ - "./lib/download-profiles/provisioning_portal/agent.rb"
142
+ - "./lib/download-profiles/provisioning_portal/commands.rb"
143
+ - "./lib/download-profiles/provisioning_portal/commands/login.rb"
144
+ - "./lib/download-profiles/provisioning_portal/commands/logout.rb"
145
+ - "./lib/download-profiles/provisioning_portal/commands/profiles.rb"
146
+ - "./lib/download-profiles/provisioning_portal/helpers.rb"
147
+ - "./lib/download-profiles/version.rb"
148
+ - bin/download-profiles
149
+ homepage: https://github.com/radex/download-profiles
150
+ licenses:
151
+ - MIT
152
+ metadata: {}
153
+ post_install_message:
154
+ rdoc_options: []
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ required_rubygems_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ requirements: []
168
+ rubyforge_project:
169
+ rubygems_version: 2.2.2
170
+ signing_key:
171
+ specification_version: 4
172
+ summary: download-profiles
173
+ test_files: []