fastlane 2.178.0 → 2.182.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/LICENSE +1 -1
- data/README.md +97 -84
- data/cert/lib/cert/commands_generator.rb +2 -1
- data/cert/lib/cert/options.rb +1 -0
- data/cert/lib/cert/runner.rb +4 -0
- data/deliver/lib/deliver/commands_generator.rb +2 -1
- data/deliver/lib/deliver/download_screenshots.rb +1 -2
- data/deliver/lib/deliver/languages.rb +1 -1
- data/deliver/lib/deliver/options.rb +3 -2
- data/deliver/lib/deliver/runner.rb +4 -0
- data/deliver/lib/deliver/setup.rb +0 -1
- data/deliver/lib/deliver/upload_metadata.rb +2 -1
- data/fastlane/lib/fastlane/actions/actions_helper.rb +2 -2
- data/fastlane/lib/fastlane/actions/app_store_build_number.rb +5 -0
- data/fastlane/lib/fastlane/actions/app_store_connect_api_key.rb +3 -3
- data/fastlane/lib/fastlane/actions/backup_xcarchive.rb +1 -1
- data/fastlane/lib/fastlane/actions/build_app.rb +4 -0
- data/fastlane/lib/fastlane/actions/check_app_store_metadata.rb +4 -0
- data/fastlane/lib/fastlane/actions/clipboard.rb +3 -6
- data/fastlane/lib/fastlane/actions/docs/capture_ios_screenshots.md +1 -1
- data/fastlane/lib/fastlane/actions/docs/frame_screenshots.md +18 -1
- data/fastlane/lib/fastlane/actions/docs/upload_to_play_store.md +2 -1
- data/fastlane/lib/fastlane/actions/ensure_env_vars.rb +2 -6
- data/fastlane/lib/fastlane/actions/get_provisioning_profile.rb +4 -0
- data/fastlane/lib/fastlane/actions/get_version_number.rb +17 -10
- data/fastlane/lib/fastlane/actions/git_branch.rb +4 -10
- data/fastlane/lib/fastlane/actions/git_commit.rb +3 -1
- data/fastlane/lib/fastlane/actions/git_submodule_update.rb +16 -8
- data/fastlane/lib/fastlane/actions/git_tag_exists.rb +4 -0
- data/fastlane/lib/fastlane/actions/import_from_git.rb +5 -5
- data/fastlane/lib/fastlane/actions/install_provisioning_profile.rb +4 -0
- data/fastlane/lib/fastlane/actions/jira.rb +61 -14
- data/fastlane/lib/fastlane/actions/latest_testflight_build_number.rb +1 -0
- data/fastlane/lib/fastlane/actions/match_nuke.rb +59 -0
- data/fastlane/lib/fastlane/actions/notarize.rb +98 -51
- data/fastlane/lib/fastlane/actions/slack.rb +155 -133
- data/fastlane/lib/fastlane/actions/sourcedocs.rb +164 -0
- data/fastlane/lib/fastlane/actions/spaceship_logs.rb +1 -1
- data/fastlane/lib/fastlane/actions/update_project_provisioning.rb +1 -2
- data/fastlane/lib/fastlane/actions/upload_symbols_to_crashlytics.rb +4 -2
- data/fastlane/lib/fastlane/cli_tools_distributor.rb +1 -1
- data/fastlane/lib/fastlane/commands_generator.rb +2 -1
- data/fastlane/lib/fastlane/fast_file.rb +10 -2
- data/fastlane/lib/fastlane/fastlane_require.rb +7 -1
- data/fastlane/lib/fastlane/helper/git_helper.rb +19 -7
- data/fastlane/lib/fastlane/lane_manager.rb +3 -2
- data/fastlane/lib/fastlane/notification/slack.rb +56 -0
- data/fastlane/lib/fastlane/plugins/plugin_fetcher.rb +1 -2
- data/fastlane/lib/fastlane/plugins/plugin_info.rb +2 -2
- data/fastlane/lib/fastlane/plugins/plugin_info_collector.rb +1 -2
- data/fastlane/lib/fastlane/plugins/plugin_manager.rb +1 -2
- data/fastlane/lib/fastlane/plugins/template/%gem_name%.gemspec.erb +7 -6
- data/fastlane/lib/fastlane/plugins/template/.rubocop.yml +30 -35
- data/fastlane/lib/fastlane/plugins/template/spec/spec_helper.rb.erb +1 -1
- data/fastlane/lib/fastlane/setup/setup.rb +23 -10
- data/fastlane/lib/fastlane/swift_fastlane_function.rb +39 -14
- data/fastlane/lib/fastlane/swift_runner_upgrader.rb +2 -0
- data/fastlane/lib/fastlane/version.rb +2 -2
- data/fastlane/swift/Deliverfile.swift +1 -1
- data/fastlane/swift/DeliverfileProtocol.swift +3 -3
- data/fastlane/swift/Fastlane.swift +6852 -3824
- data/fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/project.pbxproj +4 -0
- data/fastlane/swift/Gymfile.swift +1 -1
- data/fastlane/swift/GymfileProtocol.swift +1 -1
- data/fastlane/swift/LaneFileProtocol.swift +9 -3
- data/fastlane/swift/Matchfile.swift +1 -1
- data/fastlane/swift/MatchfileProtocol.swift +1 -1
- data/fastlane/swift/OptionalConfigValue.swift +131 -0
- data/fastlane/swift/Precheckfile.swift +1 -1
- data/fastlane/swift/PrecheckfileProtocol.swift +3 -3
- data/fastlane/swift/RubyCommand.swift +1 -1
- data/fastlane/swift/Scanfile.swift +1 -1
- data/fastlane/swift/ScanfileProtocol.swift +5 -1
- data/fastlane/swift/Screengrabfile.swift +1 -1
- data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
- data/fastlane/swift/Snapshotfile.swift +1 -1
- data/fastlane/swift/SnapshotfileProtocol.swift +5 -1
- data/fastlane/swift/SocketClient.swift +2 -1
- data/fastlane/swift/SocketResponse.swift +4 -2
- data/fastlane/swift/formatting/Brewfile.lock.json +18 -16
- data/fastlane/swift/upgrade_manifest.json +1 -1
- data/fastlane_core/lib/fastlane_core.rb +22 -21
- data/fastlane_core/lib/fastlane_core/build_watcher.rb +50 -9
- data/fastlane_core/lib/fastlane_core/clipboard.rb +20 -0
- data/fastlane_core/lib/fastlane_core/configuration/configuration.rb +5 -3
- data/fastlane_core/lib/fastlane_core/helper.rb +28 -5
- data/fastlane_core/lib/fastlane_core/languages.rb +2 -2
- data/fastlane_core/lib/fastlane_core/queue_worker.rb +2 -2
- data/fastlane_core/lib/fastlane_core/swag.rb +1 -1
- data/fastlane_core/lib/fastlane_core/ui/help.erb +35 -0
- data/fastlane_core/lib/fastlane_core/ui/help_formatter.rb +16 -0
- data/fastlane_core/lib/fastlane_core/ui/implementations/shell.rb +12 -1
- data/frameit/lib/frameit/commands_generator.rb +2 -1
- data/gym/lib/gym/commands_generator.rb +2 -1
- data/gym/lib/gym/generators/package_command_generator.rb +4 -0
- data/gym/lib/gym/generators/package_command_generator_xcode7.rb +13 -8
- data/gym/lib/gym/runner.rb +15 -4
- data/match/lib/match/change_password.rb +3 -3
- data/match/lib/match/commands_generator.rb +2 -1
- data/match/lib/match/encryption/interface.rb +1 -1
- data/match/lib/match/encryption/openssl.rb +2 -2
- data/match/lib/match/module.rb +1 -0
- data/pem/lib/pem/commands_generator.rb +2 -1
- data/pilot/lib/pilot/commands_generator.rb +2 -1
- data/pilot/lib/pilot/manager.rb +4 -0
- data/pilot/lib/pilot/options.rb +3 -2
- data/pilot/lib/pilot/tester_exporter.rb +0 -1
- data/pilot/lib/pilot/tester_manager.rb +0 -1
- data/precheck/lib/precheck/commands_generator.rb +2 -1
- data/precheck/lib/precheck/options.rb +1 -0
- data/precheck/lib/precheck/runner.rb +4 -0
- data/produce/lib/produce/commands_generator.rb +2 -1
- data/scan/lib/scan/commands_generator.rb +2 -1
- data/scan/lib/scan/options.rb +10 -5
- data/scan/lib/scan/runner.rb +54 -1
- data/scan/lib/scan/test_command_generator.rb +10 -8
- data/screengrab/lib/screengrab/android_environment.rb +6 -4
- data/screengrab/lib/screengrab/commands_generator.rb +2 -1
- data/screengrab/lib/screengrab/runner.rb +1 -1
- data/sigh/lib/sigh/commands_generator.rb +2 -1
- data/sigh/lib/sigh/options.rb +1 -0
- data/sigh/lib/sigh/runner.rb +4 -0
- data/snapshot/lib/assets/SnapfileTemplate +1 -1
- data/snapshot/lib/assets/SnapshotHelper.swift +1 -1
- data/snapshot/lib/snapshot/commands_generator.rb +3 -1
- data/snapshot/lib/snapshot/options.rb +5 -0
- data/snapshot/lib/snapshot/reports_generator.rb +4 -0
- data/snapshot/lib/snapshot/simulator_launchers/launcher_configuration.rb +2 -0
- data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher.rb +1 -1
- data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_base.rb +8 -4
- data/spaceship/README.md +2 -12
- data/spaceship/lib/spaceship/base.rb +2 -2
- data/spaceship/lib/spaceship/commands_generator.rb +4 -2
- data/spaceship/lib/spaceship/connect_api/models/app_screenshot.rb +1 -1
- data/spaceship/lib/spaceship/connect_api/models/profile.rb +6 -0
- data/spaceship/lib/spaceship/connect_api/provisioning/provisioning.rb +6 -2
- data/spaceship/lib/spaceship/connect_api/token.rb +7 -1
- data/spaceship/lib/spaceship/spaceauth_runner.rb +19 -9
- data/spaceship/lib/spaceship/tunes/members.rb +1 -1
- data/spaceship/lib/spaceship/ui.rb +2 -2
- data/supply/lib/supply/client.rb +3 -1
- data/supply/lib/supply/commands_generator.rb +2 -1
- data/supply/lib/supply/options.rb +2 -2
- data/supply/lib/supply/uploader.rb +1 -0
- metadata +53 -64
- data/gym/lib/gym/.runner.rb.swp +0 -0
- data/pilot/lib/pilot/tester_util.rb +0 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
module Fastlane
|
2
|
+
module Actions
|
3
|
+
class MatchNukeAction < Action
|
4
|
+
def self.run(params)
|
5
|
+
require 'match'
|
6
|
+
|
7
|
+
params.load_configuration_file("Matchfile")
|
8
|
+
params[:api_key] ||= Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY]
|
9
|
+
|
10
|
+
cert_type = Match.cert_type_sym(params[:type])
|
11
|
+
UI.important("Going to revoke your '#{cert_type}' certificate type and provisioning profiles")
|
12
|
+
|
13
|
+
Match::Nuke.new.run(params, type: cert_type)
|
14
|
+
end
|
15
|
+
|
16
|
+
#####################################################
|
17
|
+
# @!group Documentation
|
18
|
+
#####################################################
|
19
|
+
|
20
|
+
def self.description
|
21
|
+
"Easily nuke your certificate and provisioning profiles (via _match_)"
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.details
|
25
|
+
[
|
26
|
+
"Use the match_nuke action to revoke your certificates and provisioning profiles.",
|
27
|
+
"Don't worry, apps that are already available in the App Store / TestFlight will still work.",
|
28
|
+
"Builds distributed via Ad Hoc or Enterprise will be disabled after nuking your account, so you'll have to re-upload a new build.",
|
29
|
+
"After clearing your account you'll start from a clean state, and you can run match to generate your certificates and profiles again.",
|
30
|
+
"More information: https://docs.fastlane.tools/actions/match/"
|
31
|
+
].join("\n")
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.available_options
|
35
|
+
require 'match'
|
36
|
+
Match::Options.available_options
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.is_supported?(platform)
|
40
|
+
[:ios, :mac].include?(platform)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.example_code
|
44
|
+
[
|
45
|
+
'match_nuke(type: "development")', # See all other options https://github.com/fastlane/fastlane/blob/master/match/lib/match/module.rb#L23
|
46
|
+
'match_nuke(type: "development", api_key: app_store_connect_api_key)'
|
47
|
+
]
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.authors
|
51
|
+
["crazymanish"]
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.category
|
55
|
+
:code_signing
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -1,12 +1,14 @@
|
|
1
1
|
module Fastlane
|
2
2
|
module Actions
|
3
3
|
class NotarizeAction < Action
|
4
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
4
5
|
def self.run(params)
|
5
6
|
package_path = params[:package]
|
6
7
|
bundle_id = params[:bundle_id]
|
7
8
|
try_early_stapling = params[:try_early_stapling]
|
8
9
|
print_log = params[:print_log]
|
9
10
|
verbose = params[:verbose]
|
11
|
+
api_key_path = params[:api_key_path]
|
10
12
|
|
11
13
|
# Compress and read bundle identifier only for .app bundle.
|
12
14
|
compressed_package_path = nil
|
@@ -28,68 +30,73 @@ module Fastlane
|
|
28
30
|
|
29
31
|
UI.user_error!('Could not read bundle identifier, provide as a parameter') unless bundle_id
|
30
32
|
|
31
|
-
apple_id_account = CredentialsManager::AccountManager.new(user: params[:username])
|
32
|
-
|
33
|
-
# Add password as a temporary environment variable for altool.
|
34
|
-
# Use app specific password if specified.
|
35
|
-
ENV['FL_NOTARIZE_PASSWORD'] = ENV['FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD'] || apple_id_account.password
|
36
|
-
|
37
33
|
UI.message('Uploading package to notarization service, might take a while')
|
38
34
|
|
39
|
-
notarization_upload_command = "xcrun altool --notarize-app -t osx -f \"#{compressed_package_path || package_path}\" --primary-bundle-id #{bundle_id}
|
40
|
-
notarization_upload_command << " --asc-provider \"#{params[:asc_provider]}\"" if params[:asc_provider]
|
35
|
+
notarization_upload_command = "xcrun altool --notarize-app -t osx -f \"#{compressed_package_path || package_path}\" --primary-bundle-id #{bundle_id} --output-format xml"
|
41
36
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
)
|
37
|
+
notarization_info = {}
|
38
|
+
with_notarize_authenticator(params, api_key_path) do |notarize_authenticator|
|
39
|
+
notarization_upload_command << " --asc-provider \"#{params[:asc_provider]}\"" if params[:asc_provider] && api_key_path.nil?
|
46
40
|
|
47
|
-
|
41
|
+
notarization_upload_response = Actions.sh(
|
42
|
+
notarize_authenticator.call(notarization_upload_command),
|
43
|
+
log: verbose
|
44
|
+
)
|
48
45
|
|
49
|
-
|
50
|
-
notarization_request_id = notarization_upload_plist['notarization-upload']['RequestUUID']
|
46
|
+
FileUtils.rm_rf(compressed_package_path) if compressed_package_path
|
51
47
|
|
52
|
-
|
48
|
+
notarization_upload_plist = Plist.parse_xml(notarization_upload_response)
|
53
49
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
UI.
|
58
|
-
elsif try_early_stapling
|
59
|
-
UI.message('Request in progress, trying early staple')
|
60
|
-
|
61
|
-
begin
|
62
|
-
self.staple(package_path, verbose)
|
63
|
-
UI.message('Successfully notarized and early stapled package.')
|
64
|
-
|
65
|
-
return
|
66
|
-
rescue
|
67
|
-
UI.message('Early staple failed, waiting to query again')
|
68
|
-
end
|
50
|
+
if notarization_upload_plist.key?('product-errors') && notarization_upload_plist['product-errors'].any?
|
51
|
+
UI.important("🚫 Could not upload package to notarization service! Here are the reasons:")
|
52
|
+
notarization_upload_plist['product-errors'].each { |product_error| UI.error("#{product_error['message']} (#{product_error['code']})") }
|
53
|
+
UI.user_error!("Package upload to notarization service cancelled. Please check the error messages above.")
|
69
54
|
end
|
70
55
|
|
71
|
-
|
72
|
-
|
73
|
-
UI.
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
56
|
+
notarization_request_id = notarization_upload_plist['notarization-upload']['RequestUUID']
|
57
|
+
|
58
|
+
UI.success("Successfully uploaded package to notarization service with request identifier #{notarization_request_id}")
|
59
|
+
|
60
|
+
while notarization_info.empty? || (notarization_info['Status'] == 'in progress')
|
61
|
+
if notarization_info.empty?
|
62
|
+
UI.message('Waiting to query request status')
|
63
|
+
elsif try_early_stapling
|
64
|
+
UI.message('Request in progress, trying early staple')
|
65
|
+
|
66
|
+
begin
|
67
|
+
self.staple(package_path, verbose)
|
68
|
+
UI.message('Successfully notarized and early stapled package.')
|
69
|
+
|
70
|
+
return
|
71
|
+
rescue
|
72
|
+
UI.message('Early staple failed, waiting to query again')
|
73
|
+
end
|
74
|
+
end
|
87
75
|
|
88
|
-
|
89
|
-
|
90
|
-
|
76
|
+
sleep(30)
|
77
|
+
|
78
|
+
UI.message('Querying request status')
|
79
|
+
|
80
|
+
# As of July 2020, the request UUID might not be available for polling yet which returns an error code
|
81
|
+
# This is now handled with the error_callback (which prevents an error from being raised)
|
82
|
+
# Catching this error allows for polling to continue until the notarization is complete
|
83
|
+
error = false
|
84
|
+
notarization_info_response = Actions.sh(
|
85
|
+
notarize_authenticator.call("xcrun altool --notarization-info #{notarization_request_id} --output-format xml"),
|
86
|
+
log: verbose,
|
87
|
+
error_callback: lambda { |msg|
|
88
|
+
error = true
|
89
|
+
UI.error("Error polling for notarization info: #{msg}")
|
90
|
+
}
|
91
|
+
)
|
92
|
+
|
93
|
+
unless error
|
94
|
+
notarization_info_plist = Plist.parse_xml(notarization_info_response)
|
95
|
+
notarization_info = notarization_info_plist['notarization-info']
|
96
|
+
end
|
91
97
|
end
|
92
98
|
end
|
99
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
93
100
|
|
94
101
|
log_url = notarization_info['LogFileURL']
|
95
102
|
ENV['FL_NOTARIZE_LOG_FILE_URL'] = log_url
|
@@ -123,6 +130,35 @@ module Fastlane
|
|
123
130
|
)
|
124
131
|
end
|
125
132
|
|
133
|
+
def self.with_notarize_authenticator(params, api_key_path)
|
134
|
+
if api_key_path
|
135
|
+
# From xcrun altool for --apiKey:
|
136
|
+
# This option will search the following directories in sequence for a private key file with the name of 'AuthKey_<api_key>.p8': './private_keys', '~/private_keys', '~/.private_keys', and '~/.appstoreconnect/private_keys'.
|
137
|
+
api_key = Spaceship::ConnectAPI::Token.from_json_file(api_key_path)
|
138
|
+
api_key_folder_path = File.expand_path('~/.appstoreconnect/private_keys')
|
139
|
+
api_key_file_path = File.join(api_key_folder_path, "AuthKey_#{api_key.key_id}.p8")
|
140
|
+
directory_exists = File.directory?(api_key_folder_path)
|
141
|
+
file_exists = File.exist?(api_key_file_path)
|
142
|
+
begin
|
143
|
+
FileUtils.mkdir_p(api_key_folder_path) unless directory_exists
|
144
|
+
api_key.write_key_to_file(api_key_file_path) unless file_exists
|
145
|
+
|
146
|
+
yield(proc { |command| "#{command} --apiKey #{api_key.key_id} --apiIssuer #{api_key.issuer_id}" })
|
147
|
+
ensure
|
148
|
+
FileUtils.rm(api_key_file_path) unless file_exists
|
149
|
+
FileUtils.rm_r(api_key_folder_path) unless directory_exists
|
150
|
+
end
|
151
|
+
else
|
152
|
+
apple_id_account = CredentialsManager::AccountManager.new(user: params[:username])
|
153
|
+
|
154
|
+
# Add password as a temporary environment variable for altool.
|
155
|
+
# Use app specific password if specified.
|
156
|
+
ENV['FL_NOTARIZE_PASSWORD'] = ENV['FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD'] || apple_id_account.password
|
157
|
+
|
158
|
+
yield(proc { |command| "#{command} -u #{apple_id_account.user} -p @env:FL_NOTARIZE_PASSWORD" })
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
126
162
|
def self.description
|
127
163
|
'Notarizes a macOS app'
|
128
164
|
end
|
@@ -160,6 +196,8 @@ module Fastlane
|
|
160
196
|
env_name: 'FL_NOTARIZE_USERNAME',
|
161
197
|
description: 'Apple ID username',
|
162
198
|
default_value: username,
|
199
|
+
optional: true,
|
200
|
+
conflicting_options: [:api_key_path],
|
163
201
|
default_value_dynamic: true),
|
164
202
|
FastlaneCore::ConfigItem.new(key: :asc_provider,
|
165
203
|
env_name: 'FL_NOTARIZE_ASC_PROVIDER',
|
@@ -177,7 +215,16 @@ module Fastlane
|
|
177
215
|
description: 'Whether to log requests',
|
178
216
|
optional: true,
|
179
217
|
default_value: false,
|
180
|
-
type: Boolean)
|
218
|
+
type: Boolean),
|
219
|
+
FastlaneCore::ConfigItem.new(key: :api_key_path,
|
220
|
+
env_name: 'FL_NOTARIZE_API_KEY_PATH',
|
221
|
+
description: 'Path to AppStore Connect API key',
|
222
|
+
optional: true,
|
223
|
+
conflicting_options: [:username],
|
224
|
+
is_string: true,
|
225
|
+
verify_block: proc do |value|
|
226
|
+
UI.user_error!("API Key not found at '#{value}'") unless File.exist?(value)
|
227
|
+
end)
|
181
228
|
]
|
182
229
|
end
|
183
230
|
|
@@ -1,70 +1,178 @@
|
|
1
|
+
require 'fastlane/notification/slack'
|
2
|
+
|
1
3
|
# rubocop:disable Style/CaseEquality
|
2
4
|
# rubocop:disable Style/MultilineTernaryOperator
|
3
5
|
# rubocop:disable Style/NestedTernaryOperator
|
4
6
|
module Fastlane
|
5
7
|
module Actions
|
6
8
|
class SlackAction < Action
|
7
|
-
|
8
|
-
|
9
|
-
|
9
|
+
class Runner
|
10
|
+
def initialize(slack_url)
|
11
|
+
@notifier = Fastlane::Notification::Slack.new(slack_url)
|
12
|
+
end
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def self.trim_message(message)
|
15
|
-
# We want the last 7000 characters, instead of the first 7000, as the error is at the bottom
|
16
|
-
start_index = [message.length - 7000, 0].max
|
17
|
-
message = message[start_index..-1]
|
18
|
-
# We want line breaks to be shown on slack output so we replace
|
19
|
-
# input non-interpreted line break with interpreted line break
|
20
|
-
message.gsub('\n', "\n")
|
21
|
-
end
|
14
|
+
def run(options)
|
15
|
+
options[:message] = self.class.trim_message(options[:message].to_s || '')
|
16
|
+
options[:message] = Fastlane::Notification::Slack::LinkConverter.convert(options[:message])
|
22
17
|
|
23
|
-
|
24
|
-
require 'slack-notifier'
|
18
|
+
options[:pretext] = options[:pretext].gsub('\n', "\n") unless options[:pretext].nil?
|
25
19
|
|
26
|
-
|
27
|
-
|
20
|
+
if options[:channel].to_s.length > 0
|
21
|
+
channel = options[:channel]
|
22
|
+
channel = ('#' + options[:channel]) unless ['#', '@'].include?(channel[0]) # send message to channel by default
|
23
|
+
end
|
28
24
|
|
29
|
-
|
25
|
+
username = options[:use_webhook_configured_username_and_icon] ? nil : options[:username]
|
30
26
|
|
31
|
-
|
32
|
-
|
33
|
-
|
27
|
+
slack_attachment = self.class.generate_slack_attachments(options)
|
28
|
+
link_names = options[:link_names]
|
29
|
+
icon_url = options[:use_webhook_configured_username_and_icon] ? nil : options[:icon_url]
|
30
|
+
|
31
|
+
post_message(
|
32
|
+
channel: channel,
|
33
|
+
username: username,
|
34
|
+
attachments: [slack_attachment],
|
35
|
+
link_names: link_names,
|
36
|
+
icon_url: icon_url,
|
37
|
+
fail_on_error: options[:fail_on_error]
|
38
|
+
)
|
34
39
|
end
|
35
40
|
|
36
|
-
|
41
|
+
def post_message(channel:, username:, attachments:, link_names:, icon_url:, fail_on_error:)
|
42
|
+
@notifier.post_to_legacy_incoming_webhook(
|
43
|
+
channel: channel,
|
44
|
+
username: username,
|
45
|
+
link_names: link_names,
|
46
|
+
icon_url: icon_url,
|
47
|
+
attachments: attachments
|
48
|
+
)
|
49
|
+
UI.success('Successfully sent Slack notification')
|
50
|
+
rescue => error
|
51
|
+
UI.error("Exception: #{error}")
|
52
|
+
message = "Error pushing Slack message, maybe the integration has no permission to post on this channel? Try removing the channel parameter in your Fastfile, this is usually caused by a misspelled or changed group/channel name or an expired SLACK_URL"
|
53
|
+
if fail_on_error
|
54
|
+
UI.user_error!(message)
|
55
|
+
else
|
56
|
+
UI.error(message)
|
57
|
+
end
|
58
|
+
end
|
37
59
|
|
38
|
-
|
60
|
+
# As there is a text limit in the notifications, we are
|
61
|
+
# usually interested in the last part of the message
|
62
|
+
# e.g. for tests
|
63
|
+
def self.trim_message(message)
|
64
|
+
# We want the last 7000 characters, instead of the first 7000, as the error is at the bottom
|
65
|
+
start_index = [message.length - 7000, 0].max
|
66
|
+
message = message[start_index..-1]
|
67
|
+
# We want line breaks to be shown on slack output so we replace
|
68
|
+
# input non-interpreted line break with interpreted line break
|
69
|
+
message.gsub('\n', "\n")
|
70
|
+
end
|
39
71
|
|
40
|
-
|
72
|
+
def self.generate_slack_attachments(options)
|
73
|
+
color = (options[:success] ? 'good' : 'danger')
|
74
|
+
should_add_payload = ->(payload_name) { options[:default_payloads].map(&:to_sym).include?(payload_name.to_sym) }
|
41
75
|
|
42
|
-
|
76
|
+
slack_attachment = {
|
77
|
+
fallback: options[:message],
|
78
|
+
text: options[:message],
|
79
|
+
pretext: options[:pretext],
|
80
|
+
color: color,
|
81
|
+
mrkdwn_in: ["pretext", "text", "fields", "message"],
|
82
|
+
fields: []
|
83
|
+
}
|
43
84
|
|
44
|
-
|
85
|
+
# custom user payloads
|
86
|
+
slack_attachment[:fields] += options[:payload].map do |k, v|
|
87
|
+
{
|
88
|
+
title: k.to_s,
|
89
|
+
value: Fastlane::Notification::Slack::LinkConverter.convert(v.to_s),
|
90
|
+
short: false
|
91
|
+
}
|
92
|
+
end
|
45
93
|
|
46
|
-
|
94
|
+
# Add the lane to the Slack message
|
95
|
+
# This might be nil, if slack is called as "one-off" action
|
96
|
+
if should_add_payload[:lane] && Actions.lane_context[Actions::SharedValues::LANE_NAME]
|
97
|
+
slack_attachment[:fields] << {
|
98
|
+
title: 'Lane',
|
99
|
+
value: Actions.lane_context[Actions::SharedValues::LANE_NAME],
|
100
|
+
short: true
|
101
|
+
}
|
102
|
+
end
|
47
103
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
104
|
+
# test_result
|
105
|
+
if should_add_payload[:test_result]
|
106
|
+
slack_attachment[:fields] << {
|
107
|
+
title: 'Result',
|
108
|
+
value: (options[:success] ? 'Success' : 'Error'),
|
109
|
+
short: true
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
# git branch
|
114
|
+
if Actions.git_branch && should_add_payload[:git_branch]
|
115
|
+
slack_attachment[:fields] << {
|
116
|
+
title: 'Git Branch',
|
117
|
+
value: Actions.git_branch,
|
118
|
+
short: true
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
# git_author
|
123
|
+
if Actions.git_author_email && should_add_payload[:git_author]
|
124
|
+
if FastlaneCore::Env.truthy?('FASTLANE_SLACK_HIDE_AUTHOR_ON_SUCCESS') && options[:success]
|
125
|
+
# We only show the git author if the build failed
|
61
126
|
else
|
62
|
-
|
127
|
+
slack_attachment[:fields] << {
|
128
|
+
title: 'Git Author',
|
129
|
+
value: Actions.git_author_email,
|
130
|
+
short: true
|
131
|
+
}
|
63
132
|
end
|
64
133
|
end
|
134
|
+
|
135
|
+
# last_git_commit
|
136
|
+
if Actions.last_git_commit_message && should_add_payload[:last_git_commit]
|
137
|
+
slack_attachment[:fields] << {
|
138
|
+
title: 'Git Commit',
|
139
|
+
value: Actions.last_git_commit_message,
|
140
|
+
short: false
|
141
|
+
}
|
142
|
+
end
|
143
|
+
|
144
|
+
# last_git_commit_hash
|
145
|
+
if Actions.last_git_commit_hash(true) && should_add_payload[:last_git_commit_hash]
|
146
|
+
slack_attachment[:fields] << {
|
147
|
+
title: 'Git Commit Hash',
|
148
|
+
value: Actions.last_git_commit_hash(short: true),
|
149
|
+
short: false
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
# merge additional properties
|
154
|
+
deep_merge(slack_attachment, options[:attachment_properties])
|
155
|
+
end
|
156
|
+
|
157
|
+
# Adapted from https://stackoverflow.com/a/30225093/158525
|
158
|
+
def self.deep_merge(a, b)
|
159
|
+
merger = proc do |key, v1, v2|
|
160
|
+
Hash === v1 && Hash === v2 ?
|
161
|
+
v1.merge(v2, &merger) : Array === v1 && Array === v2 ?
|
162
|
+
v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2
|
163
|
+
end
|
164
|
+
a.merge(b, &merger)
|
65
165
|
end
|
66
166
|
end
|
67
167
|
|
168
|
+
def self.is_supported?(platform)
|
169
|
+
true
|
170
|
+
end
|
171
|
+
|
172
|
+
def self.run(options)
|
173
|
+
Runner.new(options[:slack_url]).run(options)
|
174
|
+
end
|
175
|
+
|
68
176
|
def self.description
|
69
177
|
"Send a success/error message to your [Slack](https://slack.com) group"
|
70
178
|
end
|
@@ -185,99 +293,13 @@ module Fastlane
|
|
185
293
|
# @!group Helper
|
186
294
|
#####################################################
|
187
295
|
|
188
|
-
def self.
|
189
|
-
|
190
|
-
should_add_payload = ->(payload_name) { options[:default_payloads].map(&:to_sym).include?(payload_name.to_sym) }
|
191
|
-
|
192
|
-
slack_attachment = {
|
193
|
-
fallback: options[:message],
|
194
|
-
text: options[:message],
|
195
|
-
pretext: options[:pretext],
|
196
|
-
color: color,
|
197
|
-
mrkdwn_in: ["pretext", "text", "fields", "message"],
|
198
|
-
fields: []
|
199
|
-
}
|
200
|
-
|
201
|
-
# custom user payloads
|
202
|
-
slack_attachment[:fields] += options[:payload].map do |k, v|
|
203
|
-
{
|
204
|
-
title: k.to_s,
|
205
|
-
value: Slack::Notifier::Util::LinkFormatter.format(v.to_s),
|
206
|
-
short: false
|
207
|
-
}
|
208
|
-
end
|
209
|
-
|
210
|
-
# Add the lane to the Slack message
|
211
|
-
# This might be nil, if slack is called as "one-off" action
|
212
|
-
if should_add_payload[:lane] && Actions.lane_context[Actions::SharedValues::LANE_NAME]
|
213
|
-
slack_attachment[:fields] << {
|
214
|
-
title: 'Lane',
|
215
|
-
value: Actions.lane_context[Actions::SharedValues::LANE_NAME],
|
216
|
-
short: true
|
217
|
-
}
|
218
|
-
end
|
219
|
-
|
220
|
-
# test_result
|
221
|
-
if should_add_payload[:test_result]
|
222
|
-
slack_attachment[:fields] << {
|
223
|
-
title: 'Result',
|
224
|
-
value: (options[:success] ? 'Success' : 'Error'),
|
225
|
-
short: true
|
226
|
-
}
|
227
|
-
end
|
228
|
-
|
229
|
-
# git branch
|
230
|
-
if Actions.git_branch && should_add_payload[:git_branch]
|
231
|
-
slack_attachment[:fields] << {
|
232
|
-
title: 'Git Branch',
|
233
|
-
value: Actions.git_branch,
|
234
|
-
short: true
|
235
|
-
}
|
236
|
-
end
|
237
|
-
|
238
|
-
# git_author
|
239
|
-
if Actions.git_author_email && should_add_payload[:git_author]
|
240
|
-
if FastlaneCore::Env.truthy?('FASTLANE_SLACK_HIDE_AUTHOR_ON_SUCCESS') && options[:success]
|
241
|
-
# We only show the git author if the build failed
|
242
|
-
else
|
243
|
-
slack_attachment[:fields] << {
|
244
|
-
title: 'Git Author',
|
245
|
-
value: Actions.git_author_email,
|
246
|
-
short: true
|
247
|
-
}
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
# last_git_commit
|
252
|
-
if Actions.last_git_commit_message && should_add_payload[:last_git_commit]
|
253
|
-
slack_attachment[:fields] << {
|
254
|
-
title: 'Git Commit',
|
255
|
-
value: Actions.last_git_commit_message,
|
256
|
-
short: false
|
257
|
-
}
|
258
|
-
end
|
259
|
-
|
260
|
-
# last_git_commit_hash
|
261
|
-
if Actions.last_git_commit_hash(true) && should_add_payload[:last_git_commit_hash]
|
262
|
-
slack_attachment[:fields] << {
|
263
|
-
title: 'Git Commit Hash',
|
264
|
-
value: Actions.last_git_commit_hash(short: true),
|
265
|
-
short: false
|
266
|
-
}
|
267
|
-
end
|
268
|
-
|
269
|
-
# merge additional properties
|
270
|
-
deep_merge(slack_attachment, options[:attachment_properties])
|
296
|
+
def self.trim_message(message)
|
297
|
+
Runner.trim_message(message)
|
271
298
|
end
|
272
299
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
Hash === v1 && Hash === v2 ?
|
277
|
-
v1.merge(v2, &merger) : Array === v1 && Array === v2 ?
|
278
|
-
v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2
|
279
|
-
end
|
280
|
-
a.merge(b, &merger)
|
300
|
+
def self.generate_slack_attachments(options)
|
301
|
+
UI.deprecated('`Fastlane::Actions::Slack.generate_slack_attachments` is subject to be removed as Slack recommends migrating `attachments` to Block Kit. fastlane will also follow the same direction.')
|
302
|
+
Runner.generate_slack_attachments(options)
|
281
303
|
end
|
282
304
|
end
|
283
305
|
end
|