pem 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7e40494d49183f73e7f0abc28403041ef5b77b18
4
- data.tar.gz: 16090d76a589dc478e3089330bb43603a359b7bb
3
+ metadata.gz: 71fb127b623bfd0d13e75886377470357f7ee9e4
4
+ data.tar.gz: 6d647f38ba8ed62457e76705910c141cb48319af
5
5
  SHA512:
6
- metadata.gz: 1fe30f256529ce7101055c46eb7f05b40fe3e186a414672556a84799fa9fc785a83b6e34c07981a8edd6fff2a6a63325170bf623adf03f05fb5fe4871ac727ef
7
- data.tar.gz: 53d63066a1939db9240ee7f2808563d0559470e5ecb9795cd9686d634750178d453a0bb40c2c2b0616494bd4b3efbe2bff5ab1f229353b7167c86945913a103d
6
+ metadata.gz: b2f9937ec278727792b969e1fe17b3851090bd8b2ab1ab7a71d8c63e87112011fee238d034d4d07366f9784513e434884ff463cd97fd60f45963b1b97f231f34
7
+ data.tar.gz: b62966cc13a6f027a479852952f50f5b2c447584d9b5180a387eea02d8f2f7c5b12092a8a419d55aa9e78b62b215265766f287beb0ca5954d8f29d836ab1a06b
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Felix Krause
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,105 @@
1
+ <p align="center">
2
+ <a href="https://github.com/KrauseFx/deliver">Deliver</a> &bull;
3
+ <a href="https://github.com/KrauseFx/snapshot">Snapshot</a> &bull;
4
+ <a href="https://github.com/KrauseFx/frameit">FrameIt</a> &bull;
5
+ <b>PEM</b>
6
+ </p>
7
+ -------
8
+
9
+ <p align="center">
10
+ <img src="assets/pem.png">
11
+ </p>
12
+
13
+ Pem
14
+ ============
15
+
16
+ [![Twitter: @KauseFx](https://img.shields.io/badge/contact-@KrauseFx-blue.svg?style=flat)](https://twitter.com/KrauseFx)
17
+ [![License](http://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/KrauseFx/pem/blob/master/LICENSE)
18
+ [![Gem](https://img.shields.io/gem/v/pem.svg?style=flat)](http://rubygems.org/gems/pem)
19
+
20
+
21
+ Tired of manually creating and maintaining your push notification profiles? Tired of manually generating ```pem``` files for your server?
22
+
23
+ ```PEM``` does all that for, just by running ```pem```!
24
+
25
+ -------
26
+ [Features](#features) &bull;
27
+ [Installation](#installation) &bull;
28
+ [Usage](#usage) &bull;
29
+ [How does it work?](#how-does-it-work) &bull;
30
+ [Tips](#tips) &bull;
31
+ [Need help?](#need-help)
32
+
33
+ -------
34
+
35
+ # Features
36
+ Well, it's actually just one: Generate the ```pem``` file for your server.
37
+
38
+
39
+ Check out this gif:
40
+
41
+ ![assets/PEMRecording.gif](assets/PEMRecording.gif)
42
+
43
+ # Installation
44
+ sudo gem install pem
45
+
46
+ Make sure, you have the latest version of the Xcode command line tools installed:
47
+
48
+ xcode-select --install
49
+
50
+ # Usage
51
+
52
+ pem
53
+ Yes, that's the whole command!
54
+
55
+ This does the following:
56
+
57
+ - Verifies the production push certificate looks alright
58
+ - Download the certificate
59
+ - Generates a new ```.pem``` file in the current working directory, which you can upload to your server
60
+
61
+ You can pass parameters like this:
62
+
63
+ pem -a at.felixkrause.app -u username
64
+
65
+ ## Environment Variables
66
+ In case you prefer environment variables:
67
+
68
+ - ```PEM_USERNAME```
69
+ - ```PEM_APP_IDENTIFIER```
70
+ - ```PEM_CERT_SIGNING_REQUEST``` which is used, in case a new profile needs to be created
71
+
72
+ # How does it work?
73
+ There are 2 actions involved:
74
+
75
+ - Accessing the ```iOS Dev Center``` to download the latest ```aps_production.cer```. See: [developer_center.rb](https://github.com/KrauseFx/PEM/blob/master/lib/pem/developer_center.rb).
76
+ - Generating all the necessary profiles and files to prepare the finished ```.pem``` file. See: [cert_manager.rb](https://github.com/KrauseFx/PEM/blob/master/lib/pem/cert_manager.rb).
77
+ ```PEM``` will create a temporary keychain called ```PEM.keychain``` and use that to generate the necessary profiles. That means ```PEM``` will not pollute your default keychain.
78
+
79
+
80
+ ## How is my password stored?
81
+ ```PEM``` uses the password manager from [```Deliver```](https://github.com/KrauseFx/deliver#can-i-trust-deliver). Take a look the [Deliver README](https://github.com/KrauseFx/deliver#can-i-trust-deliver) for more information.
82
+
83
+ # Tips
84
+ ## Use the ```Provisioning Quicklook plugin```
85
+ Download and install the [Provisioning Plugin](https://github.com/chockenberry/Provisioning).
86
+
87
+ It will show you the ```pem``` files like this:
88
+ ![assets/QuickLookScreenshot.png](assets/QuickLookScreenshot.png)
89
+
90
+
91
+ # Need help?
92
+ - If there is a technical problem with ```PEM```, submit an issue. Run ```pem --trace``` to get the stacktrace.
93
+ - I'm available for contract work - drop me an email: pem@felixkrause.at
94
+
95
+ # License
96
+ This project is licensed under the terms of the MIT license. See the LICENSE file.
97
+
98
+ # Contributing
99
+
100
+ 1. Create an issue to discuss about your idea
101
+ 2. Fork it (https://github.com/KrauseFx/pem/fork)
102
+ 3. Create your feature branch (`git checkout -b my-new-feature`)
103
+ 4. Commit your changes (`git commit -am 'Add some feature'`)
104
+ 5. Push to the branch (`git push origin my-new-feature`)
105
+ 6. Create a new Pull Request
data/bin/pem ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.push File.expand_path("../../lib", __FILE__)
4
+
5
+ require 'pem'
6
+ require 'commander/import'
7
+ require 'pem/update_checker'
8
+ require 'deliver/password_manager'
9
+
10
+ HighLine.track_eof = false
11
+
12
+
13
+ # Commander
14
+ program :version, PEM::VERSION
15
+ program :description, 'CLI for \'PEM\' - Automate maintaining of push notification profiles.'
16
+ program :help, 'Author', 'Felix Krause <krausefx@gmail.com>'
17
+ program :help, 'Website', 'http://felixkrause.at'
18
+ program :help, 'GitHub', 'https://github.com/krausefx/PEM'
19
+ program :help_formatter, :compact
20
+
21
+ global_option('--verbose') { $verbose = true }
22
+
23
+ default_command :run
24
+
25
+
26
+ command :renew do |c|
27
+ c.syntax = 'pem renew'
28
+ c.description = 'Renews the certificate (in case it expired) and returns the path to the generated pem file'
29
+
30
+ c.option '-a', '--identifier STRING', String, 'The bundle identifier of your app'
31
+ c.option '-u', '--username STRING', String, 'Your Apple ID username'
32
+
33
+ c.action do |args, options|
34
+ app = app_identifier(options)
35
+ username(options)
36
+
37
+ PEM::UpdateChecker.verify_latest_version
38
+
39
+ path = PEM::CertManager.new.run(app)
40
+
41
+ if path
42
+ output = "./production_#{app}.pem"
43
+ FileUtils.mv(path, output)
44
+ puts output
45
+ end
46
+ end
47
+ end
48
+
49
+ default_command :renew
50
+
51
+ def username(options)
52
+ user = nil
53
+ user = options.username if options.username
54
+ user = ENV["PEM_USERNAME"] if ENV["PEM_USERNAME"]
55
+
56
+ Deliver::PasswordManager.shared_manager(user) if user
57
+ end
58
+
59
+ def app_identifier(options)
60
+ return options.identifier if options.identifier
61
+ return ENV["PEM_APP_IDENTIFIER"] if ENV["PEM_APP_IDENTIFIER"]
62
+ return ask("App Identifier (Bundle ID, e.g. at.felixkrause.app): ")
63
+ end
data/lib/pem.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'json'
2
+ require 'pem/version'
3
+ require 'pem/helper'
4
+ require 'pem/dependency_checker'
5
+ require 'pem/developer_center'
6
+ require 'pem/cert_manager'
7
+
8
+ # Third Party code
9
+ require 'colored'
10
+
11
+ module PEM
12
+ TMP_FOLDER = "/tmp/PEM/"
13
+ end
@@ -0,0 +1,52 @@
1
+ module PEM
2
+ class CertManager
3
+ # Download the cert, do all kinds of Keychain related things
4
+ def run(app_identifier)
5
+ # Keychain (security) documentation: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/security.1.html
6
+ # Old project, which might be helpful: https://github.com/jprichardson/keychain_manager
7
+
8
+ Helper.log.info "Refreshing push notification profiles for app '#{app_identifier}'"
9
+
10
+ dev = PEM::DeveloperCenter.new
11
+
12
+ keychain = "PEM.keychain"
13
+
14
+ cert_file = dev.fetch_cer_file(app_identifier)
15
+ rsa_file = [TMP_FOLDER, 'myrsa'].join('/')
16
+
17
+ previous_keychain = command("security default-keychain")
18
+
19
+
20
+
21
+ command("security create-keychain -p '' #{keychain}") # create a new keychain for this type
22
+
23
+ command("security list-keychains -d user -s #{keychain}") # add it to the list of keychains
24
+
25
+ command("openssl genrsa -out '#{rsa_file}' 2048") # generate a new RSA file
26
+ command("security import '#{rsa_file}' -P '' -k #{keychain}") # import the RSA file into the Keychain
27
+ command("security import '#{cert_file}' -k #{keychain}") # import the profile from Apple into the Keychain
28
+
29
+ p12_file = [TMP_FOLDER, "push_prod.12"].join('/')
30
+
31
+ command("security export -k '#{keychain}' -t all -f pkcs12 -P '' -o #{p12_file}") # export code signing identity
32
+
33
+ pem_file = [TMP_FOLDER, "production_#{app_identifier}.pem"].join('')
34
+ command("openssl pkcs12 -passin pass: -nodes -in #{p12_file} -out #{pem_file}")
35
+
36
+ command("security delete-keychain #{keychain}")
37
+
38
+ command("security list-keychains -d user -s #{previous_keychain}") # switch back to default keychain
39
+
40
+ return pem_file
41
+ end
42
+
43
+ private
44
+ # Output the command, execute it, return its result
45
+ def command(com)
46
+ puts com.yellow
47
+ result = `#{com}`
48
+ puts result if (result || '').length > 0
49
+ result
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,32 @@
1
+ module PEM
2
+ class DependencyChecker
3
+ def self.check_dependencies
4
+ self.check_phantom_js
5
+ self.check_xcode_select
6
+ end
7
+
8
+ def self.check_phantom_js
9
+ if `which phantomjs`.length == 0
10
+ # Missing brew dependency
11
+ Helper.log.fatal '#############################################################'
12
+ Helper.log.fatal "# You have to install phantomjs to use PEM"
13
+ Helper.log.fatal "# phantomjs is used to control the iTunesConnect frontend"
14
+ Helper.log.fatal "# Install Homebrew using http://brew.sh/" if `which brew`.length == 0
15
+ Helper.log.fatal "# Run 'brew update && brew install phantomjs' and start PEM again"
16
+ Helper.log.fatal '#############################################################'
17
+ raise "Run 'brew update && brew install phantomjs' and start PEM again"
18
+ end
19
+ end
20
+
21
+ def self.check_xcode_select
22
+ unless `xcode-select -v`.include?"xcode-select version "
23
+ Helper.log.fatal '#############################################################'
24
+ Helper.log.fatal "# You have to install the Xcode commdand line tools to use PEM"
25
+ Helper.log.fatal "# Install the latest version of Xcode from the AppStore"
26
+ Helper.log.fatal "# Run xcode-select --install to install the developer tools"
27
+ Helper.log.fatal '#############################################################'
28
+ raise "Run 'xcode-select --install' and start PEM again"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,312 @@
1
+ require 'pry'
2
+ require 'deliver/password_manager'
3
+ require 'open-uri'
4
+
5
+ require 'capybara'
6
+ require 'capybara/poltergeist'
7
+
8
+ module PEM
9
+ class DeveloperCenter
10
+ # This error occurs only if there is something wrong with the given login data
11
+ class DeveloperCenterLoginError < StandardError
12
+ end
13
+
14
+ # This error can occur for many reaons. It is
15
+ # usually raised when a UI element could not be found
16
+ class DeveloperCenterGeneralError < StandardError
17
+ end
18
+
19
+ include Capybara::DSL
20
+
21
+ DEVELOPER_CENTER_URL = "https://developer.apple.com/devcenter/ios/index.action"
22
+ APP_IDS_URL = "https://developer.apple.com/account/ios/identifiers/bundle/bundleList.action"
23
+
24
+ # Strings
25
+ PRODUCTION_SSL_CERTIFICATE_TITLE = "Production SSL Certificate"
26
+
27
+ def initialize
28
+ FileUtils.mkdir_p TMP_FOLDER
29
+
30
+ DependencyChecker.check_dependencies
31
+
32
+ Capybara.run_server = false
33
+ Capybara.default_driver = :poltergeist
34
+ Capybara.javascript_driver = :poltergeist
35
+ Capybara.current_driver = :poltergeist
36
+ Capybara.app_host = DEVELOPER_CENTER_URL
37
+
38
+ # Since Apple has some SSL errors, we have to configure the client properly:
39
+ # https://github.com/ariya/phantomjs/issues/11239
40
+ Capybara.register_driver :poltergeist do |a|
41
+ conf = ['--debug=no', '--ignore-ssl-errors=yes', '--ssl-protocol=TLSv1']
42
+ Capybara::Poltergeist::Driver.new(a, {
43
+ phantomjs_options: conf,
44
+ phantomjs_logger: File.open("#{TMP_FOLDER}/poltergeist_log.txt", "a"),
45
+ js_errors: false
46
+ })
47
+ end
48
+
49
+ self.login
50
+ end
51
+
52
+ # Loggs in a user with the given login data on the Dev Center Frontend.
53
+ # You don't need to pass a username and password. It will
54
+ # Automatically be fetched using the {Deliver::PasswordManager}.
55
+ # This method will also automatically be called when triggering other
56
+ # actions like {#open_app_page}
57
+ # @param user (String) (optional) The username/email address
58
+ # @param password (String) (optional) The password
59
+ # @return (bool) true if everything worked fine
60
+ # @raise [DeveloperCenterGeneralError] General error while executing
61
+ # this action
62
+ # @raise [DeveloperCenterLoginError] Login data is wrong
63
+ def login(user = nil, password = nil)
64
+ begin
65
+ Helper.log.info "Login into iOS Developer Center"
66
+
67
+ user ||= Deliver::PasswordManager.shared_manager.username
68
+ password ||= Deliver::PasswordManager.shared_manager.password
69
+
70
+ result = visit DEVELOPER_CENTER_URL
71
+ raise "Could not open Developer Center" unless result['status'] == 'success'
72
+
73
+ wait_for_elements(".button.blue").first.click
74
+
75
+ (wait_for_elements('#accountpassword') rescue nil) # when the user is already logged in, this will raise an exception
76
+
77
+ if page.has_content?"My Apps"
78
+ # Already logged in
79
+ return true
80
+ end
81
+
82
+ fill_in "accountname", with: user
83
+ fill_in "accountpassword", with: password
84
+
85
+ begin
86
+ all(".button.large.blue.signin-button").first.click
87
+
88
+ wait_for_elements('#aprerelease')
89
+ rescue Exception => ex
90
+ Helper.log.debug ex
91
+ raise DeveloperCenterLoginError.new("Error logging in user #{user} with the given password. Make sure you entered them correctly.")
92
+ end
93
+
94
+ Helper.log.info "Login successful"
95
+
96
+ true
97
+ rescue Exception => ex
98
+ error_occured(ex)
99
+ end
100
+ end
101
+
102
+ def app_status(app_identifier)
103
+ # TODO
104
+ end
105
+
106
+ # This method will enable push for the given app
107
+ # and download the cer file in any case, no matter if it existed before or not
108
+ # @return the path to the push file
109
+ def fetch_cer_file(app_identifier)
110
+ begin
111
+ open_app_page(app_identifier)
112
+
113
+ click_on "Edit"
114
+ wait_for_elements(".item-details") # just to finish loading
115
+
116
+ push_value = first(:css, '#pushEnabled').value
117
+ if push_value == "on"
118
+ Helper.log.info "Push for app '#{app_identifier}' is enabled"
119
+ else
120
+ Helper.log.warn "Push for app '#{app_identifier}' is disabled. This has to change."
121
+ first(:css, '#pushEnabled').click
122
+ end
123
+
124
+ sleep 1
125
+
126
+ # Example code
127
+ # <div class="appCertificates">
128
+ # <h3>Apple Push Notification service SSL Certificates</h3>
129
+ # <p>To configure push notifications for this iOS App ID, a Client SSL Certificate that allows your notification server to connect to the Apple Push Notification Service is required. Each iOS App ID requires its own Client SSL Certificate. Manage and generate your certificates below.</p>
130
+ # <div class="title" data-hires-status="replaced">Development SSL Certificate</div>
131
+ # <div class="createCertificate">
132
+ # <p>Create certificate to use for this App ID.</p>
133
+ # <a class="button small navLink development enabled" href="/account/ios/certificate/certificateRequest.action?appIdId=...&amp;types=..."><span>Create Certificate...</span></a>
134
+ # </div>
135
+ # <div class="title" data-hires-status="replaced">Production SSL Certificate</div>
136
+ # <div class="certificate">
137
+ # <dl>
138
+ # <dt>Name:</dt>
139
+ # <dd>Apple Production iOS Push Services: net.sunapps.151</dd>
140
+ # <dt>Type:</dt>
141
+ # <dd>APNs Production iOS</dd>
142
+ # <dt>Expires:</dt>
143
+ # <dd>Nov 14, 2015</dd>
144
+ # </dl>
145
+ # <a class="button small revoke-button" href="https://developer.apple.com/services-account/QH65B2/account/ios/certificate/revokeCertificate.action?content-type=application/x-www-form-urlencoded&amp;accept=application/json&amp;requestId=....;userLocale=en_US&amp;teamId=...&amp;certificateId=...&amp;type=...."><span>Revoke</span></a>
146
+ # <a class="button small download-button" href="/account/ios/certificate/certificateContentDownload.action?displayId=....&amp;type=..."><span>Download</span></a>
147
+ # </div>
148
+ # <div class="createCertificate">
149
+ # <p>Create an additional certificate to use for this App ID.</p>
150
+ # <a class="button small navLink distribution enabled" href="/account/ios/certificate/certificateRequest.action?appIdId=...&amp;types=..."><span>Create Certificate...</span></a>
151
+ # </div>
152
+ # </div>
153
+
154
+ def find_download_button
155
+ wait_for_elements(".formContent")
156
+
157
+ certificates_block = first('.appCertificates')
158
+ download_button = nil
159
+
160
+ production_section = false
161
+ certificates_block.all(:xpath, "./div").each do |div|
162
+ if production_section
163
+ # We're now in the second part, we only care about production certificates
164
+ if (download_button = div.first(".download-button"))
165
+ return download_button
166
+ end
167
+ end
168
+
169
+ production_section = true if div.text == PRODUCTION_SSL_CERTIFICATE_TITLE
170
+ end
171
+ nil
172
+ end
173
+
174
+
175
+ download_button = find_download_button
176
+
177
+ if not download_button
178
+ Helper.log.warn "Push for app '#{app_identifier}' is enabled, but there is no production certificate yet."
179
+ create_push_for_app(app_identifier)
180
+
181
+ download_button = find_download_button
182
+ raise "Could not find download button for Production SSL Certificate. Check out: '#{current_url}'" unless download_button
183
+ end
184
+
185
+
186
+ Helper.log.info "Going to download the latest profile"
187
+
188
+ # It is enabled, now just download it
189
+ # Taken from http://stackoverflow.com/a/17111206/445598
190
+ sleep 2
191
+
192
+ host = Capybara.current_session.current_host
193
+ url = download_button['href']
194
+ url = [host, url].join('')
195
+
196
+ myacinfo = page.driver.cookies['myacinfo'].value # some magic Apple, which is required for the profile download
197
+ data = open(url, {'Cookie' => "myacinfo=#{myacinfo}"}).read
198
+
199
+ raise "Something went wrong when downloading the certificate" unless data
200
+
201
+ path = "#{TMP_FOLDER}/aps_production_#{app_identifier}.cer"
202
+ File.write(path, data)
203
+
204
+ Helper.log.info "Successfully downloaded latest .cer file."
205
+ return path
206
+
207
+ rescue Exception => ex
208
+ error_occured(ex)
209
+ end
210
+ end
211
+
212
+
213
+ private
214
+ def open_app_page(app_identifier)
215
+ begin
216
+ visit APP_IDS_URL
217
+
218
+ apps = all(:xpath, "//td[@title='#{app_identifier}']")
219
+ if apps.count == 1
220
+ apps.first.click
221
+ sleep 1
222
+
223
+ return true
224
+ else
225
+ raise DeveloperCenterGeneralError.new("Could not find app with identifier '#{app_identifier}' on apps page.")
226
+ end
227
+ rescue Exception => ex
228
+ error_occured(ex)
229
+ end
230
+ end
231
+
232
+ def create_push_for_app(app_identifier)
233
+ wait_for_elements('.button.small.navLink.distribution.enabled').last.click # Create Certificate button
234
+
235
+ sleep 2
236
+
237
+ click_next # "Continue"
238
+
239
+ sleep 1
240
+ wait_for_elements(".button.small.center.back") # just to wait
241
+
242
+ # Upload CSR file
243
+ first(:xpath, "//input[@type='file']").set signing_request
244
+
245
+ click_next # "Generate"
246
+
247
+ while all(:css, '.loadingMessage').count > 0
248
+ Helper.log.debug "Waiting for iTC to generate the profile"
249
+ sleep 2
250
+ end
251
+
252
+ open_app_page(app_identifier)
253
+ click_on "Edit"
254
+ end
255
+
256
+ def signing_request
257
+ return ENV["PEM_CERT_SIGNING_REQUEST"] if (ENV["PEM_CERT_SIGNING_REQUEST"] and File.exists?(ENV["PEM_CERT_SIGNING_REQUEST"]))
258
+
259
+ files = Dir["./*.certSigningRequest"]
260
+ if files.count == 1
261
+ Helper.log.debug "Found a .certSigningRequest at the current folder. Using that."
262
+ return files.first
263
+ end
264
+
265
+ path = nil
266
+ while not path or not File.exists?path
267
+ puts "There was no profile found. To create a new one, we need a .certSigningRequest file".yellow
268
+ puts "To not be asked for a .certSigningRequest, PEM will automatically look for one in the current folder".yellow
269
+ path = ask("Path to your .certSigningRequest file (including extension): ")
270
+ puts "Could not find certSigningRequest file at path '#{path}'".red unless File.exists?path
271
+ end
272
+
273
+ return path
274
+ end
275
+
276
+
277
+ private
278
+ def click_next
279
+ wait_for_elements('.button.small.blue.right.submit').last.click
280
+ end
281
+
282
+ def error_occured(ex)
283
+ snap
284
+ raise ex # re-raise the error after saving the snapshot
285
+ end
286
+
287
+ def snap
288
+ path = "Error#{Time.now.to_i}.png"
289
+ save_screenshot(path, :full => true)
290
+ system("open '#{path}'")
291
+ end
292
+
293
+ def wait_for_elements(name)
294
+ counter = 0
295
+ results = all(name)
296
+ while results.count == 0
297
+ # Helper.log.debug "Waiting for #{name}"
298
+ sleep 0.2
299
+
300
+ results = all(name)
301
+
302
+ counter += 1
303
+ if counter > 100
304
+ Helper.log.debug page.html
305
+ Helper.log.debug caller
306
+ raise DeveloperCenterGeneralError.new("Couldn't find element '#{name}' after waiting for quite some time")
307
+ end
308
+ end
309
+ return results
310
+ end
311
+ end
312
+ end
data/lib/pem/helper.rb ADDED
@@ -0,0 +1,49 @@
1
+ require 'logger'
2
+
3
+ module PEM
4
+ module Helper
5
+
6
+ # Logging happens using this method
7
+ def self.log
8
+ if is_test?
9
+ @@log ||= Logger.new(STDOUT) #(nil) # don't show any logs when running tests
10
+ else
11
+ @@log ||= Logger.new(STDOUT)
12
+ end
13
+
14
+ @@log.formatter = proc do |severity, datetime, progname, msg|
15
+ string = "#{severity} [#{datetime.strftime('%Y-%m-%d %H:%M:%S.%2N')}]: "
16
+ second = "#{msg}\n"
17
+
18
+ if severity == "DEBUG"
19
+ string = string.magenta
20
+ elsif severity == "INFO"
21
+ string = string.white
22
+ elsif severity == "WARN"
23
+ string = string.yellow
24
+ elsif severity == "ERROR"
25
+ string = string.red
26
+ elsif severity == "FATAL"
27
+ string = string.red.bold
28
+ end
29
+
30
+
31
+ [string, second].join("")
32
+ end
33
+
34
+ @@log
35
+ end
36
+
37
+ # @return true if the currently running program is a unit test
38
+ def self.is_test?
39
+ defined?SpecHelper
40
+ end
41
+
42
+ # @return the full path to the Xcode developer tools of the currently
43
+ # running system
44
+ def self.xcode_path
45
+ return "" if self.is_test? and not OS.mac?
46
+ `xcode-select -p`.gsub("\n", '') + "/"
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,44 @@
1
+ require 'open-uri'
2
+
3
+ module PEM
4
+ # Verifies, the user runs the latest version of this gem
5
+ class UpdateChecker
6
+ # This method will check if the latest version is installed and show a warning if that's not the case
7
+ def self.verify_latest_version
8
+ if self.update_available?
9
+ v = fetch_latest
10
+ puts '#######################################################################'.green
11
+ puts "# PEM #{v} is available.".green
12
+ puts "# It is recommended to use the latest version.".green
13
+ puts "# Update using '(sudo) gem update PEM'.".green
14
+ puts "# To see what's new, open https://github.com/KrauseFx/PEM/releases.".green
15
+ puts '#######################################################################'.green
16
+ return true
17
+ end
18
+ false
19
+ end
20
+
21
+ # Is a new official release available (this does not include pre-releases)
22
+ def self.update_available?
23
+ begin
24
+ latest = fetch_latest
25
+ if latest and Gem::Version.new(latest) > Gem::Version.new(current_version)
26
+ return true
27
+ end
28
+ rescue Exception => ex
29
+ Helper.log.error("Could not check if 'PEM' is up to date.")
30
+ end
31
+ return false
32
+ end
33
+
34
+ # The currently used version of this gem
35
+ def self.current_version
36
+ PEM::VERSION
37
+ end
38
+
39
+ private
40
+ def self.fetch_latest
41
+ JSON.parse(open("http://rubygems.org/api/v1/gems/pem.json").read)["version"]
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ module PEM
2
+ VERSION = "0.1.0"
3
+ end
metadata CHANGED
@@ -1,274 +1,243 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pem
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felix Krause
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-13 00:00:00.000000000 Z
11
+ date: 2014-11-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - '>='
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: security
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ~>
32
32
  - !ruby/object:Gem::Version
33
33
  version: 0.1.3
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ~>
39
39
  - !ruby/object:Gem::Version
40
40
  version: 0.1.3
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: highline
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ~>
46
46
  - !ruby/object:Gem::Version
47
47
  version: 1.6.21
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ~>
53
53
  - !ruby/object:Gem::Version
54
54
  version: 1.6.21
55
- - !ruby/object:Gem::Dependency
56
- name: nokogiri
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: 1.6.3.1
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: 1.6.3.1
69
- - !ruby/object:Gem::Dependency
70
- name: rubyzip
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: 1.1.6
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: 1.1.6
83
55
  - !ruby/object:Gem::Dependency
84
56
  name: colored
85
57
  requirement: !ruby/object:Gem::Requirement
86
58
  requirements:
87
- - - ">="
59
+ - - '>='
88
60
  - !ruby/object:Gem::Version
89
61
  version: '0'
90
62
  type: :runtime
91
63
  prerelease: false
92
64
  version_requirements: !ruby/object:Gem::Requirement
93
65
  requirements:
94
- - - ">="
66
+ - - '>='
95
67
  - !ruby/object:Gem::Version
96
68
  version: '0'
97
69
  - !ruby/object:Gem::Dependency
98
70
  name: commander
99
71
  requirement: !ruby/object:Gem::Requirement
100
72
  requirements:
101
- - - "~>"
73
+ - - ~>
102
74
  - !ruby/object:Gem::Version
103
75
  version: 4.2.0
104
76
  type: :runtime
105
77
  prerelease: false
106
78
  version_requirements: !ruby/object:Gem::Requirement
107
79
  requirements:
108
- - - "~>"
80
+ - - ~>
109
81
  - !ruby/object:Gem::Version
110
82
  version: 4.2.0
111
- - !ruby/object:Gem::Dependency
112
- name: prawn
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :runtime
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- version: '0'
125
83
  - !ruby/object:Gem::Dependency
126
84
  name: deliver
127
85
  requirement: !ruby/object:Gem::Requirement
128
86
  requirements:
129
- - - "~>"
87
+ - - ~>
130
88
  - !ruby/object:Gem::Version
131
89
  version: '0.3'
132
90
  type: :runtime
133
91
  prerelease: false
134
92
  version_requirements: !ruby/object:Gem::Requirement
135
93
  requirements:
136
- - - "~>"
94
+ - - ~>
137
95
  - !ruby/object:Gem::Version
138
96
  version: '0.3'
139
97
  - !ruby/object:Gem::Dependency
140
98
  name: capybara
141
99
  requirement: !ruby/object:Gem::Requirement
142
100
  requirements:
143
- - - "~>"
101
+ - - ~>
144
102
  - !ruby/object:Gem::Version
145
103
  version: 2.4.3
146
104
  type: :runtime
147
105
  prerelease: false
148
106
  version_requirements: !ruby/object:Gem::Requirement
149
107
  requirements:
150
- - - "~>"
108
+ - - ~>
151
109
  - !ruby/object:Gem::Version
152
110
  version: 2.4.3
153
111
  - !ruby/object:Gem::Dependency
154
112
  name: poltergeist
155
113
  requirement: !ruby/object:Gem::Requirement
156
114
  requirements:
157
- - - "~>"
115
+ - - ~>
158
116
  - !ruby/object:Gem::Version
159
117
  version: 1.5.1
160
118
  type: :runtime
161
119
  prerelease: false
162
120
  version_requirements: !ruby/object:Gem::Requirement
163
121
  requirements:
164
- - - "~>"
122
+ - - ~>
165
123
  - !ruby/object:Gem::Version
166
124
  version: 1.5.1
167
125
  - !ruby/object:Gem::Dependency
168
126
  name: bundler
169
127
  requirement: !ruby/object:Gem::Requirement
170
128
  requirements:
171
- - - ">="
129
+ - - '>='
172
130
  - !ruby/object:Gem::Version
173
131
  version: '0'
174
132
  type: :development
175
133
  prerelease: false
176
134
  version_requirements: !ruby/object:Gem::Requirement
177
135
  requirements:
178
- - - ">="
136
+ - - '>='
179
137
  - !ruby/object:Gem::Version
180
138
  version: '0'
181
139
  - !ruby/object:Gem::Dependency
182
140
  name: rake
183
141
  requirement: !ruby/object:Gem::Requirement
184
142
  requirements:
185
- - - ">="
143
+ - - '>='
186
144
  - !ruby/object:Gem::Version
187
145
  version: '0'
188
146
  type: :development
189
147
  prerelease: false
190
148
  version_requirements: !ruby/object:Gem::Requirement
191
149
  requirements:
192
- - - ">="
150
+ - - '>='
193
151
  - !ruby/object:Gem::Version
194
152
  version: '0'
195
153
  - !ruby/object:Gem::Dependency
196
154
  name: rspec
197
155
  requirement: !ruby/object:Gem::Requirement
198
156
  requirements:
199
- - - "~>"
157
+ - - ~>
200
158
  - !ruby/object:Gem::Version
201
159
  version: 3.1.0
202
160
  type: :development
203
161
  prerelease: false
204
162
  version_requirements: !ruby/object:Gem::Requirement
205
163
  requirements:
206
- - - "~>"
164
+ - - ~>
207
165
  - !ruby/object:Gem::Version
208
166
  version: 3.1.0
209
167
  - !ruby/object:Gem::Dependency
210
168
  name: pry
211
169
  requirement: !ruby/object:Gem::Requirement
212
170
  requirements:
213
- - - ">="
171
+ - - '>='
214
172
  - !ruby/object:Gem::Version
215
173
  version: '0'
216
174
  type: :development
217
175
  prerelease: false
218
176
  version_requirements: !ruby/object:Gem::Requirement
219
177
  requirements:
220
- - - ">="
178
+ - - '>='
221
179
  - !ruby/object:Gem::Version
222
180
  version: '0'
223
181
  - !ruby/object:Gem::Dependency
224
182
  name: yard
225
183
  requirement: !ruby/object:Gem::Requirement
226
184
  requirements:
227
- - - "~>"
185
+ - - ~>
228
186
  - !ruby/object:Gem::Version
229
187
  version: 0.8.7.4
230
188
  type: :development
231
189
  prerelease: false
232
190
  version_requirements: !ruby/object:Gem::Requirement
233
191
  requirements:
234
- - - "~>"
192
+ - - ~>
235
193
  - !ruby/object:Gem::Version
236
194
  version: 0.8.7.4
237
195
  - !ruby/object:Gem::Dependency
238
196
  name: webmock
239
197
  requirement: !ruby/object:Gem::Requirement
240
198
  requirements:
241
- - - "~>"
199
+ - - ~>
242
200
  - !ruby/object:Gem::Version
243
201
  version: 1.19.0
244
202
  type: :development
245
203
  prerelease: false
246
204
  version_requirements: !ruby/object:Gem::Requirement
247
205
  requirements:
248
- - - "~>"
206
+ - - ~>
249
207
  - !ruby/object:Gem::Version
250
208
  version: 1.19.0
251
209
  - !ruby/object:Gem::Dependency
252
210
  name: codeclimate-test-reporter
253
211
  requirement: !ruby/object:Gem::Requirement
254
212
  requirements:
255
- - - ">="
213
+ - - '>='
256
214
  - !ruby/object:Gem::Version
257
215
  version: '0'
258
216
  type: :development
259
217
  prerelease: false
260
218
  version_requirements: !ruby/object:Gem::Requirement
261
219
  requirements:
262
- - - ">="
220
+ - - '>='
263
221
  - !ruby/object:Gem::Version
264
222
  version: '0'
265
223
  description: To be announced soon
266
224
  email:
267
225
  - krausefx@gmail.com
268
- executables: []
226
+ executables:
227
+ - pem
269
228
  extensions: []
270
229
  extra_rdoc_files: []
271
- files: []
230
+ files:
231
+ - LICENSE
232
+ - README.md
233
+ - bin/pem
234
+ - lib/pem.rb
235
+ - lib/pem/cert_manager.rb
236
+ - lib/pem/dependency_checker.rb
237
+ - lib/pem/developer_center.rb
238
+ - lib/pem/helper.rb
239
+ - lib/pem/update_checker.rb
240
+ - lib/pem/version.rb
272
241
  homepage: http://felixkrause.at
273
242
  licenses:
274
243
  - MIT
@@ -280,12 +249,12 @@ require_paths:
280
249
  - lib
281
250
  required_ruby_version: !ruby/object:Gem::Requirement
282
251
  requirements:
283
- - - ">="
252
+ - - '>='
284
253
  - !ruby/object:Gem::Version
285
254
  version: 2.0.0
286
255
  required_rubygems_version: !ruby/object:Gem::Requirement
287
256
  requirements:
288
- - - ">="
257
+ - - '>='
289
258
  - !ruby/object:Gem::Version
290
259
  version: '0'
291
260
  requirements: []