Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Ruby 3.0] improve specs for Slack action #18512

Merged
merged 5 commits into from Apr 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
275 changes: 148 additions & 127 deletions fastlane/lib/fastlane/actions/slack.rb
Expand Up @@ -4,49 +4,40 @@
module Fastlane
module Actions
class SlackAction < Action
def self.is_supported?(platform)
true
end

# As there is a text limit in the notifications, we are
# usually interested in the last part of the message
# e.g. for tests
def self.trim_message(message)
# We want the last 7000 characters, instead of the first 7000, as the error is at the bottom
start_index = [message.length - 7000, 0].max
message = message[start_index..-1]
# We want line breaks to be shown on slack output so we replace
# input non-interpreted line break with interpreted line break
message.gsub('\n', "\n")
end

def self.run(options)
require 'slack-notifier'

options[:message] = self.trim_message(options[:message].to_s || '')
options[:message] = Slack::Notifier::Util::LinkFormatter.format(options[:message])

options[:pretext] = options[:pretext].gsub('\n', "\n") unless options[:pretext].nil?

if options[:channel].to_s.length > 0
channel = options[:channel]
channel = ('#' + options[:channel]) unless ['#', '@'].include?(channel[0]) # send message to channel by default
class Runner
def initialize(slack_url)
@notifier = Slack::Notifier.new(slack_url)
end

username = options[:use_webhook_configured_username_and_icon] ? nil : options[:username]
def run(options)
options[:message] = self.class.trim_message(options[:message].to_s || '')
options[:message] = Slack::Notifier::Util::LinkFormatter.format(options[:message])

notifier = Slack::Notifier.new(options[:slack_url], channel: channel, username: username)
options[:pretext] = options[:pretext].gsub('\n', "\n") unless options[:pretext].nil?

link_names = options[:link_names]
if options[:channel].to_s.length > 0
channel = options[:channel]
channel = ('#' + options[:channel]) unless ['#', '@'].include?(channel[0]) # send message to channel by default
end

icon_url = options[:use_webhook_configured_username_and_icon] ? nil : options[:icon_url]
username = options[:use_webhook_configured_username_and_icon] ? nil : options[:username]

slack_attachment = generate_slack_attachments(options)
slack_attachment = self.class.generate_slack_attachments(options)
link_names = options[:link_names]
icon_url = options[:use_webhook_configured_username_and_icon] ? nil : options[:icon_url]

return [notifier, slack_attachment] if Helper.test? # tests will verify the slack attachments and other properties
post_message(
channel: channel,
username: username,
attachments: [slack_attachment],
link_names: link_names,
icon_url: icon_url,
fail_on_error: options[:fail_on_error]
)
end

begin
results = notifier.ping('', link_names: link_names, icon_url: icon_url, attachments: [slack_attachment])
def post_message(channel:, username:, attachments:, link_names:, icon_url:, fail_on_error:)
results = @notifier.ping('', channel: channel, username: username, link_names: link_names, icon_url: icon_url, attachments: attachments)
rescue => exception
UI.error("Exception: #{exception}")
ensure
Expand All @@ -56,13 +47,129 @@ def self.run(options)
else
UI.verbose(result) unless result.nil?
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"
if options[:fail_on_error]
if fail_on_error
UI.user_error!(message)
else
UI.error(message)
end
end
end

# As there is a text limit in the notifications, we are
# usually interested in the last part of the message
# e.g. for tests
def self.trim_message(message)
# We want the last 7000 characters, instead of the first 7000, as the error is at the bottom
start_index = [message.length - 7000, 0].max
message = message[start_index..-1]
# We want line breaks to be shown on slack output so we replace
# input non-interpreted line break with interpreted line break
message.gsub('\n', "\n")
end

def self.generate_slack_attachments(options)
color = (options[:success] ? 'good' : 'danger')
should_add_payload = ->(payload_name) { options[:default_payloads].map(&:to_sym).include?(payload_name.to_sym) }

slack_attachment = {
fallback: options[:message],
text: options[:message],
pretext: options[:pretext],
color: color,
mrkdwn_in: ["pretext", "text", "fields", "message"],
fields: []
}

# custom user payloads
slack_attachment[:fields] += options[:payload].map do |k, v|
{
title: k.to_s,
value: Slack::Notifier::Util::LinkFormatter.format(v.to_s),
short: false
}
end

# Add the lane to the Slack message
# This might be nil, if slack is called as "one-off" action
if should_add_payload[:lane] && Actions.lane_context[Actions::SharedValues::LANE_NAME]
slack_attachment[:fields] << {
title: 'Lane',
value: Actions.lane_context[Actions::SharedValues::LANE_NAME],
short: true
}
end

# test_result
if should_add_payload[:test_result]
slack_attachment[:fields] << {
title: 'Result',
value: (options[:success] ? 'Success' : 'Error'),
short: true
}
end

# git branch
if Actions.git_branch && should_add_payload[:git_branch]
slack_attachment[:fields] << {
title: 'Git Branch',
value: Actions.git_branch,
short: true
}
end

