Skip to content

Commit

Permalink
[Ruby 3.0] replace slack-notifier gem with internal library (#18537)
Browse files Browse the repository at this point in the history
* Implement FastlaneCore::Slack::LinkConverter

* Implment Slack#post_to_legacy_incoming_webhook

* Rename FastlaneCore::Slack to Fastlane::Notification::Slack

* Fix comment

* Fix Fastlane::Notification::Slack

* Repalce slack-notifier with Fastlane::Notification::Slack

* Get rid of slack-notifier

* Fix HTML_PATTERN

* Add specs

* Fix format by rubocop

* Apply suggestions from code review

Co-authored-by: Roger Oba <rogerluan.oba@gmail.com>

Co-authored-by: Roger Oba <rogerluan.oba@gmail.com>
  • Loading branch information
ainame and rogerluan committed Apr 30, 2021
1 parent 39abc01 commit a45e8f3
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 20 deletions.
1 change: 0 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ PATH
rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.3)
simctl (~> 1.6.3)
slack-notifier (>= 2.0.0, < 3.0.0)
terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
tty-screen (>= 0.6.3, < 1.0.0)
Expand Down
1 change: 0 additions & 1 deletion fastlane.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ Gem::Specification.new do |spec|
# spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = Dir["*/lib"]

spec.add_dependency('slack-notifier', '>= 2.0.0', '< 3.0.0') # Slack notifications
spec.add_dependency('xcodeproj', '>= 1.13.0', '< 2.0.0') # Modify Xcode projects
spec.add_dependency('xcpretty', '~> 0.3.0') # prettify xcodebuild output
spec.add_dependency('terminal-notifier', '>= 2.0.0', '< 3.0.0') # macOS notifications
Expand Down
37 changes: 19 additions & 18 deletions fastlane/lib/fastlane/actions/slack.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require 'fastlane/notification/slack'

# rubocop:disable Style/CaseEquality
# rubocop:disable Style/MultilineTernaryOperator
# rubocop:disable Style/NestedTernaryOperator
Expand All @@ -6,12 +8,12 @@ module Actions
class SlackAction < Action
class Runner
def initialize(slack_url)
@notifier = Slack::Notifier.new(slack_url)
@notifier = Fastlane::Notification::Slack.new(slack_url)
end

def run(options)
options[:message] = self.class.trim_message(options[:message].to_s || '')
options[:message] = Slack::Notifier::Util::LinkFormatter.format(options[:message])
options[:message] = Fastlane::Notification::Slack::LinkConverter.convert(options[:message])

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

Expand All @@ -37,21 +39,21 @@ def run(options)
end

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
result = results.first if results
if !result.nil? && result.code.to_i == 200
UI.success('Successfully sent Slack notification')
@notifier.post_to_legacy_incoming_webhook(
channel: channel,
username: username,
link_names: link_names,
icon_url: icon_url,
attachments: attachments
)
UI.success('Successfully sent Slack notification')
rescue => error
UI.error("Exception: #{error}")
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 fail_on_error
UI.user_error!(message)
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 fail_on_error
UI.user_error!(message)
else
UI.error(message)
end
UI.error(message)
end
end

Expand Down Expand Up @@ -84,7 +86,7 @@ def self.generate_slack_attachments(options)
slack_attachment[:fields] += options[:payload].map do |k, v|
{
title: k.to_s,
value: Slack::Notifier::Util::LinkFormatter.format(v.to_s),
value: Fastlane::Notification::Slack::LinkConverter.convert(v.to_s),
short: false
}
end
Expand Down Expand Up @@ -168,7 +170,6 @@ def self.is_supported?(platform)
end

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

Expand Down
56 changes: 56 additions & 0 deletions fastlane/lib/fastlane/notification/slack.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
module Fastlane
module Notification
class Slack
def initialize(webhook_url)
@webhook_url = webhook_url
@client = Faraday.new do |conn|
conn.use(Faraday::Response::RaiseError)
end
end

