forked from Ivasoft/mattermost-mobile
Fix mattermost incoming webhook action for fastlane (#7262)
* Fix mattermost incoming webhook action for fastlane * feedback review * fix fastlane bundler version
This commit is contained in:
@@ -283,7 +283,7 @@ lane :upload_file_to_s3 do |options|
|
||||
:title => '',
|
||||
:thumb_url => 'https://support.apple.com/library/content/dam/edam/applecare/images/en_US/iOS/move-to-ios-icon.png',
|
||||
:msg => msg,
|
||||
:default_payloads => [],
|
||||
:default_payloads => [:git_branch, :last_git_commit_hash],
|
||||
:success => true,
|
||||
})
|
||||
end
|
||||
|
||||
@@ -2,7 +2,6 @@ source "https://rubygems.org"
|
||||
|
||||
gem "fastlane"
|
||||
gem "nokogiri"
|
||||
gem "slack-notifier", "2.3.2"
|
||||
|
||||
plugins_path = File.join(File.dirname(__FILE__), '.', 'Pluginfile')
|
||||
eval(File.read(plugins_path), binding) if File.exist?(plugins_path)
|
||||
|
||||
@@ -3,12 +3,12 @@ GEM
|
||||
specs:
|
||||
CFPropertyList (3.0.6)
|
||||
rexml
|
||||
addressable (2.8.1)
|
||||
addressable (2.8.2)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
artifactory (3.0.15)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.735.0)
|
||||
aws-partitions (1.740.0)
|
||||
aws-sdk-core (3.171.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
@@ -17,7 +17,7 @@ GEM
|
||||
aws-sdk-kms (1.63.0)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.119.2)
|
||||
aws-sdk-s3 (1.120.0)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.4)
|
||||
@@ -111,7 +111,7 @@ GEM
|
||||
fastlane-plugin-find_replace_string (0.1.0)
|
||||
fastlane-plugin-versioning_android (0.1.1)
|
||||
gh_inspector (1.1.3)
|
||||
google-apis-androidpublisher_v3 (0.37.0)
|
||||
google-apis-androidpublisher_v3 (0.38.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-core (0.11.0)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
@@ -191,7 +191,6 @@ GEM
|
||||
simctl (1.6.10)
|
||||
CFPropertyList
|
||||
naturally
|
||||
slack-notifier (2.3.2)
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
@@ -229,7 +228,6 @@ DEPENDENCIES
|
||||
fastlane-plugin-find_replace_string
|
||||
fastlane-plugin-versioning_android
|
||||
nokogiri
|
||||
slack-notifier (= 2.3.2)
|
||||
|
||||
BUNDLED WITH
|
||||
2.3.26
|
||||
|
||||
@@ -2,53 +2,216 @@
|
||||
# rubocop:disable Style/MultilineTernaryOperator
|
||||
# rubocop:disable Style/NestedTernaryOperator
|
||||
module Fastlane
|
||||
module Notification
|
||||
class Mattermost
|
||||
def initialize(webhook_url)
|
||||
@webhook_url = webhook_url
|
||||
@client = Faraday.new do | conn |
|
||||
conn.use(Faraday::Response::RaiseError)
|
||||
end
|
||||
end
|
||||
|
||||
def post_incoming_webhook(channel:, username:, attachments:, 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
|
||||
}.to_json
|
||||
end
|
||||
end
|
||||
|
||||
class LinkFormatter
|
||||
HTML_PATTERN = %r{<a.*?href=['"](?<link>#{URI.regexp})['"].*?>(?<label>.+?)<\/a>}
|
||||
MARKDOWN_PATTERN = /\[(?<label>[^\[\]]*?)\]\((?<link>#{URI.regexp}|mailto:#{URI::MailTo::EMAIL_REGEXP})\)/
|
||||
|
||||
def self.format(string)
|
||||
convert_markdown_to_link(convert_html_to_link(string.scrub))
|
||||
end
|
||||
|
||||
def self.convert_html_to_link(string)
|
||||
string.gsub(HTML_PATTERN) do |match|
|
||||
md_link(Regexp.last_match[:link], Regexp.last_match[:label])
|
||||
end
|
||||
end
|
||||
|
||||
def self.convert_markdown_to_link(string)
|
||||
string.gsub(MARKDOWN_PATTERN) do |match|
|
||||
md_link(Regexp.last_match[:link], Regexp.last_match[:label])
|
||||
end
|
||||
end
|
||||
|
||||
def self.md_link(href, text)
|
||||
return "<#{href}>" if text.nil? || text.empty?
|
||||
"<#{href}|#{text}>"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Actions
|
||||
class MattermostAction < Action
|
||||
class Runner
|
||||
def initialize(url)
|
||||
@notifier = Fastlane::Notification::Mattermost.new(url)
|
||||
end
|
||||
|
||||
def run(options)
|
||||
options[:message] = self.class.trim_message(options[:message].to_s || '')
|
||||
options[:message] = Fastlane::Notification::Mattermost::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 = ('#' + channel) unless ['#', '@'].include?(channel[0]) # send message to channel by default
|
||||
end
|
||||
|
||||
username = options[:overwrite_webhook_username_and_icon] ? nil : options[:username]
|
||||
|
||||
mattermost_attachment = self.class.generate_mattermost_attachments(options)
|
||||
icon_url = options[:overwrite_webhook_username_and_icon] ? nil : options[:icon_url]
|
||||
post_message(
|
||||
channel: channel,
|
||||
username: username,
|
||||
attachments: [mattermost_attachment],
|
||||
icon_url: icon_url,
|
||||
fail_on_error: false,
|
||||
)
|
||||
end
|
||||
|
||||
def post_message(channel:, username:, attachments:, icon_url:, fail_on_error:)
|
||||
@notifier.post_incoming_webhook(
|
||||
channel: channel,
|
||||
username: username,
|
||||
icon_url: icon_url,
|
||||
attachments: attachments
|
||||
)
|
||||
UI.success("Successfully sent Mattermost notification")
|
||||
rescue => error
|
||||
UI.error("Exception: #{error}")
|
||||
message = "Error posting Mattermost message, maybe the integration has no permission to post to this channel? Try removing the channel parameter in your Fastfile."
|
||||
if fail_on_error
|
||||
UI.user_error!(message)
|
||||
else
|
||||
UI.error(message)
|
||||
end
|
||||
end
|
||||
|
||||
def self.generate_mattermost_attachments(options)
|
||||
color = (options[:success] ? 'good' : 'danger')
|
||||
should_add_payload = ->(payload_name) { options[:default_payloads].nil? || options[:default_payloads].join(" ").include?(payload_name.to_s) }
|
||||
|
||||
mattermost_attachment = {
|
||||
fallback: options[:message],
|
||||
pretext: options[:pretext],
|
||||
text: options[:message],
|
||||
color: color,
|
||||
fields: []
|
||||
}
|
||||
|
||||
# custom user payloads
|
||||
mattermost_attachment[:fields] += options[:payload].map do |k, v|
|
||||
{
|
||||
title: k.to_s,
|
||||
value: Fastlane::Notification::Mattermost::LinkFormatter.format(v.to_s),
|
||||
short: false
|
||||
}
|
||||
end
|
||||
|
||||
# Add the lane to the Mattermost message
|
||||
# This might be nil, if mattermost is called as "one-off" action
|
||||
if should_add_payload[:lane] && Actions.lane_context[Actions::SharedValues::LANE_NAME]
|
||||
mattermost_attachment[:fields] << {
|
||||
title: 'Lane',
|
||||
value: Actions.lane_context[Actions::SharedValues::LANE_NAME],
|
||||
short: true
|
||||
}
|
||||
end
|
||||
|
||||
# test_result
|
||||
if should_add_payload[:test_result]
|
||||
mattermost_attachment[:fields] << {
|
||||
title: 'Result',
|
||||
value: (options[:success] ? 'Success' : 'Error'),
|
||||
short: true
|
||||
}
|
||||
end
|
||||
|
||||
# git branch
|
||||
if Actions.git_branch && should_add_payload[:git_branch]
|
||||
mattermost_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_MATTERMOST_HIDE_AUTHOR_ON_SUCCESS') && options[:success]
|
||||
# We only show the git author if the build failed
|
||||
else
|
||||
mattermost_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]
|
||||
mattermost_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]
|
||||
mattermost_attachment[:fields] << {
|
||||
title: 'Git Commit Hash',
|
||||
value: Actions.last_git_commit_hash(short: true),
|
||||
short: false
|
||||
}
|
||||
end
|
||||
|
||||
# merge additional properties
|
||||
deep_merge(mattermost_attachment, options[:attachment_properties])
|
||||
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]
|
||||
message.gsub('\n', "\n")
|
||||
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
|
||||
|
||||
# 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]
|
||||
message
|
||||
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])
|
||||
|
||||
username = options[:overwrite_webhook_username_and_icon] ? nil : options[:username]
|
||||
icon_url = options[:overwrite_webhook_username_and_icon] ? nil : options[:icon_url]
|
||||
|
||||
if options[:channel].to_s.length > 0
|
||||
channel = options[:channel]
|
||||
channel = ('#' + channel) unless ['#', '@'].include?(channel[0]) # send message to channel by default
|
||||
end
|
||||
|
||||
notifier = Slack::Notifier.new(options[:mattermost_url], channel: channel, username: username)
|
||||
|
||||
mattermost_attachment = generate_mattermost_attachments(options)
|
||||
|
||||
return [notifier, mattermost_attachment] if Helper.is_test? # tests will verify the mattermost attachments and other properties
|
||||
|
||||
results = notifier.ping '',
|
||||
icon_url: icon_url,
|
||||
attachments: [mattermost_attachment]
|
||||
|
||||
result = results.first
|
||||
if result.code.to_i == 200
|
||||
UI.success('Successfully sent Mattermost notification')
|
||||
else
|
||||
UI.verbose(result)
|
||||
UI.user_error!("Error pushing Mattermost message, maybe the integration has no permission to post on this channel? Try removing the channel parameter in your Fastfile.")
|
||||
end
|
||||
Runner.new(options[:mattermost_url]).run(options)
|
||||
end
|
||||
|
||||
def self.description
|
||||
@@ -119,7 +282,7 @@ module Fastlane
|
||||
end
|
||||
|
||||
def self.author
|
||||
"Mattermost base on the Slack action by KrauseFx"
|
||||
"Mattermost"
|
||||
end
|
||||
|
||||
def self.example_code
|
||||
@@ -153,96 +316,7 @@ module Fastlane
|
||||
end
|
||||
|
||||
def self.details
|
||||
"Create an Incoming WebHook and export this as `MATTERMOST_URL`. Can send a message to **channel** (by default), a direct message to **@username** or a message to a private group **group** with success (green) or failure (red) status."
|
||||
end
|
||||
|
||||
#####################################################
|
||||
# @!group Helper
|
||||
#####################################################
|
||||
|
||||
def self.generate_mattermost_attachments(options)
|
||||
color = (options[:success] ? 'good' : 'danger')
|
||||
should_add_payload = ->(payload_name) { options[:default_payloads].nil? || options[:default_payloads].join(" ").include?(payload_name.to_s) }
|
||||
|
||||
mattermost_attachment = {
|
||||
fallback: options[:message],
|
||||
pretext: options[:pretext],
|
||||
text: options[:message],
|
||||
color: color,
|
||||
fields: []
|
||||
}
|
||||
|
||||
# custom user payloads
|
||||
mattermost_attachment[:fields] += options[:payload].map do |k, v|
|
||||
{
|
||||
title: k.to_s,
|
||||
value: Slack::Notifier::LinkFormatter.format(v.to_s),
|
||||
short: false
|
||||
}
|
||||
end
|
||||
|
||||
# Add the lane to the Mattermost message
|
||||
# This might be nil, if mattermost is called as "one-off" action
|
||||
if should_add_payload[:lane] && Actions.lane_context[Actions::SharedValues::LANE_NAME]
|
||||
mattermost_attachment[:fields] << {
|
||||
title: 'Lane',
|
||||
value: Actions.lane_context[Actions::SharedValues::LANE_NAME],
|
||||
short: true
|
||||
}
|
||||
end
|
||||
|
||||
# test_result
|
||||
if should_add_payload[:test_result]
|
||||
mattermost_attachment[:fields] << {
|
||||
title: 'Result',
|
||||
value: (options[:success] ? 'Success' : 'Error'),
|
||||
short: true
|
||||
}
|
||||
end
|
||||
|
||||
# git branch
|
||||
if Actions.git_branch && should_add_payload[:git_branch]
|
||||
mattermost_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_MATTERMOST_HIDE_AUTHOR_ON_SUCCESS') && options[:success]
|
||||
# We only show the git author if the build failed
|
||||
else
|
||||
mattermost_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]
|
||||
mattermost_attachment[:fields] << {
|
||||
title: 'Git Commit',
|
||||
value: Actions.last_git_commit_message,
|
||||
short: false
|
||||
}
|
||||
end
|
||||
|
||||
# merge additional properties
|
||||
deep_merge(mattermost_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)
|
||||
"Create an Incoming WebHook and export this as `MATTERMOST_WEBHOOK_URL`. Can send a message to **channel** (by default), a direct message to **@username** or a message to a private group **group** with success (green) or failure (red) status."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -86,8 +86,8 @@ function setup() {
|
||||
fi
|
||||
|
||||
echo "Installing Fastane"
|
||||
if !gem list bundler -i --version 2.3.6 > /dev/null 2>&1; then
|
||||
gem install bundler --versio 2.3.6
|
||||
if !gem list bundler -i --version 2.3.26 > /dev/null 2>&1; then
|
||||
gem install bundler --versio 2.3.26
|
||||
fi
|
||||
cd fastlane && bundle install && cd .. || exit 1
|
||||
fi
|
||||
|
||||
@@ -11,9 +11,9 @@ function cocoapods() {
|
||||
}
|
||||
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
if !(gem list bundler -i --version 2.3.6) > /dev/null 2>&1; then
|
||||
if !(gem list bundler -i --version 2.3.26) > /dev/null 2>&1; then
|
||||
echo "Installing Bundler"
|
||||
gem install bundler --version 2.3.6 || exit 1
|
||||
gem install bundler --version 2.3.26 || exit 1
|
||||
fi
|
||||
|
||||
if !(gem list cocoapods -i --version 1.11.3) > /dev/null 2>&1; then
|
||||
|
||||
Reference in New Issue
Block a user