fastlane 2.133.0 → 2.136.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +78 -78
  3. data/fastlane/lib/fastlane/action.rb +1 -1
  4. data/fastlane/lib/fastlane/actions/actions_helper.rb +1 -1
  5. data/fastlane/lib/fastlane/actions/carthage.rb +7 -0
  6. data/fastlane/lib/fastlane/actions/cocoapods.rb +24 -2
  7. data/fastlane/lib/fastlane/actions/deploygate.rb +1 -1
  8. data/fastlane/lib/fastlane/actions/docs/capture_android_screenshots.md +38 -4
  9. data/fastlane/lib/fastlane/actions/docs/capture_ios_screenshots.md +1 -1
  10. data/fastlane/lib/fastlane/actions/docs/sync_code_signing.md +4 -2
  11. data/fastlane/lib/fastlane/actions/docs/upload_to_play_store.md +26 -2
  12. data/fastlane/lib/fastlane/actions/download_dsyms.rb +26 -3
  13. data/fastlane/lib/fastlane/actions/download_from_play_store.rb +1 -1
  14. data/fastlane/lib/fastlane/actions/get_version_number.rb +10 -4
  15. data/fastlane/lib/fastlane/actions/google_play_track_version_codes.rb +5 -1
  16. data/fastlane/lib/fastlane/actions/sonar.rb +16 -0
  17. data/fastlane/lib/fastlane/actions/testfairy.rb +1 -1
  18. data/fastlane/lib/fastlane/actions/update_fastlane.rb +9 -49
  19. data/fastlane/lib/fastlane/actions/update_keychain_access_groups.rb +94 -0
  20. data/fastlane/lib/fastlane/environment_printer.rb +9 -3
  21. data/fastlane/lib/fastlane/fast_file.rb +3 -2
  22. data/fastlane/lib/fastlane/lane_manager.rb +1 -1
  23. data/fastlane/lib/fastlane/plugins/plugin_manager.rb +12 -2
  24. data/fastlane/lib/fastlane/plugins/template/.rubocop.yml +2 -0
  25. data/fastlane/lib/fastlane/runner.rb +2 -2
  26. data/fastlane/lib/fastlane/swift_fastlane_function.rb +9 -0
  27. data/fastlane/lib/fastlane/version.rb +1 -1
  28. data/fastlane/swift/Deliverfile.swift +1 -1
  29. data/fastlane/swift/Fastlane.swift +124 -36
  30. data/fastlane/swift/Gymfile.swift +1 -1
  31. data/fastlane/swift/Matchfile.swift +1 -1
  32. data/fastlane/swift/MatchfileProtocol.swift +2 -2
  33. data/fastlane/swift/Precheckfile.swift +1 -1
  34. data/fastlane/swift/Scanfile.swift +1 -1
  35. data/fastlane/swift/Screengrabfile.swift +1 -1
  36. data/fastlane/swift/ScreengrabfileProtocol.swift +22 -2
  37. data/fastlane/swift/Snapshotfile.swift +1 -1
  38. data/fastlane_core/lib/fastlane_core/configuration/commander_generator.rb +3 -3
  39. data/fastlane_core/lib/fastlane_core/configuration/configuration.rb +1 -1
  40. data/fastlane_core/lib/fastlane_core/device_manager.rb +1 -1
  41. data/fastlane_core/lib/fastlane_core/swag.rb +1 -1
  42. data/fastlane_core/lib/fastlane_core/ui/fastlane_runner.rb +1 -1
  43. data/match/lib/match/importer.rb +1 -1
  44. data/scan/lib/scan/error_handler.rb +9 -4
  45. data/scan/lib/scan/runner.rb +1 -1
  46. data/screengrab/lib/screengrab/module.rb +2 -0
  47. data/screengrab/lib/screengrab/options.rb +33 -11
  48. data/screengrab/lib/screengrab/runner.rb +64 -24
  49. data/sigh/lib/assets/resign.sh +2 -2
  50. data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher.rb +1 -1
  51. data/spaceship/lib/spaceship/client.rb +2 -2
  52. data/supply/lib/supply.rb +23 -0
  53. data/supply/lib/supply/.uploader.rb.swp +0 -0
  54. data/supply/lib/supply/client.rb +101 -55
  55. data/supply/lib/supply/options.rb +50 -14
  56. data/supply/lib/supply/release_listing.rb +18 -0
  57. data/supply/lib/supply/setup.rb +42 -34
  58. data/supply/lib/supply/uploader.rb +191 -93
  59. metadata +32 -37
  60. data/fastlane/lib/fastlane/actions/.hockey.rb.swp +0 -0
  61. data/fastlane/lib/fastlane/actions/.slack.rb.swp +0 -0
  62. data/fastlane/lib/fastlane/actions/.update_project_provisioning.rb.swp +0 -0
  63. data/fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/project.xcworkspace/xcuserdata/josh.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  64. data/gym/lib/gym/.runner.rb.swp +0 -0
  65. data/pilot/lib/pilot/.manager.rb.swp +0 -0
  66. data/spaceship/lib/spaceship/connect_api/.DS_Store +0 -0
  67. data/spaceship/lib/spaceship/portal/.certificate.rb.swp +0 -0
