fastlane_core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: