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 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