@@ -18,4 +18,4 @@ class Gymfile: GymfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.133.0
21
+ // Generated with fastlane 2.136.0
@@ -18,4 +18,4 @@ class Matchfile: MatchfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.133.0
21
+ // Generated with fastlane 2.136.0
@@ -91,7 +91,7 @@ protocol MatchfileProtocol: class {
91
91
  extension MatchfileProtocol {
92
92
  var type: String { return "development" }
93
93
  var readonly: Bool { return false }
94
- var generateAppleCerts: Bool { return true }
94
+ var generateAppleCerts: Bool { return false }
95
95
  var skipProvisioningProfiles: Bool { return false }
96
96
  var appIdentifier: [String] { return [] }
97
97
  var username: String { return "" }
@@ -122,4 +122,4 @@ extension MatchfileProtocol {
122
122
 
123
123
  // Please don't remove the lines below
124
124
  // They are used to detect outdated files
125
- // FastlaneRunnerAPIVersion [0.9.9]
125
+ // FastlaneRunnerAPIVersion [0.9.10]
@@ -18,4 +18,4 @@ class Precheckfile: PrecheckfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.133.0
21
+ // Generated with fastlane 2.136.0
@@ -18,4 +18,4 @@ class Scanfile: ScanfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.133.0
21
+ // Generated with fastlane 2.136.0
@@ -18,4 +18,4 @@ class Screengrabfile: ScreengrabfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.133.0
21
+ // Generated with fastlane 2.136.0
@@ -39,6 +39,9 @@ protocol ScreengrabfileProtocol: class {
39
39
  /// Return the device to this locale after running tests
40
40
  var endingLocale: String { get }
41
41
 
42
+ /// Restarts the adb daemon using `adb root` to allow access to screenshots directories on device. Use if getting 'Permission denied' errors
43
+ var useAdbRoot: Bool { get }
44
+
42
45
  /// The path to the APK for the app under test
43
46
  var appApkPath: String? { get }
44
47
 
@@ -56,6 +59,18 @@ protocol ScreengrabfileProtocol: class {
56
59
 
57
60
  /// Enabling this option will automatically uninstall the application before running it
58
61
  var reinstallApp: Bool { get }
62
+
63
+ /// Add timestamp suffix to screenshot filename
64
+ var useTimestampSuffix: Bool { get }
65
+
66
+ /// Configure the host used by adb to connect, allows running on remote devices farm
67
+ var adbHost: String? { get }
68
+
69
+ /// Enabling this option will clean the status bar
70
+ var cleanStatusBar: Bool { get }
71
+
72
+ /// Specifies the configuration for the clean status bar
73
+ var cleanStatusBarConfig: [String : Any] { get }
59
74
  }
60
75
 
61
76
  extension ScreengrabfileProtocol {
@@ -70,16 +85,21 @@ extension ScreengrabfileProtocol {
70
85
  var useTestsInPackages: [String]? { return nil }
71
86
  var useTestsInClasses: [String]? { return nil }
72
87
  var launchArguments: [String]? { return nil }
73
- var testInstrumentationRunner: String { return "android.support.test.runner.AndroidJUnitRunner" }
88
+ var testInstrumentationRunner: String { return "androidx.test.runner.AndroidJUnitRunner" }
74
89
  var endingLocale: String { return "en-US" }
90
+ var useAdbRoot: Bool { return false }
75
91
  var appApkPath: String? { return nil }
76
92
  var testsApkPath: String? { return nil }
77
93
  var specificDevice: String? { return nil }
78
94
  var deviceType: String { return "phone" }
79
95
  var exitOnTestFailure: Bool { return true }
80
96
  var reinstallApp: Bool { return false }
97
+ var useTimestampSuffix: Bool { return true }
98
+ var adbHost: String? { return nil }
99
+ var cleanStatusBar: Bool { return false }
100
+ var cleanStatusBarConfig: [String : Any] { return [:] }
81
101
  }
82
102
 
83
103
  // Please don't remove the lines below
84
104
  // They are used to detect outdated files
85
- // FastlaneRunnerAPIVersion [0.9.11]
105
+ // FastlaneRunnerAPIVersion [0.9.12]
@@ -18,4 +18,4 @@ class Snapshotfile: SnapshotfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.133.0
21
+ // Generated with fastlane 2.136.0
@@ -93,9 +93,9 @@ module FastlaneCore
93
93
  return if short_switch.nil?
94
94
 
95
95
  UI.user_error!("Short option #{short_switch} already taken for key #{key}") if used_switches.include?(short_switch)
96
- UI.user_error!("-v is already used for the version (key #{key})") if short_switch == "-v"
97
- UI.user_error!("-h is already used for the help screen (key #{key})") if short_switch == "-h"
98
- UI.user_error!("-t is already used for the trace screen (key #{key})") if short_switch == "-t"
96
+ UI.user_error!("-v is already used for the fastlane version (key #{key})") if short_switch == "-v"
97
+ UI.user_error!("-h is already used for the fastlane help screen (key #{key})") if short_switch == "-h"
98
+ UI.user_error!("-t is already used for the fastlane trace screen (key #{key})") if short_switch == "-t"
99
99
 
100
100
  used_switches << short_switch
101
101
  end
@@ -120,7 +120,7 @@ module FastlaneCore
120
120
  index = @available_options.find_index { |item| item.key == conflicting_option_key }
121
121
  conflicting_option = @available_options[index]
122
122
 
123
- # ignore conflicts because because value of conflict option is nil
123
+ # ignore conflicts because value of conflict option is nil
124
124
  next if @values[conflicting_option.key].nil?
125
125
 
126
126
  if current.conflict_block
@@ -307,7 +307,7 @@ module FastlaneCore
307
307
  logarchive_dst = File.join(logs_destination_dir, "system_logs-#{log_identity}.logarchive").shellescape
308
308
  FileUtils.rm_rf(logarchive_dst)
309
309
  FileUtils.mkdir_p(File.expand_path("..", logarchive_dst))
310
- command = "xcrun simctl spawn #{device.udid} log collect --output #{logarchive_dst} 2>/dev/null"
310
+ command = "xcrun simctl spawn --standalone #{device.udid} log collect --output #{logarchive_dst} 2>/dev/null"
311
311
  FastlaneCore::CommandExecutor.execute(command: command, print_all: false, print_command: true)
312
312
  end
313
313
  end
@@ -45,7 +45,7 @@ module FastlaneCore
45
45
 
46
46
  def self.show_loader
47
47
  return unless should_be_shown?
48
- # sound is disabled as i didn't find a royality free nice midi :(
48
+ # sound is disabled as I didn't find a royality free nice midi :(
49
49
  @output = StringIO.new
50
50
  # if FastlaneCore::Env.truthy?("FL_DO_SOUND")
51
51
  # @sound_thr = Thread.new do
@@ -117,7 +117,7 @@ module Commander
117
117
  abort(e.to_s)
118
118
  end
119
119
  end
120
- rescue FastlaneCore::Interface::FastlaneCommonException => e # these are exceptions that we dont count as crashes
120
+ rescue FastlaneCore::Interface::FastlaneCommonException => e # these are exceptions that we don't count as crashes
121
121
  display_user_error!(e, e.to_s)
122
122
  rescue FastlaneCore::Interface::FastlaneError => e # user_error!
123
123
  rescue_fastlane_error(e)
@@ -17,7 +17,7 @@ module Match
17
17
  UI.user_error!("Certificate does not exist at path: #{cert_path}") unless File.exist?(cert_path)
18
18
  UI.user_error!("Private key does not exist at path: #{p12_path}") unless File.exist?(p12_path)
19
19
 
20
- # Base64 encrypt contents to find match from API to find a cert ID
20
+ # Base64 encode contents to find match from API to find a cert ID
21
21
  cert_contents_base_64 = Base64.strict_encode64(File.open(cert_path).read)
22
22
 
23
23
  # Storage
@@ -6,8 +6,13 @@ module Scan
6
6
  class << self
7
7
  # @param [String] The output of the errored build
8
8
  # This method should raise an exception in any case, as the return code indicated a failed build
9
- def handle_build_error(output)
10
- # The order of the handling below is import
9
+ def handle_build_error(output, log_path)
10
+ # The order of the handling below is important
11
+
12
+ instruction = 'See the log'
13
+ location = Scan.config[:suppress_xcode_output] ? "here: '#{log_path}'" : "above"
14
+ details = "#{instruction} #{location}."
15
+
11
16
  case output
12
17
  when /US\-ASCII/
13
18
  print("Your shell environment is not correctly configured")
@@ -23,7 +28,7 @@ module Scan
23
28
  print("For more information visit this stackoverflow answer:")
24
29
  print("https://stackoverflow.com/a/17031697/445598")
25
30
  when /Testing failed/
26
- UI.build_failure!("Error building the application - see the log above")
31
+ UI.build_failure!("Error building the application. #{details}")
27
32
  when /Executed/, /Failing tests:/
28
33
  # this is *really* important:
29
34
  # we don't want to raise an exception here
@@ -38,7 +43,7 @@ module Scan
38
43
  # followed by a list of tests that failed.
39
44
  return
40
45
  end
41
- UI.build_failure!("Error building/testing the application - see the log above")
46
+ UI.build_failure!("Error building/testing the application. #{details}")
42
47
  end
43
48
 
44
49
  private
@@ -67,7 +67,7 @@ module Scan
67
67
  error: proc do |error_output|
68
68
  begin
69
69
  exit_status = $?.exitstatus
70
- ErrorHandler.handle_build_error(error_output)
70
+ ErrorHandler.handle_build_error(error_output, @test_command_generator.xcodebuild_log_path)
71
71
  rescue => ex
72
72
  SlackPoster.new.run({
73
73
  build_errors: 1
@@ -1,4 +1,5 @@
1
1
  require 'fastlane_core/helper'
2
+ require 'fastlane/boolean'
2
3
  require_relative 'detect_values'
3
4
 
4
5
  module Screengrab
@@ -19,6 +20,7 @@ module Screengrab
19
20
 
20
21
  Helper = FastlaneCore::Helper # you gotta love Ruby: Helper.* should use the Helper class contained in FastlaneCore
21
22
  UI = FastlaneCore::UI
23
+ Boolean = Fastlane::Boolean
22
24
  ROOT = Pathname.new(File.expand_path('../../..', __FILE__))
23
25
  DESCRIPTION = "Automated localized screenshots of your Android app on every device".freeze
24
26
  end
@@ -32,7 +32,7 @@ module Screengrab
32
32
  env_name: 'SCREENGRAB_CLEAR_PREVIOUS_SCREENSHOTS',
33
33
  description: "Enabling this option will automatically clear previously generated screenshots before running screengrab",
34
34
  default_value: false,
35
- is_string: false),
35
+ type: Boolean),
36
36
  FastlaneCore::ConfigItem.new(key: :output_directory,
37
37
  short_option: "-o",
38
38
  env_name: "SCREENGRAB_OUTPUT_DIRECTORY",
@@ -43,7 +43,7 @@ module Screengrab
43
43
  description: "Don't open the summary after running _screengrab_",
44
44
  default_value: DEFAULT_SKIP_OPEN_SUMMARY,
45
45
  default_value_dynamic: true,
46
- is_string: false),
46
+ type: Boolean),
47
47
  FastlaneCore::ConfigItem.new(key: :app_package_name,
48
48
  env_name: 'SCREENGRAB_APP_PACKAGE_NAME',
49
49
  short_option: "-a",
@@ -68,22 +68,26 @@ module Screengrab
68
68
  type: Array,
69
69
  description: "Only run tests in these Java classes"),
70
70
  FastlaneCore::ConfigItem.new(key: :launch_arguments,
71
- env_name: 'SCREENGRAB_LAUNCH_ARGUMENTS',
72
- optional: true,
73
- short_option: "-e",
74
- type: Array,
75
- description: "Additional launch arguments"),
71
+ env_name: 'SCREENGRAB_LAUNCH_ARGUMENTS',
72
+ optional: true,
73
+ short_option: "-e",
74
+ type: Array,
75
+ description: "Additional launch arguments"),
76
76
  FastlaneCore::ConfigItem.new(key: :test_instrumentation_runner,
77
77
  env_name: 'SCREENGRAB_TEST_INSTRUMENTATION_RUNNER',
78
78
  optional: true,
79
- default_value: 'android.support.test.runner.AndroidJUnitRunner',
79
+ default_value: 'androidx.test.runner.AndroidJUnitRunner',
80
80
  description: "The fully qualified class name of your test instrumentation runner"),
81
81
  FastlaneCore::ConfigItem.new(key: :ending_locale,
82
82
  env_name: 'SCREENGRAB_ENDING_LOCALE',
83
83
  optional: true,
84
- is_string: true,
85
84
  default_value: 'en-US',
86
85
  description: "Return the device to this locale after running tests"),
86
+ FastlaneCore::ConfigItem.new(key: :use_adb_root,
87
+ env_name: 'SCREENGRAB_USE_ADB_ROOT',
88
+ description: "Restarts the adb daemon using `adb root` to allow access to screenshots directories on device. Use if getting 'Permission denied' errors",
89
+ default_value: false,
90
+ type: Boolean),
87
91
  FastlaneCore::ConfigItem.new(key: :app_apk_path,
88
92
  env_name: 'SCREENGRAB_APP_APK_PATH',
89
93
  optional: true,
@@ -123,12 +127,30 @@ module Screengrab
123
127
  env_name: 'EXIT_ON_TEST_FAILURE',
124
128
  description: "Whether or not to exit Screengrab on test failure. Exiting on failure will not copy sceenshots to local machine nor open sceenshots summary",
125
129
  default_value: true,
126
- is_string: false),
130
+ type: Boolean),
127
131
  FastlaneCore::ConfigItem.new(key: :reinstall_app,
128
132
  env_name: 'SCREENGRAB_REINSTALL_APP',
129
133
  description: "Enabling this option will automatically uninstall the application before running it",
130
134
  default_value: false,
131
- is_string: false)
135
+ type: Boolean),
136
+ FastlaneCore::ConfigItem.new(key: :use_timestamp_suffix,
137
+ env_name: 'SCREENGRAB_USE_TIMESTAMP_SUFFIX',
138
+ description: "Add timestamp suffix to screenshot filename",
139
+ default_value: true,
140
+ type: Boolean),
141
+ FastlaneCore::ConfigItem.new(key: :adb_host,
142
+ env_name: 'SCREENGRAB_ADB_HOST',
143
+ description: "Configure the host used by adb to connect, allows running on remote devices farm",
144
+ optional: true),
145
+ FastlaneCore::ConfigItem.new(key: :clean_status_bar,
146
+ env_name: 'SCREENGRAB_CLEAN_STATUS_BAR',
147
+ description: "Enabling this option will clean the status bar",
148
+ default_value: false,
149
+ type: Boolean),
150
+ FastlaneCore::ConfigItem.new(key: :clean_status_bar_config,
151
+ description: "Specifies the configuration for the clean status bar",
152
+ default_value: {},
153
+ type: Hash)
132
154
  ]
