fastlane 1.98.0 → 1.99.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1f16378edbabcd69c5f903140420acfe44d166c7
4
- data.tar.gz: 90af73d800d00489162497032aebb4d9de19e248
3
+ metadata.gz: 28b8a9977502edeea107da475bedffceb4ff575e
4
+ data.tar.gz: 45ec673ada611ee9ebe2348fd90eae048c5a26cb
5
5
  SHA512:
6
- metadata.gz: 5f7c8aa76a5b2afe13d06a363e0c2f1dbedebf0b759d3e58df24c62930724be515f3e561f9a522023b5beef08a1eb47405ab1b352402c2b3a2f98e8c4716888a
7
- data.tar.gz: acfa0f72deaf2f82735844cc9a0e124e0c36c20cd9d71a2e1231980f90a8e424fdf5f9abc0e5812352bfb3cdd3b4df7fc47270bcf7204ae7157067067e2d56c8
6
+ metadata.gz: d2250b41a5ab0b0e806550352e723eaf1e84d233fdd1c705a9edd5dcb578c82bab9d3400bae707289e2559445ae05a308ac90523a266b46de32c2f8b485f8426
7
+ data.tar.gz: 59c837b323a5fa9d8978a689ac584ce236995e2e2833e64d97d845220bea3c4ece1522fbfcdb9e14dea90f70e9f16d6dc2694a5d512e461a2e39ed55f0cf8caa
Binary file
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? platform
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
@@ -54,6 +54,7 @@ module Fastlane
54
54
  UI.verbose sanitizer.call(result) if $verbose
55
55
 
56
56
  UI.success('Build successfully uploaded to Crashlytics Beta 🌷')
57
+ UI.success('Visit https://fabric.io/_/beta to add release notes and notify testers.')
57
58
  end
58
59
 
59
60
  def self.description
@@ -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/blob/master/fastlane/docs/CodeSigning.md")
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/blob/master/fastlane/docs/CodeSigning.md")
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
- require 'fastlane/setup/crashlytics_beta'
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] = Dir["./Pods/iOS/Crashlytics/Crashlytics.framework"].last || Dir["./**/Crashlytics.framework"].last
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]
@@ -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
- UI.user_error!('Beta by Crashlytics configuration is currently only available for iOS projects.') unless Setup.new.is_ios?
5
- config = {}
6
- FastlaneCore::Project.detect_projects(config)
7
- project = FastlaneCore::Project.new(config)
8
- keys = keys_from_project(project)
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
- UI.header('Copy and paste the following lane into your Fastfile to use Crashlytics Beta!')
12
- puts lane_template(keys[:api_key], keys[:build_secret], project.schemes.first).cyan
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(keys[:api_key], keys[:build_secret], project.schemes.first)
35
+ fastfile = fastfile_template
15
36
  FileUtils.mkdir_p('fastlane')
16
37
  File.write('fastlane/Fastfile', fastfile)
17
- UI.success('A Fastfile has been generated for you at ./fastlane/Fastfile 🚀')
38
+ @ui.success('A Fastfile has been generated for you at ./fastlane/Fastfile 🚀')
18
39
  end
19
- UI.header('Next Steps')
20
- UI.success('Run `fastlane beta` to build and upload to Beta by Crashlytics. 🎯')
21
- UI.success('After submitting your beta, visit https://fabric.io/_/beta to add release notes and notify testers.')
22
- UI.success('You can edit your Fastfile to distribute and notify testers automatically.')
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 keys_from_project(project)
27
- require 'xcodeproj'
28
- target_name = project.default_build_settings(key: 'TARGETNAME')
29
- path = project.is_workspace ? project.path.gsub('xcworkspace', 'xcodeproj') : project.path
30
- UI.crash!("No project available at path #{path}") unless File.exist?(path)
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
- def api_key_valid?(key)
63
- key.to_s.length == 40
64
- end
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
- def build_secret_valid?(secret)
67
- secret.to_s.length == 64
68
- end
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
- def includes_run_script?(string)
71
- string.include?('Fabric/run') || string.include?('Crashlytics/run') || string.include?('Fabric.framework/run') || string.include?('Crashlytics.framework/run')
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 lane_template(api_key, build_secret, scheme)
75
- %{
76
- lane :beta do
77
- # set 'export_method' to 'ad-hoc' if your Crashlytics Beta distribution uses ad-hoc provisioning
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(api_key, build_secret, scheme)
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
- lane :beta do
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
@@ -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'
@@ -1,4 +1,4 @@
1
1
  module Fastlane
2
- VERSION = '1.98.0'.freeze
2
+ VERSION = '1.99.0'.freeze
3
3
  DESCRIPTION = "The easiest way to automate building and releasing your iOS and Android apps"
4
4
  end
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.98.0
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 00:00:00.000000000 Z
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.1
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.1
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