# git_author
if Actions.git_author_email && should_add_payload[:git_author]
if FastlaneCore::Env.truthy?('FASTLANE_SLACK_HIDE_AUTHOR_ON_SUCCESS') && options[:success]
# We only show the git author if the build failed
else
slack_attachment[:fields] << {
title: 'Git Author',
value: Actions.git_author_email,
short: true
}
end
end

# last_git_commit
if Actions.last_git_commit_message && should_add_payload[:last_git_commit]
slack_attachment[:fields] << {
title: 'Git Commit',
value: Actions.last_git_commit_message,
short: false
}
end

# last_git_commit_hash
if Actions.last_git_commit_hash(true) && should_add_payload[:last_git_commit_hash]
slack_attachment[:fields] << {
title: 'Git Commit Hash',
value: Actions.last_git_commit_hash(short: true),
short: false
}
end

# merge additional properties
deep_merge(slack_attachment, options[:attachment_properties])
end

# Adapted from https://stackoverflow.com/a/30225093/158525
def self.deep_merge(a, b)
merger = proc do |key, v1, v2|
Hash === v1 && Hash === v2 ?
v1.merge(v2, &merger) : Array === v1 && Array === v2 ?
v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2
end
a.merge(b, &merger)
end
end

def self.is_supported?(platform)
true
end

def self.run(options)
require 'slack-notifier'
Runner.new(options[:slack_url]).run(options)
end

def self.description
Expand Down Expand Up @@ -185,99 +292,13 @@ def self.details
# @!group Helper
#####################################################

def self.generate_slack_attachments(options)
color = (options[:success] ? 'good' : 'danger')
should_add_payload = ->(payload_name) { options[:default_payloads].map(&:to_sym).include?(payload_name.to_sym) }

slack_attachment = {
fallback: options[:message],
text: options[:message],
pretext: options[:pretext],
color: color,
mrkdwn_in: ["pretext", "text", "fields", "message"],
fields: []
}

# custom user payloads
slack_attachment[:fields] += options[:payload].map do |k, v|
{
title: k.to_s,
value: Slack::Notifier::Util::LinkFormatter.format(v.to_s),
short: false
}
end

# Add the lane to the Slack message
# This might be nil, if slack is called as "one-off" action
if should_add_payload[:lane] && Actions.lane_context[Actions::SharedValues::LANE_NAME]
slack_attachment[:fields] << {
title: 'Lane',
value: Actions.lane_context[Actions::SharedValues::LANE_NAME],
short: true
}
end

# test_result
if should_add_payload[:test_result]
slack_attachment[:fields] << {
title: 'Result',
value: (options[:success] ? 'Success' : 'Error'),
short: true
}
end

# git branch
if Actions.git_branch && should_add_payload[:git_branch]
slack_attachment[:fields] << {
title: 'Git Branch',
value: Actions.git_branch,
short: true
}
end

# git_author
if Actions.git_author_email && should_add_payload[:git_author]
if FastlaneCore::Env.truthy?('FASTLANE_SLACK_HIDE_AUTHOR_ON_SUCCESS') && options[:success]
# We only show the git author if the build failed
else
slack_attachment[:fields] << {
title: 'Git Author',
value: Actions.git_author_email,
short: true
}
end
end

# last_git_commit
if Actions.last_git_commit_message && should_add_payload[:last_git_commit]
slack_attachment[:fields] << {
title: 'Git Commit',
value: Actions.last_git_commit_message,
short: false
}
end

# last_git_commit_hash
if Actions.last_git_commit_hash(true) && should_add_payload[:last_git_commit_hash]
slack_attachment[:fields] << {
title: 'Git Commit Hash',
value: Actions.last_git_commit_hash(short: true),
short: false
}
end

# merge additional properties
deep_merge(slack_attachment, options[:attachment_properties])
def self.trim_message(message)
Runner.trim_message(message)
end

# Adapted from https://stackoverflow.com/a/30225093/158525
def self.deep_merge(a, b)
merger = proc do |key, v1, v2|
Hash === v1 && Hash === v2 ?
v1.merge(v2, &merger) : Array === v1 && Array === v2 ?
v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2
end
a.merge(b, &merger)
def self.generate_slack_attachments(options)
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.')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we don't support Block Kit yet (do we?), I'm concerned to be marking something as deprecated without offering an alternative to users yet 馃

Copy link
Contributor Author

@ainame ainame Apr 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about this?
#18512 (comment)

I guess UI.deprecated is not necessarily meant to provide alternatives and can be used to announce future removal 馃 I mean most users won't see this message. Only plugin users whose plugins refer to this method directly, like this plugin, will see it.

https://github.com/crazymanish/fastlane-plugin-slack_bot/blob/0ca42f4d8f6f6d7cdd02f54615210a6ae254eed9/lib/fastlane/plugin/slack_bot/actions/update_slack_message.rb#L16

Runner.generate_slack_attachments(options)
end
end
end
Expand Down