133
155
  end
134
156
  end
@@ -57,8 +57,13 @@ module Screengrab
57
57
 
58
58
  device_screenshots_paths = [
59
59
  determine_external_screenshots_path(device_serial),
60
- determine_internal_screenshots_path
61
- ]
60
+ determine_internal_screenshots_paths(@config[:app_package_name], @config[:locales])
61
+ ].flatten
62
+
63
+ # Root is needed to access device paths at /data
64
+ if @config[:use_adb_root]
65
+ run_adb_command("root", print_all: false, print_command: true)
66
+ end
62
67
 
63
68
  clear_device_previous_screenshots(device_serial, device_screenshots_paths)
64
69
 
@@ -67,6 +72,8 @@ module Screengrab
67
72
 
68
73
  validate_apk(app_apk_path)
69
74
 
75
+ enable_clean_status_bar(device_serial, app_apk_path)
76
+
70
77
  run_tests(device_serial, app_apk_path, tests_apk_path, test_classes_to_use, test_packages_to_use, @config[:launch_arguments])
71
78
 
72
79
  number_of_screenshots = pull_screenshots_from_device(device_serial, device_screenshots_paths, device_type_dir_name)
@@ -77,7 +84,7 @@ module Screengrab
77
84
  end
78
85
 
79
86
  def select_device