# Overriding channel, icon_url and username is only supported in legacy incoming webhook.
# Also note that the use of attachments has been discouraged by Slack, in favor of Block Kit.
# https://api.slack.com/legacy/custom-integrations/messaging/webhooks
def post_to_legacy_incoming_webhook(channel:, username:, attachments:, link_names:, icon_url:)
@client.post(@webhook_url) do |request|
request.headers['Content-Type'] = 'application/json'
request.body = {
channel: channel,
username: username,
icon_url: icon_url,
attachments: attachments,
link_names: link_names
}.to_json
end
end

# This class was inspired by `LinkFormatter` in `slack-notifier` gem
# https://github.com/stevenosloan/slack-notifier/blob/4bf6582663dc9e5070afe3fdc42d67c14a513354/lib/slack-notifier/util/link_formatter.rb
class LinkConverter
HTML_PATTERN = %r{<a.*?href=['"](?<link>#{URI.regexp})['"].*?>(?<label>.+?)<\/a>}
MARKDOWN_PATTERN = /\[(?<label>[^\[\]]*?)\]\((?<link>#{URI.regexp}|mailto:#{URI::MailTo::EMAIL_REGEXP})\)/

def self.convert(string)
convert_markdown_to_slack_link(convert_html_to_slack_link(string.scrub))
end

def self.convert_html_to_slack_link(string)
string.gsub(HTML_PATTERN) do |match|
slack_link(Regexp.last_match[:link], Regexp.last_match[:label])
end
end

def self.convert_markdown_to_slack_link(string)
string.gsub(MARKDOWN_PATTERN) do |match|
slack_link(Regexp.last_match[:link], Regexp.last_match[:label])
end
end

def self.slack_link(href, text)
return "<#{href}>" if text.nil? || text.empty?
"<#{href}|#{text}>"
end
end
end
end
end
28 changes: 28 additions & 0 deletions fastlane/spec/notification/slack_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
describe Fastlane::Notification::Slack do
describe Fastlane::Notification::Slack::LinkConverter do
it 'should convert HTML anchor tag to Slack link format' do
{
%|Hello <a href="https://fastlane.tools">fastlane</a>| => 'Hello <https://fastlane.tools|fastlane>',
%|Hello <a href='https://fastlane.tools'>fastlane</a>| => 'Hello <https://fastlane.tools|fastlane>',
%|Hello <a id="foo" href="https://fastlane.tools">fastlane</a>| => 'Hello <https://fastlane.tools|fastlane>',
%|Hello <a href="https://fastlane.tools">fastlane</a> <a href="https://github.com/fastlane">GitHub</a>| => 'Hello <https://fastlane.tools|fastlane> <https://github.com/fastlane|GitHub>'
}.each do |input, output|
expect(described_class.convert(input)).to eq(output)
end
end

it 'should convert Markdown link to Slack link format' do
{
%|Hello [fastlane](https://fastlane.tools)| => 'Hello <https://fastlane.tools|fastlane>',
%|Hello [fastlane](mailto:fastlane@fastlane.tools)| => 'Hello <mailto:fastlane@fastlane.tools|fastlane>',
%|Hello [fastlane](https://fastlane.tools) [GitHub](https://github.com/fastlane)| => 'Hello <https://fastlane.tools|fastlane> <https://github.com/fastlane|GitHub>',
%|Hello [[fastlane](https://fastlane.tools) in brackets]| => 'Hello [<https://fastlane.tools|fastlane> in brackets]',
%|Hello [](https://fastlane.tools)| => 'Hello <https://fastlane.tools>',
%|Hello ([fastlane](https://fastlane.tools) in parens)| => 'Hello (<https://fastlane.tools|fastlane> in parens)',
%|Hello ([fastlane(:rocket:)](https://fastlane.tools) in parens)| => 'Hello (<https://fastlane.tools|fastlane(:rocket:)> in parens)'
}.each do |input, output|
expect(described_class.convert(input)).to eq(output)
end
end
end
end

0 comments on commit a45e8f3

Please sign in to comment.