fastlane 1.98.0 → 1.99.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/.DS_Store +0 -0
- data/lib/fastlane/.DS_Store +0 -0
- data/lib/fastlane/actions/add_git_tag.rb +5 -0
- data/lib/fastlane/actions/carthage.rb +12 -2
- data/lib/fastlane/actions/crashlytics.rb +1 -0
- data/lib/fastlane/actions/hockey.rb +8 -1
- data/lib/fastlane/actions/update_app_identifier.rb +1 -1
- data/lib/fastlane/actions/update_project_code_signing.rb +1 -1
- data/lib/fastlane/actions/update_project_provisioning.rb +1 -1
- data/lib/fastlane/actions/update_project_team.rb +1 -1
- data/lib/fastlane/commands_generator.rb +6 -2
- data/lib/fastlane/helper/crashlytics_helper.rb +5 -1
- data/lib/fastlane/plugins/.DS_Store +0 -0
- data/lib/fastlane/plugins/templates/.DS_Store +0 -0
- data/lib/fastlane/setup/crashlytics_beta.rb +68 -76
- data/lib/fastlane/setup/crashlytics_beta_command_line_handler.rb +24 -0
- data/lib/fastlane/setup/crashlytics_beta_info.rb +48 -0
- data/lib/fastlane/setup/crashlytics_beta_info_collector.rb +164 -0
- data/lib/fastlane/setup/crashlytics_beta_ui.rb +39 -0
- data/lib/fastlane/setup/crashlytics_beta_user_email_fetcher.rb +13 -0
- data/lib/fastlane/setup/crashlytics_project_parser.rb +49 -0
- data/lib/fastlane/setup/setup.rb +7 -0
- data/lib/fastlane/version.rb +1 -1
- metadata +12 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 28b8a9977502edeea107da475bedffceb4ff575e
|
|
4
|
+
data.tar.gz: 45ec673ada611ee9ebe2348fd90eae048c5a26cb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d2250b41a5ab0b0e806550352e723eaf1e84d233fdd1c705a9edd5dcb578c82bab9d3400bae707289e2559445ae05a308ac90523a266b46de32c2f8b485f8426
|
|
7
|
+
data.tar.gz: 59c837b323a5fa9d8978a689ac584ce236995e2e2833e64d97d845220bea3c4ece1522fbfcdb9e14dea90f70e9f16d6dc2694a5d512e461a2e39ed55f0cf8caa
|
data/lib/.DS_Store
CHANGED
|
Binary file
|
data/lib/fastlane/.DS_Store
CHANGED
|
Binary file
|
|
@@ -13,6 +13,7 @@ module Fastlane
|
|
|
13
13
|
cmd << ["-am #{message.shellescape}"]
|
|
14
14
|
cmd << '--force' if options[:force]
|
|
15
15
|
cmd << "'#{tag}'"
|
|
16
|
+
cmd << options[:commit].to_s if options[:commit]
|
|
16
17
|
|
|
17
18
|
UI.message "Adding git tag '#{tag}' 🎯."
|
|
18
19
|
Actions.sh(cmd.join(' '))
|
|
@@ -45,6 +46,10 @@ module Fastlane
|
|
|
45
46
|
env_name: "FL_GIT_TAG_MESSAGE",
|
|
46
47
|
description: "The tag message. Defaults to the tag's name",
|
|
47
48
|
optional: true),
|
|
49
|
+
FastlaneCore::ConfigItem.new(key: :commit,
|
|
50
|
+
env_name: "FL_GIT_TAG_COMMIT",
|
|
51
|
+
description: "The commit or object where the tag will be set. Defaults to the current HEAD",
|
|
52
|
+
optional: true),
|
|
48
53
|
FastlaneCore::ConfigItem.new(key: :force,
|
|
49
54
|
env_name: "FL_GIT_TAG_FORCE",
|
|
50
55
|
description: "Force adding the tag",
|
|
@@ -5,6 +5,11 @@ module Fastlane
|
|
|
5
5
|
cmd = ["carthage"]
|
|
6
6
|
|
|
7
7
|
cmd << params[:command]
|
|
8
|
+
|
|
9
|
+
if params[:command] == "update" && params[:dependencies].count > 0
|
|
10
|
+
cmd.concat params[:dependencies]
|
|
11
|
+
end
|
|
12
|
+
|
|
8
13
|
cmd << "--use-ssh" if params[:use_ssh]
|
|
9
14
|
cmd << "--use-submodules" if params[:use_submodules]
|
|
10
15
|
cmd << "--no-use-binaries" if params[:use_binaries] == false
|
|
@@ -39,6 +44,11 @@ module Fastlane
|
|
|
39
44
|
verify_block: proc do |value|
|
|
40
45
|
UI.user_error!("Please pass a valid command. Use one of the following: #{available_commands.join(', ')}") unless available_commands.include? value
|
|
41
46
|
end),
|
|
47
|
+
FastlaneCore::ConfigItem.new(key: :dependencies,
|
|
48
|
+
description: "Carthage dependencies to update",
|
|
49
|
+
default_value: [],
|
|
50
|
+
is_string: false,
|
|
51
|
+
type: Array),
|
|
42
52
|
FastlaneCore::ConfigItem.new(key: :use_ssh,
|
|
43
53
|
env_name: "FL_CARTHAGE_USE_SSH",
|
|
44
54
|
description: "Use SSH for downloading GitHub repositories",
|
|
@@ -97,7 +107,7 @@ module Fastlane
|
|
|
97
107
|
optional: true,
|
|
98
108
|
verify_block: proc do |value|
|
|
99
109
|
value.split(',').each do |platform|
|
|
100
|
-
UI.user_error!("Please pass a valid platform. Use one of the following: #{available_platforms.join(', ')}") unless available_platforms.include?
|
|
110
|
+
UI.user_error!("Please pass a valid platform. Use one of the following: #{available_platforms.join(', ')}") unless available_platforms.map(&:downcase).include?(platform.downcase)
|
|
101
111
|
end
|
|
102
112
|
end),
|
|
103
113
|
FastlaneCore::ConfigItem.new(key: :configuration,
|
|
@@ -117,7 +127,7 @@ module Fastlane
|
|
|
117
127
|
end
|
|
118
128
|
|
|
119
129
|
def self.authors
|
|
120
|
-
["bassrock", "petester42", "jschmid", "JaviSoto", "uny", "phatblat"]
|
|
130
|
+
["bassrock", "petester42", "jschmid", "JaviSoto", "uny", "phatblat", "bfcrampton"]
|
|
121
131
|
end
|
|
122
132
|
end
|
|
123
133
|
end
|
|
@@ -178,7 +178,14 @@ module Fastlane
|
|
|
178
178
|
FastlaneCore::ConfigItem.new(key: :owner_id,
|
|
179
179
|
env_name: "FL_HOCKEY_OWNER_ID",
|
|
180
180
|
description: "ID for the owner of the app",
|
|
181
|
-
optional: true)
|
|
181
|
+
optional: true),
|
|
182
|
+
FastlaneCore::ConfigItem.new(key: :strategy,
|
|
183
|
+
env_name: "FL_HOCKEY_STRATEGY",
|
|
184
|
+
description: "Strategy: 'add' = to add the build as a new build even if it has the same build number (default); 'replace' = to replace a build with the same build number",
|
|
185
|
+
default_value: "add",
|
|
186
|
+
verify_block: proc do |value|
|
|
187
|
+
UI.user_error!("Invalid value '#{value}' for key 'strategy'. Allowed values are 'add', 'replace'.") unless ['add', 'replace'].include?(value)
|
|
188
|
+
end)
|
|
182
189
|
]
|
|
183
190
|
end
|
|
184
191
|
|
|
@@ -78,7 +78,7 @@ module Fastlane
|
|
|
78
78
|
FastlaneCore::ConfigItem.new(key: :app_identifier,
|
|
79
79
|
env_name: 'FL_UPDATE_APP_IDENTIFIER',
|
|
80
80
|
description: 'The app Identifier you want to set',
|
|
81
|
-
default_value: ENV['PRODUCE_APP_IDENTIFIER'])
|
|
81
|
+
default_value: ENV['PRODUCE_APP_IDENTIFIER'] || CredentialsManager::AppfileConfig.try_fetch_value(:app_identifier))
|
|
82
82
|
]
|
|
83
83
|
end
|
|
84
84
|
|
|
@@ -7,7 +7,7 @@ module Fastlane
|
|
|
7
7
|
def self.run(params)
|
|
8
8
|
UI.message("You shouldn't use update_project_code_signing")
|
|
9
9
|
UI.message("Have you considered using the recommended way to do code signing?")
|
|
10
|
-
UI.message("https://github.com/fastlane/fastlane/
|
|
10
|
+
UI.message("https://github.com/fastlane/fastlane/tree/master/fastlane/docs/Codesigning")
|
|
11
11
|
|
|
12
12
|
path = params[:path]
|
|
13
13
|
path = File.join(path, "project.pbxproj")
|
|
@@ -9,7 +9,7 @@ module Fastlane
|
|
|
9
9
|
ROOT_CERTIFICATE_URL = "http://www.apple.com/appleca/AppleIncRootCertificate.cer"
|
|
10
10
|
def self.run(params)
|
|
11
11
|
UI.message("You’re updating provisioning profiles directly in your project, but have you considered easier ways to do code signing?")
|
|
12
|
-
UI.message("https://github.com/fastlane/fastlane/
|
|
12
|
+
UI.message("https://github.com/fastlane/fastlane/tree/master/fastlane/docs/Codesigning")
|
|
13
13
|
|
|
14
14
|
# assign folder from parameter or search for xcodeproj file
|
|
15
15
|
folder = params[:xcodeproj] || Dir["*.xcodeproj"].first
|
|
@@ -36,7 +36,7 @@ module Fastlane
|
|
|
36
36
|
FastlaneCore::ConfigItem.new(key: :teamid,
|
|
37
37
|
env_name: "FL_PROJECT_TEAM_ID",
|
|
38
38
|
description: "The Team ID you want to use",
|
|
39
|
-
default_value: ENV["TEAM_ID"])
|
|
39
|
+
default_value: ENV["TEAM_ID"] || CredentialsManager::AppfileConfig.try_fetch_value(:team_id))
|
|
40
40
|
]
|
|
41
41
|
end
|
|
42
42
|
|
|
@@ -57,10 +57,14 @@ module Fastlane
|
|
|
57
57
|
c.syntax = 'fastlane init'
|
|
58
58
|
c.description = 'Helps you with your initial fastlane setup'
|
|
59
59
|
|
|
60
|
+
if FastlaneCore::Feature.enabled?('FASTLANE_ENABLE_CRASHLYTICS_BETA_INITIALIZATION')
|
|
61
|
+
CrashlyticsBetaCommandLineHandler.apply_options(c)
|
|
62
|
+
end
|
|
63
|
+
|
|
60
64
|
c.action do |args, options|
|
|
61
65
|
if args[0] == 'beta' && FastlaneCore::Feature.enabled?('FASTLANE_ENABLE_CRASHLYTICS_BETA_INITIALIZATION')
|
|
62
|
-
|
|
63
|
-
Fastlane::CrashlyticsBeta.new.run
|
|
66
|
+
beta_info = CrashlyticsBetaCommandLineHandler.info_from_options(options)
|
|
67
|
+
Fastlane::CrashlyticsBeta.new(beta_info, Fastlane::CrashlyticsBetaUi.new).run
|
|
64
68
|
else
|
|
65
69
|
Fastlane::Setup.new.run
|
|
66
70
|
end
|
|
@@ -2,9 +2,13 @@ module Fastlane
|
|
|
2
2
|
module Helper
|
|
3
3
|
class CrashlyticsHelper
|
|
4
4
|
class << self
|
|
5
|
+
def discover_default_crashlytics_path
|
|
6
|
+
Dir["./Pods/iOS/Crashlytics/Crashlytics.framework"].last || Dir["./**/Crashlytics.framework"].last
|
|
7
|
+
end
|
|
8
|
+
|
|
5
9
|
def generate_ios_command(params)
|
|
6
10
|
unless params[:crashlytics_path]
|
|
7
|
-
params[:crashlytics_path] =
|
|
11
|
+
params[:crashlytics_path] = discover_default_crashlytics_path
|
|
8
12
|
end
|
|
9
13
|
|
|
10
14
|
UI.user_error!("No value found for 'crashlytics_path'") unless params[:crashlytics_path]
|
|
Binary file
|
|
Binary file
|
|
@@ -1,102 +1,94 @@
|
|
|
1
1
|
module Fastlane
|
|
2
2
|
class CrashlyticsBeta
|
|
3
|
+
def initialize(beta_info, ui)
|
|
4
|
+
@beta_info = beta_info
|
|
5
|
+
@ui = ui
|
|
6
|
+
end
|
|
7
|
+
|
|
3
8
|
def run
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
setup = Setup.new
|
|
10
|
+
|
|
11
|
+
@ui.message 'This command will generate a fastlane configuration for distributing your app with Beta by Crashlytics'
|
|
12
|
+
@ui.message 'so that you can get your testers new builds with a single command!'
|
|
13
|
+
|
|
14
|
+
@ui.message ''
|
|
15
|
+
|
|
16
|
+
if setup.is_android?
|
|
17
|
+
UI.user_error!('Sorry, Beta by Crashlytics configuration is currently only available for iOS projects!')
|
|
18
|
+
elsif !setup.is_ios?
|
|
19
|
+
UI.user_error!('Please run Beta by Crashlytics configuration from your iOS project folder.')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
@ui.message "\nAttempting to detect your project settings in this directory...".cyan
|
|
23
|
+
info_collector = CrashlyticsBetaInfoCollector.new(CrashlyticsProjectParser.new,
|
|
24
|
+
CrashlyticsBetaUserEmailFetcher.new,
|
|
25
|
+
@ui)
|
|
26
|
+
info_collector.collect_info_into(@beta_info)
|
|
9
27
|
|
|
10
28
|
if FastlaneFolder.setup?
|
|
11
|
-
|
|
12
|
-
|
|
29
|
+
@ui.message ""
|
|
30
|
+
@ui.header('Copy and paste the following lane into your Fastfile to use Crashlytics Beta!')
|
|
31
|
+
@ui.message ""
|
|
32
|
+
puts lane_template.cyan
|
|
33
|
+
@ui.message ""
|
|
13
34
|
else
|
|
14
|
-
fastfile = fastfile_template
|
|
35
|
+
fastfile = fastfile_template
|
|
15
36
|
FileUtils.mkdir_p('fastlane')
|
|
16
37
|
File.write('fastlane/Fastfile', fastfile)
|
|
17
|
-
|
|
38
|
+
@ui.success('A Fastfile has been generated for you at ./fastlane/Fastfile 🚀')
|
|
18
39
|
end
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
UI.success('Learn more here: https://github.com/fastlane/setups/blob/master/samples-ios/distribute-beta-build.md 🚀')
|
|
40
|
+
@ui.header('Next Steps')
|
|
41
|
+
@ui.success('Run the following command to build and upload to Beta by Crashlytics. 🎯')
|
|
42
|
+
@ui.message("\n fastlane beta")
|
|
43
|
+
@ui.message ""
|
|
24
44
|
end
|
|
25
45
|
|
|
26
|
-
def
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
xcode_project = Xcodeproj::Project.open(path)
|
|
32
|
-
target = xcode_project.targets.find { |t| t.name == target_name }
|
|
33
|
-
UI.crash!("Unable to locate a target by the name of #{target_name}") if target.nil?
|
|
34
|
-
scripts = target.build_phases.select { |t| t.class == Xcodeproj::Project::Object::PBXShellScriptBuildPhase }
|
|
35
|
-
crash_script = scripts.find { |s| includes_run_script?(s.shell_script) }
|
|
36
|
-
UI.user_error!("Unable to find Crashlytics Run Script Build Phase") if crash_script.nil?
|
|
37
|
-
script_array = crash_script.shell_script.split('\n').find { |l| includes_run_script?(l) }.split(' ')
|
|
38
|
-
if script_array.count == 3 && api_key_valid?(script_array[1]) && build_secret_valid?(script_array[2])
|
|
39
|
-
{
|
|
40
|
-
api_key: script_array[1],
|
|
41
|
-
build_secret: script_array[2]
|
|
42
|
-
}
|
|
43
|
-
else
|
|
44
|
-
UI.important('fastlane was unable to detect your Fabric API Key and Build Secret. 🔑')
|
|
45
|
-
UI.important('Navigate to https://www.fabric.io/settings/organizations, select the appropriate organization,')
|
|
46
|
-
UI.important('and copy the API Key and Build Secret.')
|
|
47
|
-
keys = {}
|
|
48
|
-
loop do
|
|
49
|
-
keys[:api_key] = UI.input('API Key:')
|
|
50
|
-
break if api_key_valid?(keys[:api_key])
|
|
51
|
-
UI.important "Invalid API Key, Please Try Again!"
|
|
52
|
-
end
|
|
53
|
-
loop do
|
|
54
|
-
keys[:build_secret] = UI.input('Build Secret:')
|
|
55
|
-
break if build_secret_valid?(keys[:build_secret])
|
|
56
|
-
UI.important "Invalid Build Secret, Please Try Again!"
|
|
57
|
-
end
|
|
58
|
-
keys
|
|
46
|
+
def lane_template
|
|
47
|
+
discovered_crashlytics_path = Fastlane::Helper::CrashlyticsHelper.discover_default_crashlytics_path
|
|
48
|
+
|
|
49
|
+
unless expanded_paths_equal?(@beta_info.crashlytics_path, discovered_crashlytics_path)
|
|
50
|
+
crashlytics_path_arg = "\n crashlytics_path: '#{@beta_info.crashlytics_path}',"
|
|
59
51
|
end
|
|
60
|
-
end
|
|
61
52
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
# rubocop:disable Style/IndentationConsistency
|
|
54
|
+
%{ #
|
|
55
|
+
# Learn more here: https://github.com/fastlane/setups/blob/master/samples-ios/distribute-beta-build.md 🚀
|
|
56
|
+
#
|
|
57
|
+
lane :beta do
|
|
58
|
+
# set 'export_method' to 'ad-hoc' if your Crashlytics Beta distribution uses ad-hoc provisioning
|
|
59
|
+
gym(scheme: '#{@beta_info.schemes.first}', export_method: '#{@beta_info.export_method}')
|
|
65
60
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
61
|
+
crashlytics(api_token: '#{@beta_info.api_key}',
|
|
62
|
+
build_secret: '#{@beta_info.build_secret}',#{crashlytics_path_arg}
|
|
63
|
+
emails: ['#{@beta_info.emails.join("', '")}'], # You can list more emails here
|
|
64
|
+
# groups: ['group_alias_1', 'group_alias_2'], # You can define groups on the web and reference them here
|
|
65
|
+
notes: 'Distributed with fastlane', # Check out the changelog_from_git_commits action!
|
|
66
|
+
notifications: true) # Should this distribution notify your testers via email?
|
|
69
67
|
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
# You can notify your team in chat that a beta build has been uploaded
|
|
69
|
+
# slack(
|
|
70
|
+
# slack_url: "https://hooks.slack.com/services/YOUR/TEAM/INFO"
|
|
71
|
+
# channel: "beta-releases",
|
|
72
|
+
# message: "Successfully uploaded a beta release - see it at https://fabric.io/_/beta"
|
|
73
|
+
# )
|
|
74
|
+
end}
|
|
75
|
+
# rubocop:enable Style/IndentationConsistency
|
|
72
76
|
end
|
|
73
77
|
|
|
74
|
-
def
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
gym(scheme: '#{scheme}', export_method: 'development')
|
|
79
|
-
crashlytics(api_token: '#{api_key}',
|
|
80
|
-
build_secret: '#{build_secret}',
|
|
81
|
-
notifications: true
|
|
82
|
-
)
|
|
83
|
-
end
|
|
84
|
-
}
|
|
78
|
+
def expanded_paths_equal?(path1, path2)
|
|
79
|
+
return nil if path1.nil? || path2.nil?
|
|
80
|
+
|
|
81
|
+
File.expand_path(path1) == File.expand_path(path2)
|
|
85
82
|
end
|
|
86
83
|
|
|
87
|
-
def fastfile_template
|
|
84
|
+
def fastfile_template
|
|
88
85
|
<<-eos
|
|
89
86
|
fastlane_version "#{Fastlane::VERSION}"
|
|
87
|
+
|
|
90
88
|
default_platform :ios
|
|
89
|
+
|
|
91
90
|
platform :ios do
|
|
92
|
-
|
|
93
|
-
# set 'export_method' to 'ad-hoc' if your Crashlytics Beta distribution uses ad-hoc provisioning
|
|
94
|
-
gym(scheme: '#{scheme}', export_method: 'development')
|
|
95
|
-
crashlytics(api_token: '#{api_key}',
|
|
96
|
-
build_secret: '#{build_secret}',
|
|
97
|
-
notifications: true
|
|
98
|
-
)
|
|
99
|
-
end
|
|
91
|
+
#{lane_template}
|
|
100
92
|
end
|
|
101
93
|
eos
|
|
102
94
|
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Fastlane
|
|
2
|
+
class CrashlyticsBetaCommandLineHandler
|
|
3
|
+
def self.info_from_options(options)
|
|
4
|
+
beta_info = CrashlyticsBetaInfo.new
|
|
5
|
+
beta_info.crashlytics_path = options.crashlytics_path
|
|
6
|
+
beta_info.api_key = options.api_key
|
|
7
|
+
beta_info.build_secret = options.build_secret
|
|
8
|
+
beta_info.emails = options.emails
|
|
9
|
+
beta_info.schemes = [options.scheme] if options.scheme
|
|
10
|
+
beta_info.export_method = options.export_method
|
|
11
|
+
|
|
12
|
+
beta_info
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.apply_options(command)
|
|
16
|
+
command.option '--crashlytics_path STRING', String, 'Path to Crashlytics.framework'
|
|
17
|
+
command.option '--api_key STRING', String, 'Crashlytics API key'
|
|
18
|
+
command.option '--build_secret STRING', String, 'Crashlytics build secret'
|
|
19
|
+
command.option '--emails ARRAY', Array, 'List of emails to invite'
|
|
20
|
+
command.option '--scheme STRING', String, 'Xcode scheme'
|
|
21
|
+
command.option '--export_method STRING', String, 'Provisioning profile type (ad-hoc, enterprise, development)'
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Fastlane
|
|
2
|
+
class CrashlyticsBetaInfo
|
|
3
|
+
EXPORT_METHODS = %w(app-store ad-hoc package enterprise development developer-id).freeze
|
|
4
|
+
|
|
5
|
+
attr_accessor :crashlytics_path
|
|
6
|
+
attr_accessor :api_key
|
|
7
|
+
attr_accessor :build_secret
|
|
8
|
+
attr_accessor :emails
|
|
9
|
+
attr_accessor :schemes
|
|
10
|
+
attr_accessor :export_method
|
|
11
|
+
|
|
12
|
+
def schemes=(schemes)
|
|
13
|
+
@schemes = schemes ? schemes.compact : nil
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def emails=(emails)
|
|
17
|
+
@emails = emails ? emails.compact : nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def api_key_valid?
|
|
21
|
+
!api_key.nil? && api_key.to_s.length == 40
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def build_secret_valid?
|
|
25
|
+
!build_secret.nil? && build_secret.to_s.length == 64
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def crashlytics_path_valid?
|
|
29
|
+
!crashlytics_path.nil? && File.exist?(crashlytics_path) && File.exist?(File.join(crashlytics_path, 'submit'))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def emails_valid?
|
|
33
|
+
!emails.nil? && emails.any? { |email| !email.empty? }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def schemes_valid?
|
|
37
|
+
!schemes.nil? && schemes.size == 1 && !schemes.first.empty?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def export_method_valid?
|
|
41
|
+
!export_method.nil? && !export_method.empty? && EXPORT_METHODS.include?(export_method)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def has_all_detectable_values?
|
|
45
|
+
api_key && build_secret && crashlytics_path && emails && schemes
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
module Fastlane
|
|
2
|
+
class CrashlyticsBetaInfoCollector
|
|
3
|
+
def initialize(project_parser, user_email_fetcher, ui)
|
|
4
|
+
@project_parser = project_parser
|
|
5
|
+
@user_email_fetcher = user_email_fetcher
|
|
6
|
+
@ui = ui
|
|
7
|
+
|
|
8
|
+
@shown_fabric_org_help = false
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @param info CrashlyticsBetaInfo to supplement with needed info that is collected
|
|
12
|
+
def collect_info_into(info)
|
|
13
|
+
if user_provided_invalid_values?(info) || !info.has_all_detectable_values?
|
|
14
|
+
@ui.message "\nTrying to discover Beta by Crashlytics info from your project...".cyan
|
|
15
|
+
parse_project_info_into(info)
|
|
16
|
+
fetch_email_into(info)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
prompt_for_missing_values(info)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def user_provided_invalid_values?(info)
|
|
23
|
+
invalid = false
|
|
24
|
+
|
|
25
|
+
if info.crashlytics_path && !info.crashlytics_path_valid?
|
|
26
|
+
@ui.message "The crashlytics_path you provided (#{info.crashlytics_path}) is not valid."
|
|
27
|
+
invalid = true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if info.api_key && !info.api_key_valid?
|
|
31
|
+
@ui.message "The api_key you provided (#{info.api_key}) is not valid."
|
|
32
|
+
invalid = true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
if info.build_secret && !info.build_secret_valid?
|
|
36
|
+
@ui.message "The build_secret you provided (#{info.build_secret}) is not valid."
|
|
37
|
+
invalid = true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
if info.emails && !info.emails_valid?
|
|
41
|
+
@ui.message "The email you provided (#{info.emails.first}) is not valid."
|
|
42
|
+
invalid = true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if info.schemes && !info.schemes_valid?
|
|
46
|
+
@ui.message "The scheme you provided (#{info.schemes.first}) is not valid."
|
|
47
|
+
invalid = true
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
invalid
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def parse_project_info_into(info)
|
|
54
|
+
begin
|
|
55
|
+
info_hash = @project_parser.parse
|
|
56
|
+
rescue => ex
|
|
57
|
+
@ui.important ex.message
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
if info_hash
|
|
61
|
+
info.api_key = info_hash[:api_key] unless info.api_key_valid?
|
|
62
|
+
info.build_secret = info_hash[:build_secret] unless info.build_secret_valid?
|
|
63
|
+
info.crashlytics_path = info_hash[:crashlytics_path] unless info.crashlytics_path_valid?
|
|
64
|
+
info.schemes = info_hash[:schemes] unless info.schemes_valid?
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def fetch_email_into(info)
|
|
69
|
+
email = @user_email_fetcher.fetch
|
|
70
|
+
info.emails = [email] if !info.emails_valid? && email
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def prompt_for_missing_values(info)
|
|
74
|
+
if !info.crashlytics_path || !info.crashlytics_path_valid?
|
|
75
|
+
@ui.important "A Crashlytics submit binary path couldn't be discovered from your project 🔍"
|
|
76
|
+
prompt_for_crashlytics_path(info)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
if !info.api_key || !info.api_key_valid?
|
|
80
|
+
@ui.important "Your Fabric organization's API Key couldn't be discovered from your project 🔍"
|
|
81
|
+
show_fabric_org_help unless @shown_fabric_org_help
|
|
82
|
+
prompt_for_api_key(info)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
if !info.build_secret || !info.build_secret_valid?
|
|
86
|
+
@ui.important "Your Fabric organization's Build Secret couldn't be discovered from your project 🔍"
|
|
87
|
+
show_fabric_org_help unless @shown_fabric_org_help
|
|
88
|
+
prompt_for_build_secret(info)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
if !info.emails || !info.emails_valid?
|
|
92
|
+
@ui.important "Your email address couldn't be discovered from your project 🔍"
|
|
93
|
+
prompt_for_email(info)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if !info.schemes || info.schemes.empty?
|
|
97
|
+
@ui.important "Your scheme couldn't be discovered from your project 🔍"
|
|
98
|
+
prompt_for_schemes(info)
|
|
99
|
+
elsif info.schemes.size > 1
|
|
100
|
+
@ui.important "Multiple schemes were discovered from your project 🔍"
|
|
101
|
+
prompt_for_schemes(info)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
info.export_method = 'development' unless info.export_method
|
|
105
|
+
prompt_for_export_method(info) unless info.export_method_valid?
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def show_fabric_org_help
|
|
109
|
+
@ui.important("\nNavigate to https://www.fabric.io/settings/organizations, select the appropriate organization,")
|
|
110
|
+
@ui.important('and copy the API Key and Build Secret.')
|
|
111
|
+
@shown_fabric_org_help = true
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def prompt_for_api_key(info)
|
|
115
|
+
loop do
|
|
116
|
+
info.api_key = @ui.ask("\nPlease provide your Fabric organization's API Key:").strip
|
|
117
|
+
break if info.api_key_valid?
|
|
118
|
+
@ui.message "The API Key you provided was invalid (must be 40 characters)."
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def prompt_for_build_secret(info)
|
|
123
|
+
loop do
|
|
124
|
+
info.build_secret = @ui.ask("\nPlease provide your Fabric organization's Build Secret:").strip
|
|
125
|
+
break if info.build_secret_valid?
|
|
126
|
+
@ui.message "The Build Secret you provided was invalid (must be 64 characters)."
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def prompt_for_crashlytics_path(info)
|
|
131
|
+
loop do
|
|
132
|
+
info.crashlytics_path = @ui.ask("\nPlease provide the path to Crashlytics.framework:").strip
|
|
133
|
+
break if info.crashlytics_path_valid?
|
|
134
|
+
@ui.message "A submit binary could not be found at the framework path you provided."
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def prompt_for_email(info)
|
|
139
|
+
loop do
|
|
140
|
+
info.emails = [@ui.ask("\nPlease enter an email address to distribute the beta to:").strip]
|
|
141
|
+
break if info.emails_valid?
|
|
142
|
+
@ui.message "You must provide an email address."
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def prompt_for_schemes(info)
|
|
147
|
+
current_schemes = info.schemes
|
|
148
|
+
if current_schemes.nil? || current_schemes.empty?
|
|
149
|
+
loop do
|
|
150
|
+
info.schemes = [@ui.ask("\nPlease enter the name of the scheme you would like to use:").strip]
|
|
151
|
+
break if info.schemes_valid?
|
|
152
|
+
@ui.message "You must provide a scheme name."
|
|
153
|
+
end
|
|
154
|
+
else
|
|
155
|
+
info.schemes = [@ui.choose("\nWhich scheme would you like to use?", current_schemes)]
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def prompt_for_export_method(info)
|
|
160
|
+
@ui.important "The export method you entered was not valid."
|
|
161
|
+
info.export_method = @ui.choose("\nWhich export method would you like to use?", CrashlyticsBetaInfo::EXPORT_METHODS)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Fastlane
|
|
2
|
+
class CrashlyticsBetaUi
|
|
3
|
+
def success(text)
|
|
4
|
+
puts text.green
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def message(text)
|
|
8
|
+
puts text
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def header(text)
|
|
12
|
+
i = text.length + 8
|
|
13
|
+
success("-" * i)
|
|
14
|
+
success("--- " + text + " ---")
|
|
15
|
+
success("-" * i)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def important(text)
|
|
19
|
+
puts text.yellow
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def input(text)
|
|
23
|
+
UI.input(text)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def confirm(text)
|
|
27
|
+
UI.confirm(text)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def ask(text)
|
|
31
|
+
UI.ask(text)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def choose(text, options)
|
|
35
|
+
message(text)
|
|
36
|
+
Kernel.choose(*options)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Fastlane
|
|
2
|
+
class CrashlyticsBetaUserEmailFetcher
|
|
3
|
+
def initialize(app_file_path = nil)
|
|
4
|
+
@app_file_config = CredentialsManager::AppfileConfig.new(app_file_path)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def fetch
|
|
8
|
+
@app_file_config.data[:itunes_connect_id] ||
|
|
9
|
+
@app_file_config.data[:apple_dev_portal_id] ||
|
|
10
|
+
@app_file_config.data[:apple_id]
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'xcodeproj'
|
|
2
|
+
|
|
3
|
+
module Fastlane
|
|
4
|
+
class CrashlyticsProjectParser
|
|
5
|
+
# @param project_file_path path to a .xcodeproj file
|
|
6
|
+
def initialize(config = {})
|
|
7
|
+
FastlaneCore::Project.detect_projects(config)
|
|
8
|
+
@project = FastlaneCore::Project.new(config, xcodebuild_list_silent: true, xcodebuild_suppress_stderr: true)
|
|
9
|
+
|
|
10
|
+
@target_name = @project.default_build_settings(key: 'TARGETNAME')
|
|
11
|
+
@project_file_path = @project.is_workspace ? @project.path.gsub('xcworkspace', 'xcodeproj') : @project.path
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def parse
|
|
15
|
+
results = {
|
|
16
|
+
schemes: @project.schemes
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
xcode_project = Xcodeproj::Project.open(@project_file_path)
|
|
20
|
+
target = xcode_project.targets.find { |t| t.name == @target_name }
|
|
21
|
+
|
|
22
|
+
UI.crash!("Unable to locate a target by the name of #{@target_name}") if target.nil?
|
|
23
|
+
|
|
24
|
+
scripts = target.build_phases.select { |t| t.class == Xcodeproj::Project::Object::PBXShellScriptBuildPhase }
|
|
25
|
+
crash_script = scripts.find { |s| includes_run_script?(s.shell_script) }
|
|
26
|
+
|
|
27
|
+
UI.user_error!("Unable to find Crashlytics Run Script Build Phase") if crash_script.nil?
|
|
28
|
+
|
|
29
|
+
script_array = crash_script.shell_script.split('\n').find { |line| includes_run_script?(line) }.split(' ')
|
|
30
|
+
if script_array.count == 3
|
|
31
|
+
results.merge!({
|
|
32
|
+
# The run script build phase probably refers to Fabric.framework/run, but the submit binary
|
|
33
|
+
# only lives in the Crashlytics.framework, so we'll substitute and try to resolve it that way.
|
|
34
|
+
crashlytics_path: File.dirname(script_array[0].gsub("Fabric.framework", "Crashlytics.framework")),
|
|
35
|
+
api_key: script_array[1],
|
|
36
|
+
build_secret: script_array[2]
|
|
37
|
+
})
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
results
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def includes_run_script?(string)
|
|
44
|
+
['Fabric/run', 'Crashlytics/run', 'Fabric.framework/run', 'Crashlytics.framework/run'].any? do |script_path_fragment|
|
|
45
|
+
string.include?(script_path_fragment)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
data/lib/fastlane/setup/setup.rb
CHANGED
|
@@ -42,3 +42,10 @@ end
|
|
|
42
42
|
|
|
43
43
|
require 'fastlane/setup/setup_ios'
|
|
44
44
|
require 'fastlane/setup/setup_android'
|
|
45
|
+
require 'fastlane/setup/crashlytics_beta_ui'
|
|
46
|
+
require 'fastlane/setup/crashlytics_beta'
|
|
47
|
+
require 'fastlane/setup/crashlytics_project_parser'
|
|
48
|
+
require 'fastlane/setup/crashlytics_beta_info'
|
|
49
|
+
require 'fastlane/setup/crashlytics_beta_info_collector'
|
|
50
|
+
require 'fastlane/setup/crashlytics_beta_command_line_handler'
|
|
51
|
+
require 'fastlane/setup/crashlytics_beta_user_email_fetcher'
|
data/lib/fastlane/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fastlane
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.99.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Felix Krause
|
|
@@ -15,7 +15,7 @@ authors:
|
|
|
15
15
|
autorequire:
|
|
16
16
|
bindir: bin
|
|
17
17
|
cert_chain: []
|
|
18
|
-
date: 2016-07-
|
|
18
|
+
date: 2016-07-27 00:00:00.000000000 Z
|
|
19
19
|
dependencies:
|
|
20
20
|
- !ruby/object:Gem::Dependency
|
|
21
21
|
name: krausefx-shenzhen
|
|
@@ -183,7 +183,7 @@ dependencies:
|
|
|
183
183
|
requirements:
|
|
184
184
|
- - ">="
|
|
185
185
|
- !ruby/object:Gem::Version
|
|
186
|
-
version: 0.48.
|
|
186
|
+
version: 0.48.3
|
|
187
187
|
- - "<"
|
|
188
188
|
- !ruby/object:Gem::Version
|
|
189
189
|
version: 1.0.0
|
|
@@ -193,7 +193,7 @@ dependencies:
|
|
|
193
193
|
requirements:
|
|
194
194
|
- - ">="
|
|
195
195
|
- !ruby/object:Gem::Version
|
|
196
|
-
version: 0.48.
|
|
196
|
+
version: 0.48.3
|
|
197
197
|
- - "<"
|
|
198
198
|
- !ruby/object:Gem::Version
|
|
199
199
|
version: 1.0.0
|
|
@@ -941,8 +941,15 @@ files:
|
|
|
941
941
|
- lib/fastlane/plugins/template/lib/fastlane/plugin/%plugin_name%/version.rb.erb
|
|
942
942
|
- lib/fastlane/plugins/template/spec/%plugin_name%_action_spec.rb.erb
|
|
943
943
|
- lib/fastlane/plugins/template/spec/spec_helper.rb.erb
|
|
944
|
+
- lib/fastlane/plugins/templates/.DS_Store
|
|
944
945
|
- lib/fastlane/runner.rb
|
|
945
946
|
- lib/fastlane/setup/crashlytics_beta.rb
|
|
947
|
+
- lib/fastlane/setup/crashlytics_beta_command_line_handler.rb
|
|
948
|
+
- lib/fastlane/setup/crashlytics_beta_info.rb
|
|
949
|
+
- lib/fastlane/setup/crashlytics_beta_info_collector.rb
|
|
950
|
+
- lib/fastlane/setup/crashlytics_beta_ui.rb
|
|
951
|
+
- lib/fastlane/setup/crashlytics_beta_user_email_fetcher.rb
|
|
952
|
+
- lib/fastlane/setup/crashlytics_project_parser.rb
|
|
946
953
|
- lib/fastlane/setup/setup.rb
|
|
947
954
|
- lib/fastlane/setup/setup_android.rb
|
|
948
955
|
- lib/fastlane/setup/setup_ios.rb
|
|
@@ -969,7 +976,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
969
976
|
version: '0'
|
|
970
977
|
requirements: []
|
|
971
978
|
rubyforge_project:
|
|
972
|
-
rubygems_version: 2.5.1
|
|
979
|
+
rubygems_version: 2.4.5.1
|
|
973
980
|
signing_key:
|
|
974
981
|
specification_version: 4
|
|
975
982
|
summary: The easiest way to automate building and releasing your iOS and Android apps
|