80
- devices = run_adb_command("adb devices -l", print_all: true, print_command: true).split("\n")
87
+ devices = run_adb_command("devices -l", print_all: true, print_command: true).split("\n")
81
88
  # the first output by adb devices is "List of devices attached" so remove that and any adb startup output
82
89
  devices.reject! do |device|
83
90
  [
@@ -139,15 +146,19 @@ module Screengrab
139
146
  # macOS evaluates $foo in `echo $foo` before executing the command,
140
147
  # Windows doesn't - hence the double backslash vs. single backslash
141
148
  command = Helper.windows? ? "shell echo \$EXTERNAL_STORAGE " : "shell echo \\$EXTERNAL_STORAGE"
142
- device_ext_storage = run_adb_command("adb -s #{device_serial} #{command}",
149
+ device_ext_storage = run_adb_command("-s #{device_serial} #{command}",
143
150
  print_all: true,
144
151
  print_command: true)
145
152
  device_ext_storage = device_ext_storage.strip
146
153
  File.join(device_ext_storage, @config[:app_package_name], 'screengrab')
147
154
  end
148
155
 
149
- def determine_internal_screenshots_path
150
- "/data/data/#{@config[:app_package_name]}/app_screengrab"
156
+ def determine_internal_screenshots_paths(app_package_name, locales)
157
+ locale_paths = locales.map do |locale|
158
+ "/data/user/0/#{app_package_name}/files/#{app_package_name}/screengrab/#{locale}/images/screenshots"
159
+ end
160
+
161
+ return ["/data/data/#{app_package_name}/app_screengrab"] + locale_paths
151
162
  end
152
163
 
153
164
  def clear_device_previous_screenshots(device_serial, device_screenshots_paths)
@@ -155,7 +166,7 @@ module Screengrab
155
166
 
156
167
  device_screenshots_paths.each do |device_path|
157
168
  if_device_path_exists(device_serial, device_path) do |path|
158
- run_adb_command("adb -s #{device_serial} shell rm -rf #{path}",
169
+ run_adb_command("-s #{device_serial} shell rm -rf #{path}",
159
170
  print_all: true,
160
171
  print_command: true)
161
172
  end
@@ -182,13 +193,13 @@ module Screengrab
182
193
 
183
194
  def install_apks(device_serial, app_apk_path, tests_apk_path)
184
195
  UI.message('Installing app APK')
185
- apk_install_output = run_adb_command("adb -s #{device_serial} install -t -r #{app_apk_path.shellescape}",
196
+ apk_install_output = run_adb_command("-s #{device_serial} install -t -r #{app_apk_path.shellescape}",
186
197
  print_all: true,
187
198
  print_command: true)
188
199
  UI.user_error!("App APK could not be installed") if apk_install_output.include?("Failure [")
189
200
 
190
201
  UI.message('Installing tests APK')
191
- apk_install_output = run_adb_command("adb -s #{device_serial} install -t -r #{tests_apk_path.shellescape}",
202
+ apk_install_output = run_adb_command("-s #{device_serial} install -t -r #{tests_apk_path.shellescape}",
192
203
  print_all: true,
193
204
  print_command: true)
194
205
  UI.user_error!("Tests APK could not be installed") if apk_install_output.include?("Failure [")
@@ -199,14 +210,14 @@ module Screengrab
199
210
 
200
211
  if packages.include?(app_package_name.to_s)
201
212
  UI.message('Uninstalling app APK')
202
- run_adb_command("adb -s #{device_serial} uninstall #{app_package_name}",
213
+ run_adb_command("-s #{device_serial} uninstall #{app_package_name}",
203
214
  print_all: true,
204
215
  print_command: true)
205
216
  end
206
217
 
207
218
  if packages.include?(tests_package_name.to_s)
208
219
  UI.message('Uninstalling tests APK')
209
- run_adb_command("adb -s #{device_serial} uninstall #{tests_package_name}",
220
+ run_adb_command("-s #{device_serial} uninstall #{tests_package_name}",
210
221
  print_all: true,
211
222
  print_command: true)
212
223
  end
@@ -214,20 +225,16 @@ module Screengrab
214
225
 
215
226
  def grant_permissions(device_serial)
216
227
  UI.message('Granting the permission necessary to change locales on the device')
217
- run_adb_command("adb -s #{device_serial} shell pm grant #{@config[:app_package_name]} android.permission.CHANGE_CONFIGURATION",
228
+ run_adb_command("-s #{device_serial} shell pm grant #{@config[:app_package_name]} android.permission.CHANGE_CONFIGURATION",
218
229
  print_all: true,
219
230
  print_command: true)
220
231
 
221
- device_api_version = run_adb_command("adb -s #{device_serial} shell getprop ro.build.version.sdk",
222
- print_all: true,
223
- print_command: true).to_i
224
-
225
- if device_api_version >= 23
232
+ if device_api_version(device_serial) >= 23
226
233
  UI.message('Granting the permissions necessary to access device external storage')
227
- run_adb_command("adb -s #{device_serial} shell pm grant #{@config[:app_package_name]} android.permission.WRITE_EXTERNAL_STORAGE",
234
+ run_adb_command("-s #{device_serial} shell pm grant #{@config[:app_package_name]} android.permission.WRITE_EXTERNAL_STORAGE",
228
235
  print_all: true,
229
236
  print_command: true)
230
- run_adb_command("adb -s #{device_serial} shell pm grant #{@config[:app_package_name]} android.permission.READ_EXTERNAL_STORAGE",
237
+ run_adb_command("-s #{device_serial} shell pm grant #{@config[:app_package_name]} android.permission.READ_EXTERNAL_STORAGE",
231
238
  print_all: true,
232
239
  print_command: true)
233
240
  end
@@ -252,9 +259,10 @@ module Screengrab
252
259
  def run_tests_for_locale(locale, device_serial, test_classes_to_use, test_packages_to_use, launch_arguments)
253
260
  UI.message("Running tests for locale: #{locale}")
254
261
 
255
- instrument_command = ["adb -s #{device_serial} shell am instrument --no-window-animation -w",
262
+ instrument_command = ["-s #{device_serial} shell am instrument --no-window-animation -w",
256
263
  "-e testLocale #{locale.tr('-', '_')}",
257
264
  "-e endingLocale #{@config[:ending_locale].tr('-', '_')}"]
265
+ instrument_command << "-e appendTimestamp #{@config[:use_timestamp_suffix]}"
258
266
  instrument_command << "-e class #{test_classes_to_use.join(',')}" if test_classes_to_use
259
267
  instrument_command << "-e package #{test_packages_to_use.join(',')}" if test_packages_to_use
260
268
  instrument_command << launch_arguments.map { |item| '-e ' + item }.join(' ') if launch_arguments
@@ -281,10 +289,11 @@ module Screengrab
281
289
 
282
290
  # Make a temp directory into which to pull the screenshots before they are moved to their final location.
283
291
  # This makes directory cleanup easier, as the temp directory will be removed when the block completes.
292
+
284
293
  Dir.mktmpdir do |tempdir|
285
294
  device_screenshots_paths.each do |device_path|
286
295
  if_device_path_exists(device_serial, device_path) do |path|
287
- run_adb_command("adb -s #{device_serial} pull #{path} #{tempdir}",
296
+ run_adb_command("-s #{device_serial} pull #{path} #{tempdir}",
288
297
  print_all: false,
289
298
  print_command: true)
290
299
  end
@@ -347,7 +356,7 @@ module Screengrab
347
356
  # Some device commands fail if executed against a device path that does not exist, so this helper method
348
357
  # provides a way to conditionally execute a block only if the provided path exists on the device.
349
358
  def if_device_path_exists(device_serial, device_path)
350
- return if run_adb_command("adb -s #{device_serial} shell ls #{device_path}",
359
+ return if run_adb_command("-s #{device_serial} shell ls #{device_path}",
351
360
  print_all: false,
352
361
  print_command: false).include?('No such file')
353
362
 
@@ -359,7 +368,7 @@ module Screengrab
359
368
 
360
369
  # Return an array of packages that are installed on the device
361
370
  def installed_packages(device_serial)
362
- packages = run_adb_command("adb -s #{device_serial} shell pm list packages",
371
+ packages = run_adb_command("-s #{device_serial} shell pm list packages",
363
372
  print_all: true,
364
373
  print_command: true)
365
374
  packages.split("\n").map { |package| package.gsub("package:", "") }
@@ -367,7 +376,9 @@ module Screengrab
367
376
 
368
377
  def run_adb_command(command, print_all: false, print_command: false)
369
378
  adb_path = @android_env.adb_path.chomp("adb")
370
- output = @executor.execute(command: adb_path + command,
379
+ adb_host = @config[:adb_host]
380
+ host = adb_host.nil? ? '' : "-H #{adb_host} "
381
+ output = @executor.execute(command: adb_path + "adb " + host + command,
371
382
  print_all: print_all,
372
383
  print_command: print_command) || ''
373
384
  output.lines.reject do |line|
@@ -375,5 +386,34 @@ module Screengrab
375
386
  line.start_with?('adb: ')
376
387
  end.join('') # Lines retain their newline chars
377
388
  end
389
+
390
+ def device_api_version(device_serial)
391
+ run_adb_command("-s #{device_serial} shell getprop ro.build.version.sdk",
392
+ print_all: true, print_command: true).to_i
393
+ end
394
+
395
+ def enable_clean_status_bar(device_serial, app_apk_path)
396
+ return unless device_api_version(device_serial) >= 23
397
+
398
+ # Check if the app wants to use the clean status bar feature
399
+ badging_dump = @executor.execute(command: "#{@android_env.aapt_path} dump badging #{app_apk_path}",
400
+ print_all: true, print_command: true)
401
+ return unless badging_dump.include?('uses-feature: name=\'tools.fastlane.screengrab.cleanstatusbar\'')
402
+
403
+ UI.message('Enabling clean status bar')
404
+
405
+ # Make sure the app requests the DUMP permission
406
+ unless badging_dump.include?('uses-permission: name=\'android.permission.DUMP\'')
407
+ UI.user_error!("The clean status bar feature requires the android.permission.DUMP permission but it could not be found in your app APK")
408
+ end
409
+
410
+ # Grant the DUMP permission
411
+ run_adb_command("-s #{device_serial} shell pm grant #{@config[:app_package_name]} android.permission.DUMP",
412
+ print_all: true, print_command: true)
413
+
414
+ # Enable the SystemUI demo mode
415
+ run_adb_command("-s #{device_serial} shell settings put global sysui_demo_allowed 1",
416
+ print_all: true, print_command: true)
417
+ end
378
418
  end
379
419
  end