fastlane_core 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bba431883f062fc0b42eca6e525b4a299214401a
4
+ data.tar.gz: a01c9ca046143ca50a9c8574c2d85cfcfa41af1c
5
+ SHA512:
6
+ metadata.gz: 7ee079587326a09667ca0fdb355a63fa808063492ad8c0f58b7453eba5a57e6d719d9f1e685a2fef031d2a602ee12e948bac22a03b5c597dbd9fe46d7da506f8
7
+ data.tar.gz: ab0561256f15a2906e4de8652ab8b080565e6b93c068fa98c9ac63d34e15c9da2b43d4afda11a6e71afa26d444022121532cb106a6708d8eb4d1bfe921fabf62
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 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.
@@ -0,0 +1,53 @@
1
+ <h3 align="center">
2
+ <a href="https://github.com/KrauseFx/fastlane">
3
+ <img src="assets/fastlane.png" width="150" />
4
+ <br />
5
+ fastlane
6
+ </a>
7
+ </h3>
8
+ <p align="center">
9
+ <a href="https://github.com/KrauseFx/deliver">deliver</a> &bull;
10
+ <a href="https://github.com/KrauseFx/snapshot">snapshot</a> &bull;
11
+ <a href="https://github.com/KrauseFx/frameit">frameit</a> &bull;
12
+ <a href="https://github.com/KrauseFx/PEM">PEM</a> &bull;
13
+ <a href="https://github.com/KrauseFx/sigh">sigh</a> &bull;
14
+ <a href="https://github.com/KrauseFx/produce">produce</a>
15
+ </p>
16
+ -------
17
+
18
+ Fastlane Core
19
+ ============
20
+
21
+ [![Twitter: @KauseFx](https://img.shields.io/badge/contact-@KrauseFx-blue.svg?style=flat)](https://twitter.com/KrauseFx)
22
+ [![License](http://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/KrauseFx/fastlane_core/blob/master/LICENSE)
23
+ [![Gem](https://img.shields.io/gem/v/fastlane_core.svg?style=flat)](http://rubygems.org/gems/fastlane_core)
24
+ [![Build Status](https://img.shields.io/travis/KrauseFx/fastlane_core/master.svg?style=flat)](https://travis-ci.org/KrauseFx/fastlane_core)
25
+
26
+ All shared code of the fastlane tools is stored in this repository.
27
+
28
+ Get in contact with the developer on Twitter: [@KrauseFx](https://twitter.com/KrauseFx)
29
+
30
+ # Features
31
+
32
+ This gem contains all shared classes and code:
33
+
34
+ - Setting up a headless JavaScript browser using [poltergeist](https://github.com/teampoltergeist/poltergeist)
35
+ - Login and basic navigation on iTunes Connect
36
+ - Login and basic navigation on the Apple Developer Portal
37
+ - Checking for updates for a specific gem and showing an update message
38
+ - All output of all tools with different logging levels
39
+ - Finding of the current Xcode and iTunes Transporter path
40
+ - A list of available languages of iTunes Connect
41
+
42
+
43
+ # License
44
+ This project is licensed under the terms of the MIT license. See the LICENSE file.
45
+
46
+ # Contributing
47
+
48
+ 1. Create an issue to start a discussion about your idea
49
+ 2. Fork it (https://github.com/KrauseFx/fastlane_core/fork)
50
+ 3. Create your feature branch (`git checkout -b my-new-feature`)
51
+ 4. Commit your changes (`git commit -am 'Add some feature'`)
52
+ 5. Push to the branch (`git push origin my-new-feature`)
53
+ 6. Create a new Pull Request
@@ -0,0 +1,12 @@
1
+ require 'json'
2
+ require 'fastlane_core/version'
3
+ require 'fastlane_core/helper'
4
+ require 'fastlane_core/update_checker'
5
+ require 'fastlane_core/languages'
6
+
7
+ # Third Party code
8
+ require 'colored'
9
+
10
+ module FastlaneCore
11
+
12
+ end
@@ -0,0 +1,163 @@
1
+ require 'credentials_manager/password_manager'
2
+ require 'open-uri'
3
+
4
+ require 'capybara'
5
+ require 'capybara/poltergeist'
6
+ require 'phantomjs/poltergeist'
7
+
8
+ require 'fastlane_core/developer_center/developer_center_login'
9
+ require 'fastlane_core/developer_center/developer_center_helper'
10
+ require 'fastlane_core/developer_center/developer_center_signing_certificates'
11
+
12
+ module FastlaneCore
13
+ class DeveloperCenter
14
+ # This error occurs only if there is something wrong with the given login data
15
+ class DeveloperCenterLoginError < StandardError
16
+ end
17
+
18
+ # This error can occur for many reaons. It is
19
+ # usually raised when a UI element could not be found
20
+ class DeveloperCenterGeneralError < StandardError
21
+ end
22
+
23
+ include Capybara::DSL
24
+
25
+ DEVELOPER_CENTER_URL = "https://developer.apple.com/devcenter/ios/index.action"
26
+ PROFILES_URL = "https://developer.apple.com/account/ios/profile/profileList.action?type=production"
27
+ TMP_FOLDER = "/tmp/fastlane_core/"
28
+
29
+ def initialize
30
+ FileUtils.mkdir_p TMP_FOLDER
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: Phantomjs.path,
44
+ phantomjs_options: conf,
45
+ phantomjs_logger: File.open("#{TMP_FOLDER}/poltergeist_log.txt", "a"),
46
+ js_errors: false
47
+ })
48
+ end
49
+
50
+ page.driver.headers = { "Accept-Language" => "en" }
51
+
52
+ self.login
53
+ end
54
+
55
+ # Loggs in a user with the given login data on the Dev Center Frontend.
56
+ # You don't need to pass a username and password. It will
57
+ # Automatically be fetched using the {CredentialsManager::PasswordManager}.
58
+ # This method will also automatically be called when triggering other
59
+ # actions like {#open_app_page}
60
+ # @param user (String) (optional) The username/email address
61
+ # @param password (String) (optional) The password
62
+ # @return (bool) true if everything worked fine
63
+ # @raise [DeveloperCenterGeneralError] General error while executing
64
+ # this action
65
+ # @raise [DeveloperCenterLoginError] Login data is wrong
66
+ def login(user = nil, password = nil)
67
+ begin
68
+ Helper.log.info "Login into iOS Developer Center"
69
+
70
+ user ||= CredentialsManager::PasswordManager.shared_manager.username
71
+ password ||= CredentialsManager::PasswordManager.shared_manager.password
72
+
73
+ result = visit PROFILES_URL
74
+ raise "Could not open Developer Center" unless result['status'] == 'success'
75
+
76
+ # Already logged in
77
+ return true if page.has_content? "Member Center"
78
+
79
+ (wait_for_elements(".button.blue").first.click rescue nil) # maybe already logged in
80
+
81
+ (wait_for_elements('#accountpassword') rescue nil) # when the user is already logged in, this will raise an exception
82
+
83
+ # Already logged in
84
+ return true if page.has_content? "Member Center"
85
+
86
+ fill_in "accountname", with: user
87
+ fill_in "accountpassword", with: password
88
+
89
+ all(".button.large.blue.signin-button").first.click
90
+
91
+ begin
92
+ # If the user is not on multiple teams
93
+ select_team if page.has_content? "Select Team"
94
+ rescue => ex
95
+ Helper.log.debug ex
96
+ raise DeveloperCenterLoginError.new("Error loggin in user #{user}. User is on multiple teams and we were unable to correctly retrieve them.")
97
+ end
98
+
99
+ begin
100
+ wait_for_elements('.ios.profiles.gridList')
101
+ visit PROFILES_URL # again, since after the login, the dev center loses the production GET value
102
+ rescue => ex
103
+ Helper.log.debug ex
104
+ if page.has_content?"Getting Started"
105
+ raise "There was no valid signing certificate found. Please log in and follow the 'Getting Started guide' on '#{current_url}'".red
106
+ else
107
+ raise DeveloperCenterLoginError.new("Error logging in user #{user} with the given password. Make sure you entered them correctly.")
108
+ end
109
+ end
110
+
111
+ Helper.log.info "Login successful"
112
+
113
+ true
114
+ rescue => ex
115
+ error_occured(ex)
116
+ end
117
+ end
118
+
119
+
120
+ def select_team
121
+ team_id = ENV["FASTLANE_TEAM_ID"]
122
+ team_id = nil if team_id.to_s.length == 0
123
+
124
+ unless team_id
125
+ Helper.log.info "You can store you preferred team using the environment variable `FASTLANE_TEAM_ID`".green
126
+ Helper.log.info "Your ID belongs to the following teams:".green
127
+ end
128
+
129
+ available_options = []
130
+
131
+ teams = find("div.input").all('.team-value') # Grab all the teams data
132
+ teams.each_with_index do |val, index|
133
+ current_team_id = '"' + val.find("input").value + '"'
134
+ team_text = val.find(".label-primary").text
135
+ description_text = val.find(".label-secondary").text
136
+ description_text = "(#{description_text})" unless description_text.empty? # Include the team description if any
137
+ index_text = (index + 1).to_s + "."
138
+
139
+ available_options << [index_text, current_team_id, team_text, description_text].join(" ")
140
+ end
141
+
142
+ unless team_id
143
+ puts available_options.join("\n").green
144
+ team_index = ask("Please select the team number you would like to access: ".green)
145
+ team_id = teams[team_index.to_i - 1].find(".radio").value
146
+ end
147
+
148
+ team_button = first(:xpath, "//input[@type='radio' and @value='#{team_id}']") # Select the desired team
149
+ if team_button
150
+ team_button.click
151
+ else
152
+ Helper.log.fatal "Could not find given Team. Available options: ".red
153
+ puts available_options.join("\n").yellow
154
+ raise DeveloperCenterLoginError.new("Error finding given team #{team_id}.".red)
155
+ end
156
+
157
+ all(".button.large.blue.submit").first.click
158
+
159
+ result = visit PROFILES_URL
160
+ raise "Could not open Developer Center" unless result['status'] == 'success'
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,73 @@
1
+ module FastlaneCore
2
+ class DeveloperCenter
3
+ # Download a file from the dev center, by using a HTTP client. This will return the content of the file
4
+ def download_file(url)
5
+ Helper.log.info "Downloading profile..."
6
+ host = Capybara.current_session.current_host
7
+ url = [host, url].join('')
8
+
9
+ cookieString = ""
10
+
11
+ page.driver.cookies.each do |key, cookie|
12
+ cookieString << "#{cookie.name}=#{cookie.value};" # append all known cookies
13
+ end
14
+
15
+ data = open(url, {'Cookie' => cookieString}).read
16
+
17
+ raise "Something went wrong when downloading the file from the Dev Center" unless data
18
+ Helper.log.info "Successfully downloaded provisioning profile"
19
+ return data
20
+ end
21
+
22
+ def post_ajax(url)
23
+ JSON.parse(page.evaluate_script("$.ajax({type: 'POST', url: '#{url}', async: false})")['responseText'])
24
+ end
25
+
26
+ def click_next
27
+ wait_for_elements('.button.small.blue.right.submit').last.click
28
+ end
29
+
30
+ def error_occured(ex)
31
+ snap
32
+ raise ex # re-raise the error after saving the snapshot
33
+ end
34
+
35
+ def snap
36
+ path = "Error#{Time.now.to_i}.png"
37
+ save_screenshot(path, :full => true)
38
+ system("open '#{path}'") unless ENV['SIGH_DISABLE_OPEN_ERROR']
39
+ end
40
+
41
+ def wait_for(method, parameter, success)
42
+ counter = 0
43
+ result = method.call(parameter)
44
+ while !success.call(result)
45
+ sleep 0.2
46
+
47
+ result = method.call(parameter)
48
+
49
+ counter += 1
50
+ if counter > 100
51
+ Helper.log.debug caller
52
+ raise DeveloperCenterGeneralError.new("Couldn't find '#{parameter}' after waiting for quite some time")
53
+ end
54
+ end
55
+ return result
56
+ end
57
+
58
+ def wait_for_elements(name)
59
+ method = Proc.new { |n| all(name) }
60
+ success = Proc.new { |r| r.count > 0 }
61
+ return wait_for(method, name, success)
62
+ end
63
+
64
+ def wait_for_variable(name)
65
+ method = Proc.new { |n|
66
+ retval = page.html.match(/var #{n} = "(.*)"/)
67
+ retval[1] unless retval == nil
68
+ }
69
+ success = Proc.new { |r| r != nil }
70
+ return wait_for(method, name, success)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,5 @@
1
+ module FastlaneCore
2
+ class DeveloperCenter
3
+
4
+ end
5
+ end
@@ -0,0 +1,76 @@
1
+ module FastlaneCore
2
+ class DeveloperPortal
3
+ # Returns a array of hashes, that contains information about the iOS certificate
4
+ # @example
5
+ # [{"certRequestId"=>"B23Q2P396B",
6
+ # "name"=>"SunApps GmbH",
7
+ # "statusString"=>"Issued",
8
+ # "expirationDate"=>"2015-11-25T22:45:50Z",
9
+ # "expirationDateString"=>"Nov 25, 2015",
10
+ # "ownerType"=>"team",
11
+ # "ownerName"=>"SunApps GmbH",
12
+ # "ownerId"=>"....",
13
+ # "canDownload"=>true,
14
+ # "canRevoke"=>true,
15
+ # "certificateId"=>"....",
16
+ # "certificateStatusCode"=>0,
17
+ # "certRequestStatusCode"=>4,
18
+ # "certificateTypeDisplayId"=>"...",
19
+ # "serialNum"=>"....",
20
+ # "typeString"=>"iOS Distribution"},
21
+ # {another sertificate...}]
22
+ def code_signing_certificates(type, cert_date)
23
+ certs_url = "https://developer.apple.com/account/ios/certificate/certificateList.action?type="
24
+ certs_url << (type == DEVELOPMENT ? 'development' : 'distribution')
25
+ visit certs_url
26
+
27
+ certificateDataURL = wait_for_variable('certificateDataURL')
28
+ certificateRequestTypes = wait_for_variable('certificateRequestTypes')
29
+ certificateStatuses = wait_for_variable('certificateStatuses')
30
+
31
+ url = [certificateDataURL, certificateRequestTypes, certificateStatuses].join('')
32
+
33
+ # https://developer.apple.com/services-account/.../account/ios/certificate/listCertRequests.action?content-type=application/x-www-form-urlencoded&accept=application/json&requestId=...&userLocale=en_US&teamId=...&types=...&status=4&certificateStatus=0&type=distribution
34
+
35
+ certs = post_ajax(url)['certRequests']
36
+
37
+ ret_certs = []
38
+ certificate_name = ENV['SIGH_CERTIFICATE']
39
+
40
+ # The other profiles are push profiles
41
+ certificate_type = type == DEVELOPMENT ? 'iOS Development' : 'iOS Distribution'
42
+
43
+ # New profiles first
44
+ certs.sort! do |a, b|
45
+ Time.parse(b['expirationDate']) <=> Time.parse(a['expirationDate'])
46
+ end
47
+
48
+ certs.each do |current_cert|
49
+ next unless current_cert['typeString'] == certificate_type
50
+
51
+ if cert_date || certificate_name
52
+ if current_cert['expirationDateString'] == cert_date
53
+ Helper.log.info "Certificate ID '#{current_cert['certificateId']}' with expiry date '#{current_cert['expirationDateString']}' located"
54
+ ret_certs << current_cert
55
+ end
56
+ if current_cert['name'] == certificate_name
57
+ Helper.log.info "Certificate ID '#{current_cert['certificateId']}' with name '#{certificate_name}' located"
58
+ ret_certs << current_cert
59
+ end
60
+ else
61
+ ret_certs << current_cert
62
+ end
63
+ end
64
+
65
+ return ret_certs unless ret_certs.empty?
66
+
67
+ predicates = []
68
+ predicates << "name: #{certificate_name}" if certificate_name
69
+ predicates << "expiry date: #{cert_date}" if cert_date
70
+
71
+ predicates_str = " with #{predicates.join(' or ')}"
72
+
73
+ raise "Could not find a Certificate#{predicates_str}. Please open #{current_url} and make sure you have a signing profile created.".red
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,72 @@
1
+ require 'logger'
2
+
3
+ module FastlaneCore
4
+ module Helper
5
+
6
+ # Logging happens using this method
7
+ def self.log
8
+ if is_test?
9
+ @@log ||= Logger.new(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
+ [string, second].join("")
31
+ end
32
+
33
+ @@log
34
+ end
35
+
36
+ # @return true if the currently running program is a unit test
37
+ def self.test?
38
+ defined?SpecHelper
39
+ end
40
+
41
+ # Use Helper.test? instead
42
+ def self.is_test?
43
+ self.test?
44
+ end
45
+
46
+ # @return the full path to the Xcode developer tools of the currently
47
+ # running system
48
+ def self.xcode_path
49
+ return "" if self.is_test? and not OS.mac?
50
+ `xcode-select -p`.gsub("\n", '') + "/"
51
+ end
52
+
53
+ # @return the full path to the iTMSTransporter executable
54
+ def self.transporter_path
55
+ self.xcode_path + '../Applications/Application\ Loader.app/Contents/MacOS/itms/bin/iTMSTransporter'
56
+ end
57
+
58
+ def self.fastlane_enabled?
59
+ # This is called from the root context on the first start
60
+ @@enabled ||= File.directory?"./fastlane"
61
+ end
62
+
63
+ # Path to the installed gem to load resources (e.g. resign.sh)
64
+ def self.gem_path(gem_name)
65
+ if not Helper.is_test? and Gem::Specification::find_all_by_name(gem_name).any?
66
+ return Gem::Specification.find_by_name(gem_name).gem_dir
67
+ else
68
+ return './'
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,63 @@
1
+ require 'capybara'
2
+ require 'capybara/poltergeist'
3
+ require 'credentials_manager/password_manager'
4
+ require 'phantomjs/poltergeist' # this will download and store phantomjs
5
+
6
+
7
+ require 'fastlane_core/itunes_connect/itunes_connect_helper.rb'
8
+ require 'fastlane_core/itunes_connect/itunes_connect_login.rb'
9
+
10
+ module FastlaneCore
11
+ # Everything that can't be achived using the {FastlaneCore::ItunesTransporter}
12
+ # will be scripted using the iTunesConnect frontend.
13
+ #
14
+ # Every method you call here, might take a time
15
+ class ItunesConnect
16
+ # This error occurs only if there is something wrong with the given login data
17
+ class ItunesConnectLoginError < StandardError
18
+ end
19
+
20
+ # This error can occur for many reaons. It is
21
+ # usually raised when an UI element could not be found
22
+ class ItunesConnectGeneralError < StandardError
23
+ end
24
+
25
+ include Capybara::DSL
26
+
27
+ ITUNESCONNECT_URL = "https://itunesconnect.apple.com/"
28
+ APP_DETAILS_URL = "https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/ra/ng/app/[[app_id]]"
29
+
30
+ BUTTON_STRING_NEW_VERSION = "New Version"
31
+ BUTTON_STRING_SUBMIT_FOR_REVIEW = "Submit for Review"
32
+
33
+ WAITING_FOR_REVIEW = "Waiting For Review"
34
+
35
+ def initialize
36
+ super
37
+
38
+ return if Helper.is_test?
39
+
40
+ Capybara.run_server = false
41
+ Capybara.default_driver = :poltergeist
42
+ Capybara.javascript_driver = :poltergeist
43
+ Capybara.current_driver = :poltergeist
44
+ Capybara.app_host = ITUNESCONNECT_URL
45
+
46
+ # Since Apple has some SSL errors, we have to configure the client properly:
47
+ # https://github.com/ariya/phantomjs/issues/11239
48
+ Capybara.register_driver :poltergeist do |a|
49
+ conf = ['--debug=no', '--ignore-ssl-errors=yes', '--ssl-protocol=TLSv1']
50
+ Capybara::Poltergeist::Driver.new(a, {
51
+ phantomjs: Phantomjs.path,
52
+ phantomjs_options: conf,
53
+ phantomjs_logger: File.open("/tmp/poltergeist_log.txt", "a"),
54
+ js_errors: false
55
+ })
56
+ end
57
+
58
+ page.driver.headers = { "Accept-Language" => "en" }
59
+
60
+ login
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,82 @@
1
+ module FastlaneCore
2
+ class ItunesConnect
3
+ # All the private helpers
4
+ private
5
+ # Opens the app details page of the given app.
6
+ # @param app (Deliver::App) the app that should be opened
7
+ # @return (bool) true if everything worked fine
8
+ # @raise [ItunesConnectGeneralError] General error while executing
9
+ # this action
10
+ # @raise [ItunesConnectLoginError] Login data is wrong
11
+ def open_app_page(app)
12
+ verify_app(app)
13
+
14
+ Helper.log.info "Opening detail page for app #{app}"
15
+
16
+ visit APP_DETAILS_URL.gsub("[[app_id]]", app.apple_id.to_s)
17
+
18
+ wait_for_elements('.page-subnav')
19
+ sleep 5
20
+
21
+ if current_url.include?"wa/defaultError" # app could not be found
22
+ raise "Could not open app details for app '#{app}'. Make sure you're using the correct Apple ID and the correct Apple developer account (#{CredentialsManager::PasswordManager.shared_manager.username}).".red
23
+ end
24
+
25
+ true
26
+ rescue => ex
27
+ error_occured(ex)
28
+ end
29
+
30
+
31
+ def verify_app(app)
32
+ raise ItunesConnectGeneralError.new("No valid Deliver::App given") unless app.kind_of?Deliver::App
33
+ raise ItunesConnectGeneralError.new("App is missing information (apple_id not given)") unless (app.apple_id || '').to_s.length > 5
34
+ end
35
+
36
+ def error_occured(ex)
37
+ snap
38
+ raise ex # re-raise the error after saving the snapshot
39
+ end
40
+
41
+ def snap
42
+ path = "Error#{Time.now.to_i}.png"
43
+ save_screenshot(path, :full => true)
44
+ system("open '#{path}'")
45
+ end
46
+
47
+ # Since Apple takes for ages, after the upload is properly processed, we have to wait here
48
+ def wait_for_preprocessing
49
+ started = Time.now
50
+
51
+ # Wait, while iTunesConnect is processing the uploaded file
52
+ while (page.has_content?"Uploaded")
53
+ # iTunesConnect is super slow... so we have to wait...
54
+ Helper.log.info("Sorry, we have to wait for iTunesConnect, since it's still processing the uploaded ipa file\n" +
55
+ "If this takes longer than 45 minutes, you have to re-upload the ipa file again.\n" +
56
+ "You can always open the browser page yourself: '#{current_url}'\n" +
57
+ "Passed time: ~#{((Time.now - started) / 60.0).to_i} minute(s)")
58
+ sleep 30
59
+ visit current_url
60
+ sleep 30
61
+ end
62
+ end
63
+
64
+ def wait_for_elements(name)
65
+ counter = 0
66
+ results = all(name)
67
+ while results.count == 0
68
+ # Helper.log.debug "Waiting for #{name}"
69
+ sleep 0.2
70
+
71
+ results = all(name)
72
+
73
+ counter += 1
74
+ if counter > 100
75
+ Helper.log.debug caller
76
+ raise ItunesConnectGeneralError.new("Couldn't find element '#{name}' after waiting for quite some time")
77
+ end
78
+ end
79
+ return results
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,61 @@
1
+ module FastlaneCore
2
+ # Login code
3
+ class ItunesConnect
4
+ # Loggs in a user with the given login data on the iTC Frontend.
5
+ # You don't need to pass a username and password. It will
6
+ # Automatically be fetched using the {CredentialsManager::PasswordManager}.
7
+ # This method will also automatically be called when triggering other
8
+ # actions like {#open_app_page}
9
+ # @param user (String) (optional) The username/email address
10
+ # @param password (String) (optional) The password
11
+ # @return (bool) true if everything worked fine
12
+ # @raise [ItunesConnectGeneralError] General error while executing
13
+ # this action
14
+ # @raise [ItunesConnectLoginError] Login data is wrong
15
+ def login(user = nil, password = nil)
16
+ Helper.log.info "Logging into iTunesConnect"
17
+
18
+ user ||= CredentialsManager::PasswordManager.shared_manager.username
19
+ password ||= CredentialsManager::PasswordManager.shared_manager.password
20
+
21
+ result = visit ITUNESCONNECT_URL
22
+ raise "Could not open iTunesConnect" unless result['status'] == 'success'
23
+
24
+ sleep 3
25
+
26
+ if page.has_content?"My Apps"
27
+ # Already logged in
28
+ return true
29
+ end
30
+
31
+ begin
32
+ wait_for_elements('#accountpassword')
33
+ rescue => ex
34
+ # when the user is already logged in, this will raise an exception
35
+ end
36
+
37
+ fill_in "accountname", with: user
38
+ fill_in "accountpassword", with: password
39
+
40
+ begin
41
+ (wait_for_elements(".enabled").first.click rescue nil) # Login Button
42
+ wait_for_elements('.homepageWrapper.ng-scope')
43
+
44
+ if page.has_content?"My Apps"
45
+ # Everything looks good
46
+ else
47
+ raise ItunesConnectLoginError.new("Looks like your login data was correct, but you do not have access to the apps.")
48
+ end
49
+ rescue => ex
50
+ Helper.log.debug(ex)
51
+ raise ItunesConnectLoginError.new("Error logging in user #{user} with the given password. Make sure you entered them correctly.")
52
+ end
53
+
54
+ Helper.log.info "Successfully logged into iTunesConnect"
55
+
56
+ true
57
+ rescue => ex
58
+ error_occured(ex)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,6 @@
1
+ module FastlaneCore
2
+ module Languages
3
+ # These are all the languages which are available to use to upload app metadata and screenshots
4
+ ALL_LANGUAGES = ["da-DK", "de-DE", "el-GR", "en-AU", "en-CA", "en-GB", "en-US", "es-ES", "es-MX", "fi-FI", "fr-CA", "fr-FR", "id-ID", "it-IT", "ja-JP", "ko-KR", "ms-MY", "nl-NL", "no-NO", "pt-BR", "pt-PT", "ru-RU", "sv-SE", "th-TH", "tr-TR", "vi-VI", "cmn-Hans", "zh_CN", "cmn-Hant"]
5
+ end
6
+ end
@@ -0,0 +1,38 @@
1
+ require 'open-uri'
2
+
3
+ module FastlaneCore
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(gem_name, current_version)
8
+ return true unless self.update_available?(gem_name, current_version)
9
+
10
+ v = fetch_latest(gem_name)
11
+ puts '#######################################################################'.green
12
+ puts "# #{gem_name} #{v} is available. You are on #{current_version}.".green
13
+ puts "# It is recommended to use the latest version.".green
14
+ puts "# Update using 'sudo gem update #{gem_name}'.".green
15
+ puts "# To see what's new, open https://github.com/KrauseFx/#{gem_name}/releases.".green
16
+ puts '#######################################################################'.green
17
+ false
18
+ end
19
+
20
+ # Is a new official release available (this does not include pre-releases)
21
+ def self.update_available?(gem_name, current_version)
22
+ begin
23
+ latest = fetch_latest(gem_name)
24
+ if latest and Gem::Version.new(latest) > Gem::Version.new(current_version)
25
+ return true
26
+ end
27
+ rescue => ex
28
+ Helper.log.error("Could not check if '#{gem_name}' is up to date.")
29
+ end
30
+ return false
31
+ end
32
+
33
+ private
34
+ def self.fetch_latest(gem_name)
35
+ JSON.parse(open("http://rubygems.org/api/v1/gems/#{gem_name}.json").read)["version"]
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module FastlaneCore
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,283 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastlane_core
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Felix Krause
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: multi_json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: highline
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 1.6.21
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.6.21
55
+ - !ruby/object:Gem::Dependency
56
+ name: colored
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: commander
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 4.3.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 4.3.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: credentials_manager
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: phantomjs
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: 1.9.8
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: 1.9.8
111
+ - !ruby/object:Gem::Dependency
112
+ name: capybara
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: 2.4.3
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ~>
123
+ - !ruby/object:Gem::Version
124
+ version: 2.4.3
125
+ - !ruby/object:Gem::Dependency
126
+ name: poltergeist
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ~>
130
+ - !ruby/object:Gem::Version
131
+ version: 1.5.1
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ~>
137
+ - !ruby/object:Gem::Version
138
+ version: 1.5.1
139
+ - !ruby/object:Gem::Dependency
140
+ name: bundler
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rake
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - '>='
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: rspec
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ~>
172
+ - !ruby/object:Gem::Version
173
+ version: 3.1.0
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ~>
179
+ - !ruby/object:Gem::Version
180
+ version: 3.1.0
181
+ - !ruby/object:Gem::Dependency
182
+ name: pry
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - '>='
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - '>='
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: yard
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ~>
200
+ - !ruby/object:Gem::Version
201
+ version: 0.8.7.4
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ~>
207
+ - !ruby/object:Gem::Version
208
+ version: 0.8.7.4
209
+ - !ruby/object:Gem::Dependency
210
+ name: webmock
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ~>
214
+ - !ruby/object:Gem::Version
215
+ version: 1.19.0
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ~>
221
+ - !ruby/object:Gem::Version
222
+ version: 1.19.0
223
+ - !ruby/object:Gem::Dependency
224
+ name: codeclimate-test-reporter
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - '>='
228
+ - !ruby/object:Gem::Version
229
+ version: '0'
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - '>='
235
+ - !ruby/object:Gem::Version
236
+ version: '0'
237
+ description: Contains all shared code/dependencies of the fastlane.tools
238
+ email:
239
+ - fastlanecore@krausefx.com
240
+ executables: []
241
+ extensions: []
242
+ extra_rdoc_files: []
243
+ files:
244
+ - LICENSE
245
+ - README.md
246
+ - lib/fastlane_core.rb
247
+ - lib/fastlane_core/developer_center/developer_center.rb
248
+ - lib/fastlane_core/developer_center/developer_center_helper.rb
249
+ - lib/fastlane_core/developer_center/developer_center_login.rb
250
+ - lib/fastlane_core/developer_center/developer_center_signing_certificates.rb
251
+ - lib/fastlane_core/helper.rb
252
+ - lib/fastlane_core/itunes_connect/itunes_connect.rb
253
+ - lib/fastlane_core/itunes_connect/itunes_connect_helper.rb
254
+ - lib/fastlane_core/itunes_connect/itunes_connect_login.rb
255
+ - lib/fastlane_core/languages.rb
256
+ - lib/fastlane_core/update_checker.rb
257
+ - lib/fastlane_core/version.rb
258
+ homepage: http://fastlane.tools
259
+ licenses:
260
+ - MIT
261
+ metadata: {}
262
+ post_install_message:
263
+ rdoc_options: []
264
+ require_paths:
265
+ - lib
266
+ required_ruby_version: !ruby/object:Gem::Requirement
267
+ requirements:
268
+ - - '>='
269
+ - !ruby/object:Gem::Version
270
+ version: 2.0.0
271
+ required_rubygems_version: !ruby/object:Gem::Requirement
272
+ requirements:
273
+ - - '>='
274
+ - !ruby/object:Gem::Version
275
+ version: '0'
276
+ requirements: []
277
+ rubyforge_project:
278
+ rubygems_version: 2.2.2
279
+ signing_key:
280
+ specification_version: 4
281
+ summary: Contains all shared code/dependencies of the fastlane.tools
282
+ test_files: []
283
+ has_rdoc: