diff --git a/lib/fastlane/plugin/ddg_apple_automation/actions/mattermost_send_message_action.rb b/lib/fastlane/plugin/ddg_apple_automation/actions/mattermost_send_message_action.rb
new file mode 100644
index 0000000..9dfd8e4
--- /dev/null
+++ b/lib/fastlane/plugin/ddg_apple_automation/actions/mattermost_send_message_action.rb
@@ -0,0 +1,97 @@
+require "fastlane/action"
+require "fastlane_core/configuration/config_item"
+require "yaml"
+require "httparty"
+require "json"
+require_relative "../helper/ddg_apple_automation_helper"
+
+module Fastlane
+ module Actions
+ class MattermostSendMessageAction < Action
+ def self.run(params)
+ github_handle = params[:github_handle]
+ template_name = params[:template_name]
+ mm_webhook_url = params[:mattermost_webhook_url]
+ args = (params[:template_args] || {}).merge(Hash(ENV).transform_keys { |key| key.downcase.gsub('-', '_') })
+ mapping_file = Helper::DdgAppleAutomationHelper.path_for_asset_file("mattermost_send_message/github-mattermost-user-id-mapping.yml")
+ user_mapping = YAML.load_file(mapping_file)
+ mattermost_user_handle = user_mapping[github_handle]
+
+ if mattermost_user_handle.nil? || mattermost_user_handle.to_s.empty?
+ UI.message("Mattermost user handle not known for #{github_handle}, skipping sending message")
+ return
+ end
+
+ text = process_template(template_name, args)
+ payload = {
+ "channel" => mattermost_user_handle,
+ "username" => "GitHub Actions",
+ "text" => text,
+ "icon_url" => "https://duckduckgo.com/assets/logo_header.v108.svg"
+ }
+
+ response = HTTParty.post(mm_webhook_url, {
+ headers: { 'Content-Type' => 'application/json' },
+ body: payload.to_json
+ })
+
+ # Check response status
+ if response.success?
+ UI.success("Message sent successfully!")
+ else
+ UI.user_error!("Failed to send message: #{response.body}")
+ end
+ end
+
+ def self.description
+ "This action sends a message to Mattermost, reporting the outcome to the user who triggered the workflow"
+ end
+
+ def self.authors
+ ["DuckDuckGo"]
+ end
+
+ def self.return_value
+ ""
+ end
+
+ def self.details
+ # Optional:
+ ""
+ end
+
+ def self.process_template(template_name, args)
+ template_file = Helper::DdgAppleAutomationHelper.path_for_asset_file("mattermost_send_message/templates/#{template_name}.txt.erb")
+ Helper::DdgAppleAutomationHelper.process_erb_template(template_file, args)
+ end
+
+ def self.available_options
+ [
+ FastlaneCore::ConfigItem.new(key: :mattermost_webhook_url,
+ env_name: "MM_WEBHOOK_URL",
+ description: "Mattermost webhook URL",
+ optional: false,
+ type: String),
+ FastlaneCore::ConfigItem.new(key: :github_handle,
+ description: "Github user handle",
+ optional: false,
+ type: String),
+ FastlaneCore::ConfigItem.new(key: :template_args,
+ description: "Template arguments. For backward compatibility, environment variables are added to this hash",
+ optional: true,
+ type: Hash,
+ default_value: {}),
+ FastlaneCore::ConfigItem.new(key: :template_name,
+ description: "Name of a template file (without extension) for the message. Templates can be found in assets/mattermost_send_message/templates subdirectory.
+ The file is processed before being posted",
+ optional: false,
+ type: String)
+ ]
+ end
+
+ def self.is_supported?(platform)
+ true
+ end
+ end
+ end
+end
diff --git a/lib/fastlane/plugin/ddg_apple_automation/actions/start_new_release_action.rb b/lib/fastlane/plugin/ddg_apple_automation/actions/start_new_release_action.rb
index 6188ada..dcae45a 100644
--- a/lib/fastlane/plugin/ddg_apple_automation/actions/start_new_release_action.rb
+++ b/lib/fastlane/plugin/ddg_apple_automation/actions/start_new_release_action.rb
@@ -22,7 +22,7 @@ def self.run(params)
options[:version] = new_version
options[:release_branch_name] = release_branch_name
- release_task_id = Helper::AsanaHelper.create_release_task(options[:platform], options[:version], options[:asana_user_id], options[:asana_access_token], options[:is_hotfix])
+ release_task_id = Helper::AsanaHelper.create_release_task(options[:platform], options[:version], options[:asana_user_id], options[:asana_access_token])
options[:release_task_id] = release_task_id
Helper::AsanaHelper.update_asana_tasks_for_internal_release(options)
@@ -68,12 +68,7 @@ def self.available_options
FastlaneCore::ConfigItem.new(key: :target_section_id,
description: "Section ID in Asana where tasks included in the release should be moved",
optional: false,
- type: String),
- FastlaneCore::ConfigItem.new(key: :is_hotfix,
- description: "Is this a hotfix release?",
- optional: true,
- type: Boolean,
- default_value: false)
+ type: String)
]
end
diff --git a/lib/fastlane/plugin/ddg_apple_automation/actions/validate_internal_release_bump_action.rb b/lib/fastlane/plugin/ddg_apple_automation/actions/validate_internal_release_bump_action.rb
index 46ebec8..a02467f 100644
--- a/lib/fastlane/plugin/ddg_apple_automation/actions/validate_internal_release_bump_action.rb
+++ b/lib/fastlane/plugin/ddg_apple_automation/actions/validate_internal_release_bump_action.rb
@@ -14,7 +14,7 @@ def self.run(params)
options = params.values
find_release_task_if_needed(options)
- unless Helper::GitHelper.assert_branch_has_changes(options[:release_branch])
+ if params[:is_scheduled_release] && !Helper::GitHelper.assert_branch_has_changes(options[:release_branch])
UI.important("No changes to the release branch (or only changes to scripts and workflows). Skipping automatic release.")
Helper::GitHubActionsHelper.set_output("skip_release", true)
return
diff --git a/lib/fastlane/plugin/ddg_apple_automation/assets/github-asana-user-id-mapping.yml b/lib/fastlane/plugin/ddg_apple_automation/assets/asana_get_user_id_for_github_handle/github-asana-user-id-mapping.yml
similarity index 100%
rename from lib/fastlane/plugin/ddg_apple_automation/assets/github-asana-user-id-mapping.yml
rename to lib/fastlane/plugin/ddg_apple_automation/assets/asana_get_user_id_for_github_handle/github-asana-user-id-mapping.yml
diff --git a/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/github-mattermost-user-id-mapping.yml b/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/github-mattermost-user-id-mapping.yml
new file mode 100644
index 0000000..9b2c747
--- /dev/null
+++ b/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/github-mattermost-user-id-mapping.yml
@@ -0,0 +1,30 @@
+aataraxiaa: "@psmith"
+afterxleep: "@dbernal"
+alessandroboron: "@aboron"
+amddg44: "@amallon"
+ayoy: "@dkapusta"
+brindy: "@brindy"
+bwaresiak: "@bartek"
+Bunn: "@fbunn"
+dharb: "@dave"
+diegoreymendez: "@dreymendez"
+dus7: "@mariusz"
+federicocappelli: "@fcappelli"
+GioSensation: "@emanuele"
+graeme: "@garthur"
+jaceklyp: "@jlyp"
+jonathanKingston: "@jkingston"
+jotaemepereira: "@jpereira"
+ladamski: "@ladamski"
+mallexxx: "@amartemyanov"
+miasma13: "@msmaga"
+muodov: "@mtsoy"
+quanganhdo: "@ado"
+samsymons: "@ssymons"
+shakyShane: "@sosbourne"
+SabrinaTardio: "@stardio"
+THISISDINOSAUR: "@esullivan"
+tomasstrba: "@tom"
+viktorjansson: "@viktor"
+vinay-nadig-0042: "@vnadig"
+kshann: "@kshannon"
\ No newline at end of file
diff --git a/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/ios-release-failed.txt.erb b/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/ios-release-failed.txt.erb
new file mode 100644
index 0000000..60ce446
--- /dev/null
+++ b/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/ios-release-failed.txt.erb
@@ -0,0 +1 @@
+:warning: **iOS release job failed** :thisisfine: | [:github: Workflow run summary](<%= workflow_url %>)
\ No newline at end of file
diff --git a/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/notarized-build-complete.txt.erb b/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/notarized-build-complete.txt.erb
new file mode 100644
index 0000000..40758e6
--- /dev/null
+++ b/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/notarized-build-complete.txt.erb
@@ -0,0 +1 @@
+Notarized macOS app `<%= release_type %>` build is ready :goose_honk_tada: | [:github: Workflow run summary](<%= workflow_url %>)<% if defined?(asana_task_url) && !asana_task_url.to_s.strip.empty? %> | [:asana: Asana Task](<%= asana_task_url %>)<% end %>
\ No newline at end of file
diff --git a/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/notarized-build-failed.txt.erb b/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/notarized-build-failed.txt.erb
new file mode 100644
index 0000000..c25fee7
--- /dev/null
+++ b/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/notarized-build-failed.txt.erb
@@ -0,0 +1 @@
+:rotating_light: Notarized macOS app `<%= release_type %>` build failed | [:github: Workflow run summary](<%= workflow_url %>)<% if defined?(asana_task_url) && !asana_task_url.to_s.strip.empty? %> | [:asana: Asana Task](<%= asana_task_url %>)<% end %>
\ No newline at end of file
diff --git a/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/public-release-complete.txt.erb b/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/public-release-complete.txt.erb
new file mode 100644
index 0000000..d18ee20
--- /dev/null
+++ b/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/public-release-complete.txt.erb
@@ -0,0 +1 @@
+<%= platform %> app has been successfully uploaded to <%= destination %> :goose_honk_tada: | [:github: Workflow run summary](<%= workflow_url %>)
\ No newline at end of file
diff --git a/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/public-release-failed.txt.erb b/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/public-release-failed.txt.erb
new file mode 100644
index 0000000..7f6a981
--- /dev/null
+++ b/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/public-release-failed.txt.erb
@@ -0,0 +1 @@
+:rotating_light: <%= platform %> app <%= destination %> workflow failed | [:github: Workflow run summary](<%= workflow_url %>)
\ No newline at end of file
diff --git a/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/variants-release-failed.txt.erb b/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/variants-release-failed.txt.erb
new file mode 100644
index 0000000..7ffa10d
--- /dev/null
+++ b/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/variants-release-failed.txt.erb
@@ -0,0 +1 @@
+:rotating_light: macOS app variants workflow failed | [:github: Workflow run summary](<%= workflow_url %>)
\ No newline at end of file
diff --git a/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/variants-release-published.txt.erb b/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/variants-release-published.txt.erb
new file mode 100644
index 0000000..5c269db
--- /dev/null
+++ b/lib/fastlane/plugin/ddg_apple_automation/assets/mattermost_send_message/templates/variants-release-published.txt.erb
@@ -0,0 +1 @@
+macOS app variants have been published successfully :goose_honk_tada: | [:github: Workflow run summary](<%= workflow_url %>)
\ No newline at end of file
diff --git a/lib/fastlane/plugin/ddg_apple_automation/helper/asana_helper.rb b/lib/fastlane/plugin/ddg_apple_automation/helper/asana_helper.rb
index fbd02d3..a2e6514 100644
--- a/lib/fastlane/plugin/ddg_apple_automation/helper/asana_helper.rb
+++ b/lib/fastlane/plugin/ddg_apple_automation/helper/asana_helper.rb
@@ -106,7 +106,7 @@ def self.get_release_automation_subtask_id(task_url, asana_access_token)
end
def self.get_asana_user_id_for_github_handle(github_handle)
- mapping_file = File.expand_path('../assets/github-asana-user-id-mapping.yml', __dir__)
+ mapping_file = Helper::DdgAppleAutomationHelper.path_for_asset_file('asana_get_user_id_for_github_handle/github-asana-user-id-mapping.yml')
user_mapping = YAML.load_file(mapping_file)
asana_user_id = user_mapping[github_handle]
@@ -129,7 +129,7 @@ def self.upload_file_to_asana_task(task_id, file_path, asana_access_token)
end
end
- def self.release_template_task_id(platform, is_hotfix)
+ def self.release_template_task_id(platform, is_hotfix: false)
case platform
when "ios"
is_hotfix ? IOS_HOTFIX_TASK_TEMPLATE_ID : IOS_RELEASE_TASK_TEMPLATE_ID
@@ -140,7 +140,7 @@ def self.release_template_task_id(platform, is_hotfix)
end
end
- def self.release_task_name(version, platform, is_hotfix)
+ def self.release_task_name(version, platform, is_hotfix: false)
case platform
when "ios"
is_hotfix ? "iOS App Hotfix Release #{version}" : "iOS App Release #{version}"
@@ -173,9 +173,9 @@ def self.release_section_id(platform)
end
end
- def self.create_release_task(platform, version, assignee_id, asana_access_token, is_hotfix)
- template_task_id = release_template_task_id(platform, is_hotfix)
- task_name = release_task_name(version, platform, is_hotfix)
+ def self.create_release_task(platform, version, assignee_id, asana_access_token)
+ template_task_id = release_template_task_id(platform)
+ task_name = release_task_name(version, platform)
section_id = release_section_id(platform)
UI.message("Creating release task for #{version}")
@@ -296,10 +296,6 @@ def self.update_asana_tasks_for_public_release(params)
Helper::ReleaseTaskHelper.construct_release_announcement_task_description(params[:version], release_notes, task_ids)
end
- # Rertieves tasks between this release and the last internal release
- #
- # @param github_token [String] GitHub token
- #
def self.get_tasks_in_last_internal_release(platform, github_token)
# 1. Find last internal release tag (last internal release is the second one, because the first one is the release that's just created)
UI.message("Fetching tasks in latest internal release")
@@ -325,7 +321,7 @@ def self.fetch_tasks_for_tag(tag_id, asana_access_token)
asana_client = make_asana_client(asana_access_token)
task_ids = []
begin
- response = asana_client.tasks.get_tasks_for_tag(tag_gid: tag_id, options: { opt_fields: ["gid"] })
+ response = asana_client.tasks.get_tasks_for_tag(tag_gid: tag_id, options: { fields: ["gid"] })
loop do
task_ids += response.map(&:gid)
response = response.next_page
@@ -341,7 +337,7 @@ def self.fetch_subtasks(task_id, asana_access_token)
asana_client = make_asana_client(asana_access_token)
task_ids = []
begin
- response = asana_client.tasks.get_subtasks_for_task(task_gid: task_id, options: { opt_fields: ["gid"] })
+ response = asana_client.tasks.get_subtasks_for_task(task_gid: task_id, options: { fields: ["gid"] })
loop do
task_ids += response.map(&:gid)
response = response.next_page
@@ -381,7 +377,7 @@ def self.complete_tasks(task_ids, asana_access_token)
next
end
- projects_ids = asana_client.projects.get_projects_for_task(task_gid: task_id, options: { opt_fields: ["gid"] }).map(&:gid)
+ projects_ids = asana_client.projects.get_projects_for_task(task_gid: task_id, options: { fields: ["gid"] }).map(&:gid)
if projects_ids.include?(CURRENT_OBJECTIVES_PROJECT_ID)
UI.important("Not completing task #{task_id} because it's a Current Objective")
next
@@ -395,7 +391,7 @@ def self.complete_tasks(task_ids, asana_access_token)
def self.find_asana_release_tag(tag_name, release_task_id, asana_access_token)
asana_client = make_asana_client(asana_access_token)
- release_task_tags = asana_client.tasks.get_task(task_gid: release_task_id, options: { opt_fields: ["tags"] }).tags
+ release_task_tags = asana_client.tasks.get_task(task_gid: release_task_id, options: { fields: ["tags"] }).tags
if (tag_id = release_task_tags.find { |t| t.name == tag_name }&.gid) && !tag_id.to_s.empty?
return tag_id
@@ -436,22 +432,6 @@ def self.sanitize_asana_html_notes(content)
.gsub(%r{
}, "\n") # replace
tags with newlines
end
- def self.get_task_ids_from_git_log(from_ref, to_ref = "HEAD")
- git_log = `git log #{from_ref}..#{to_ref}`
-
- git_log
- .gsub("\n", " ")
- .scan(%r{\bTask/Issue URL:.*?https://app\.asana\.com[/0-9f]+\b})
- .map { |task_line| task_line.gsub(/.*(https.*)/, '\1') }
- .map { |task_url| extract_asana_task_id(task_url, set_gha_output: false) }
- end
-
- def self.fetch_release_notes(release_task_id, asana_access_token, output_type: "asana")
- asana_client = make_asana_client(asana_access_token)
- release_task_body = asana_client.tasks.get_task(task_gid: release_task_id, options: { opt_fields: ["notes"] }).notes
- ReleaseTaskHelper.extract_release_notes(release_task_body, output_type: output_type)
- end
-
def self.construct_this_release_includes(task_ids)
return '' if task_ids.empty?
diff --git a/lib/fastlane/plugin/ddg_apple_automation/version.rb b/lib/fastlane/plugin/ddg_apple_automation/version.rb
index 0d8ee25..e532462 100644
--- a/lib/fastlane/plugin/ddg_apple_automation/version.rb
+++ b/lib/fastlane/plugin/ddg_apple_automation/version.rb
@@ -1,5 +1,5 @@
module Fastlane
module DdgAppleAutomation
- VERSION = "0.11.3"
+ VERSION = "0.11.7"
end
end
diff --git a/spec/asana_helper_spec.rb b/spec/asana_helper_spec.rb
index 8a7d090..43ad357 100644
--- a/spec/asana_helper_spec.rb
+++ b/spec/asana_helper_spec.rb
@@ -140,24 +140,11 @@ def get_release_automation_subtask_id(task_url)
end
describe "#get_asana_user_id_for_github_handle" do
- let(:yaml_content) do
- {
- "duck" => "123",
- "goose" => "456",
- "pigeon" => nil,
- "hawk" => ""
- }
- end
-
- before do
- allow(YAML).to receive(:load_file).and_return(yaml_content)
- end
-
it "sets the user ID output and GHA output correctly" do
allow(Fastlane::Helper::GitHubActionsHelper).to receive(:set_output)
- expect(get_asana_user_id_for_github_handle("duck")).to eq("123")
- expect(Fastlane::Helper::GitHubActionsHelper).to have_received(:set_output).with("asana_user_id", "123")
+ expect(get_asana_user_id_for_github_handle("jotaemepereira")).to eq("1203972458584419")
+ expect(Fastlane::Helper::GitHubActionsHelper).to have_received(:set_output).with("asana_user_id", "1203972458584419")
end
it "shows warning when handle does not exist" do
@@ -348,7 +335,11 @@ def sanitize_asana_html_notes(content)
expect(@asana_sections).to receive(:add_task_for_section).with(section_gid: section_id, task: task_id)
expect(@asana_tasks).to receive(:update_task).with(task_gid: task_id, assignee: assignee_id)
+<<<<<<< HEAD
Fastlane::Helper::AsanaHelper.create_release_task(platform, version, assignee_id, asana_access_token, false)
+=======
+ Fastlane::Helper::AsanaHelper.create_release_task(platform, version, assignee_id, asana_access_token)
+>>>>>>> main
expect(Fastlane::UI).to have_received(:message).with("Creating release task for #{version}")
expect(Fastlane::Helper::GitHubActionsHelper).to have_received(:set_output).with("asana_task_id", task_id)
@@ -363,7 +354,11 @@ def sanitize_asana_html_notes(content)
allow(HTTParty).to receive(:post).and_return(double(success?: false, code: 500, message: "Internal Server Error"))
expect do
+<<<<<<< HEAD
Fastlane::Helper::AsanaHelper.create_release_task(platform, version, assignee_id, asana_access_token, false)
+=======
+ Fastlane::Helper::AsanaHelper.create_release_task(platform, version, assignee_id, asana_access_token)
+>>>>>>> main
end.to raise_error(FastlaneCore::Interface::FastlaneError, "Failed to instantiate task from template #{template_task_id}: (500 Internal Server Error)")
end
end
@@ -458,11 +453,19 @@ def sanitize_asana_html_notes(content)
allow(response_page2).to receive(:next_page).and_return(nil)
allow(@asana_tasks).to receive(:get_tasks_for_tag)
+<<<<<<< HEAD
.with(tag_gid: tag_id, options: { opt_fields: ["gid"] })
.and_return(response_page1)
allow(@asana_tasks).to receive(:get_tasks_for_tag)
.with(tag_gid: tag_id, options: { opt_fields: ["gid"], offset: "eyJ0eXAiOJiKV1iQLCJhbGciOiJIUzI1NiJ9" })
+=======
+ .with(tag_gid: tag_id, options: { fields: ["gid"] })
+ .and_return(response_page1)
+
+ allow(@asana_tasks).to receive(:get_tasks_for_tag)
+ .with(tag_gid: tag_id, options: { fields: ["gid"], offset: "eyJ0eXAiOJiKV1iQLCJhbGciOiJIUzI1NiJ9" })
+>>>>>>> main
.and_return(response_page2)
result = Fastlane::Helper::AsanaHelper.fetch_tasks_for_tag(tag_id, asana_access_token)
@@ -507,11 +510,19 @@ def sanitize_asana_html_notes(content)
allow(response_page1).to receive(:next_page).and_return(response_page2)
allow(@asana_tasks).to receive(:get_subtasks_for_task)
+<<<<<<< HEAD
.with(task_gid: task_id, options: { opt_fields: ["gid"] })
.and_return(response_page1)
allow(@asana_tasks).to receive(:get_subtasks_for_task)
.with(task_gid: task_id, options: { opt_fields: ["gid"], offset: "eyJ0eXAiOJiKV1iQLCJhbGciOiJIUzI1NiJ9" })
+=======
+ .with(task_gid: task_id, options: { fields: ["gid"] })
+ .and_return(response_page1)
+
+ allow(@asana_tasks).to receive(:get_subtasks_for_task)
+ .with(task_gid: task_id, options: { fields: ["gid"], offset: "eyJ0eXAiOJiKV1iQLCJhbGciOiJIUzI1NiJ9" })
+>>>>>>> main
.and_return(response_page2)
result = Fastlane::Helper::AsanaHelper.fetch_subtasks(task_id, asana_access_token)
@@ -568,19 +579,31 @@ def sanitize_asana_html_notes(content)
response_task1 = double("Asana::Collection", data: [double("Asana::Project", gid: Fastlane::Helper::AsanaHelper::INCIDENTS_PARENT_TASK_ID)])
allow(response_task1).to receive(:map).and_return([Fastlane::Helper::AsanaHelper::INCIDENTS_PARENT_TASK_ID])
allow(@asana_projects).to receive(:get_projects_for_task)
+<<<<<<< HEAD
.with(task_gid: "1234567890", options: { opt_fields: ["gid"] })
+=======
+ .with(task_gid: "1234567890", options: { fields: ["gid"] })
+>>>>>>> main
.and_return(response_task1)
response_task2 = double("Asana::Collection", data: [double("Asana::Project", gid: "non_objective_id")])
allow(response_task2).to receive(:map).and_return(["non_objective_id"])
allow(@asana_projects).to receive(:get_projects_for_task)
+<<<<<<< HEAD
.with(task_gid: "1234567891", options: { opt_fields: ["gid"] })
+=======
+ .with(task_gid: "1234567891", options: { fields: ["gid"] })
+>>>>>>> main
.and_return(response_task2)
response_task3 = double("Asana::Collection", data: [double("Asana::Project", gid: Fastlane::Helper::AsanaHelper::CURRENT_OBJECTIVES_PROJECT_ID)])
allow(response_task3).to receive(:map).and_return([Fastlane::Helper::AsanaHelper::CURRENT_OBJECTIVES_PROJECT_ID])
allow(@asana_projects).to receive(:get_projects_for_task)
+<<<<<<< HEAD
.with(task_gid: "1234567892", options: { opt_fields: ["gid"] })
+=======
+ .with(task_gid: "1234567892", options: { fields: ["gid"] })
+>>>>>>> main
.and_return(response_task3)
expect(@asana_tasks).to receive(:update_task)
@@ -657,6 +680,7 @@ def sanitize_asana_html_notes(content)
Fastlane::Helper::AsanaHelper.tag_tasks(tag_id, task_ids, asana_access_token)
end
end
+<<<<<<< HEAD
describe "#get_tasks_in_last_internal_release" do
let(:params) do
@@ -729,4 +753,6 @@ def sanitize_asana_html_notes(content)
expect(html_list).to eq("")
end
end
+=======
+>>>>>>> main
end
diff --git a/spec/mattermost_send_message_action_spec.rb b/spec/mattermost_send_message_action_spec.rb
new file mode 100644
index 0000000..9d6aeab
--- /dev/null
+++ b/spec/mattermost_send_message_action_spec.rb
@@ -0,0 +1,167 @@
+describe Fastlane::Actions::MattermostSendMessageAction do
+ describe "run" do
+ let(:params) do
+ {
+ mattermost_webhook_url: "http://example.com/webhook",
+ github_handle: "user",
+ template_name: "test_template"
+ }
+ end
+
+ let(:user_mapping) { { "user" => "@mattermost_user" } }
+ let(:template_content) { { "text" => "Hello <%= name %>" } }
+ let(:processed_template) { "Hello World" }
+
+ before do
+ allow(YAML).to receive(:load_file).with(anything).and_return(user_mapping)
+ allow(Fastlane::Helper::DdgAppleAutomationHelper).to receive(:path_for_asset_file).and_return("mock_path")
+ allow(Fastlane::Helper::DdgAppleAutomationHelper).to receive(:process_erb_template).and_return(processed_template)
+ allow(YAML).to receive(:safe_load).and_return("text" => processed_template)
+ allow(HTTParty).to receive(:post).and_return(double(success?: true))
+
+ allow(ENV).to receive(:[]).with("NAME").and_return("World")
+ end
+
+ it "sends a message to Mattermost with correct payload" do
+ expected_payload = {
+ "channel" => "@mattermost_user",
+ "username" => "GitHub Actions",
+ "text" => "Hello World",
+ "icon_url" => "https://duckduckgo.com/assets/logo_header.v108.svg"
+ }
+
+ expect(HTTParty).to receive(:post).with(
+ "http://example.com/webhook",
+ hash_including(
+ headers: { 'Content-Type' => 'application/json' },
+ body: expected_payload.to_json
+ )
+ ).and_return(double(success?: true))
+
+ Fastlane::Actions::MattermostSendMessageAction.run(params)
+ end
+
+ it "skips sending if Mattermost user handle is unknown" do
+ allow(YAML).to receive(:load_file).and_return({})
+
+ expect(HTTParty).not_to receive(:post)
+ expect(FastlaneCore::UI).to receive(:message).with("Mattermost user handle not known for user, skipping sending message")
+
+ Fastlane::Actions::MattermostSendMessageAction.run(params)
+ end
+
+ it "handles unsuccessful HTTP response" do
+ allow(HTTParty).to receive(:post).and_return(double(success?: false, body: "Error message"))
+
+ expect { Fastlane::Actions::MattermostSendMessageAction.run(params) }.to raise_error(FastlaneCore::Interface::FastlaneError, "Failed to send message: Error message")
+ end
+ end
+
+ describe "process_template" do
+ it "processes ios-release-failed template" do
+ expected = ":warning: **iOS release job failed** :thisisfine: | [:github: Workflow run summary](https://workflow.com)"
+
+ expect(process_template("ios-release-failed", {
+ "workflow_url" => "https://workflow.com"
+ })).to eq(expected)
+ end
+
+ it "processes notarized-build-complete template" do
+ expected = "Notarized macOS app `release` build is ready :goose_honk_tada: | [:github: Workflow run summary](https://workflow.com)"
+
+ expect(process_template("notarized-build-complete", {
+ "release_type" => "release",
+ "workflow_url" => "https://workflow.com"
+ })).to eq(expected)
+ end
+
+ it "processes notarized-build-complete template with a nil asana_task_url" do
+ expected = "Notarized macOS app `release` build is ready :goose_honk_tada: | [:github: Workflow run summary](https://workflow.com)"
+
+ expect(process_template("notarized-build-complete", {
+ "release_type" => "release",
+ "workflow_url" => "https://workflow.com",
+ "asana_task_url" => nil
+ })).to eq(expected)
+ end
+
+ it "processes notarized-build-complete template with an empty asana_task_url" do
+ expected = "Notarized macOS app `release` build is ready :goose_honk_tada: | [:github: Workflow run summary](https://workflow.com)"
+
+ expect(process_template("notarized-build-complete", {
+ "release_type" => "release",
+ "workflow_url" => "https://workflow.com",
+ "asana_task_url" => ""
+ })).to eq(expected)
+ end
+
+ it "processes notarized-build-complete template with Asana task URL" do
+ expected = "Notarized macOS app `release` build is ready :goose_honk_tada: | [:github: Workflow run summary](https://workflow.com) | [:asana: Asana Task](https://asana.com)"
+
+ expect(process_template("notarized-build-complete", {
+ "asana_task_url" => "https://asana.com",
+ "release_type" => "release",
+ "workflow_url" => "https://workflow.com"
+ })).to eq(expected)
+ end
+
+ it "processes notarized-build-failed template" do
+ expected = ":rotating_light: Notarized macOS app `release` build failed | [:github: Workflow run summary](https://workflow.com)"
+
+ expect(process_template("notarized-build-failed", {
+ "release_type" => "release",
+ "workflow_url" => "https://workflow.com"
+ })).to eq(expected)
+ end
+
+ it "processes notarized-build-failed template with Asana task URL" do
+ expected = ":rotating_light: Notarized macOS app `release` build failed | [:github: Workflow run summary](https://workflow.com) | [:asana: Asana Task](https://asana.com)"
+
+ expect(process_template("notarized-build-failed", {
+ "asana_task_url" => "https://asana.com",
+ "release_type" => "release",
+ "workflow_url" => "https://workflow.com"
+ })).to eq(expected)
+ end
+
+ it "processes public-release-complete template" do
+ expected = "macOS app has been successfully uploaded to testflight :goose_honk_tada: | [:github: Workflow run summary](https://workflow.com)"
+
+ expect(process_template("public-release-complete", {
+ "platform" => "macOS",
+ "destination" => "testflight",
+ "workflow_url" => "https://workflow.com"
+ })).to eq(expected)
+ end
+
+ it "processes public-release-failed template" do
+ expected = ":rotating_light: macOS app testflight workflow failed | [:github: Workflow run summary](https://workflow.com)"
+
+ expect(process_template("public-release-failed", {
+ "platform" => "macOS",
+ "destination" => "testflight",
+ "workflow_url" => "https://workflow.com"
+ })).to eq(expected)
+ end
+
+ it "processes variants-release-failed template" do
+ expected = ":rotating_light: macOS app variants workflow failed | [:github: Workflow run summary](https://workflow.com)"
+
+ expect(process_template("variants-release-failed", {
+ "workflow_url" => "https://workflow.com"
+ })).to eq(expected)
+ end
+
+ it "processes variants-release-published template" do
+ expected = "macOS app variants have been published successfully :goose_honk_tada: | [:github: Workflow run summary](https://workflow.com)"
+
+ expect(process_template("variants-release-published", {
+ "workflow_url" => "https://workflow.com"
+ })).to eq(expected)
+ end
+
+ def process_template(template_name, args)
+ Fastlane::Actions::MattermostSendMessageAction.process_template(template_name, args)
+ end
+ end
+end
diff --git a/spec/start_new_release_action_spec.rb b/spec/start_new_release_action_spec.rb
index 3ed6b4e..5ddcef4 100644
--- a/spec/start_new_release_action_spec.rb
+++ b/spec/start_new_release_action_spec.rb
@@ -35,12 +35,15 @@
end
end
+<<<<<<< HEAD
shared_context "with hotfix" do
before do
@params[:is_hotfix] = true
end
end
+=======
+>>>>>>> main
describe Fastlane::Actions::StartNewReleaseAction do
describe '#run' do
subject do
@@ -70,7 +73,11 @@
it 'creates a release task in Asana' do
subject
+<<<<<<< HEAD
expect(Fastlane::Helper::AsanaHelper).to have_received(:create_release_task).with("ios", "1.1.0", "user", "secret-token", false)
+=======
+ expect(Fastlane::Helper::AsanaHelper).to have_received(:create_release_task).with("ios", "1.1.0", "user", "secret-token")
+>>>>>>> main
end
it 'updates Asana tasks for internal release' do
@@ -107,7 +114,11 @@
it 'creates a release task in Asana' do
subject
+<<<<<<< HEAD
expect(Fastlane::Helper::AsanaHelper).to have_received(:create_release_task).with("macos", "1.1.0", "user", "secret-token", false)
+=======
+ expect(Fastlane::Helper::AsanaHelper).to have_received(:create_release_task).with("macos", "1.1.0", "user", "secret-token")
+>>>>>>> main
end
it 'updates Asana tasks for internal release' do
@@ -123,6 +134,7 @@
)
end
end
+<<<<<<< HEAD
context "when creating a hotfix release task" do
include_context "common setup"
@@ -157,6 +169,8 @@
)
end
end
+=======
+>>>>>>> main
end
# Constants
diff --git a/spec/validate_internal_release_bump_action_spec.rb b/spec/validate_internal_release_bump_action_spec.rb
index 63b1cce..5d0816b 100644
--- a/spec/validate_internal_release_bump_action_spec.rb
+++ b/spec/validate_internal_release_bump_action_spec.rb
@@ -63,11 +63,29 @@
end
context "when there are no changes in the release branch" do
- it "skips the release" do
+ before do
allow(Fastlane::Helper::GitHelper).to receive(:assert_branch_has_changes).and_return(false)
- expect(Fastlane::UI).to receive(:important).with("No changes to the release branch (or only changes to scripts and workflows). Skipping automatic release.")
- expect(Fastlane::Helper::GitHubActionsHelper).to receive(:set_output).with("skip_release", true)
- subject
+ end
+
+ context "when it's a scheduled release" do
+ before do
+ @params[:is_scheduled_release] = true
+ end
+
+ it "skips the release" do
+ allow(Fastlane::Helper::GitHelper).to receive(:assert_branch_has_changes).and_return(false)
+ expect(Fastlane::UI).to receive(:important).with("No changes to the release branch (or only changes to scripts and workflows). Skipping automatic release.")
+ expect(Fastlane::Helper::GitHubActionsHelper).to receive(:set_output).with("skip_release", true)
+ subject
+ end
+ end
+
+ context "when it's not a scheduled release" do
+ it "proceeds with release bump if release notes are valid" do
+ expect(Fastlane::UI).to receive(:message).with("Validating release notes")
+ expect(Fastlane::UI).to receive(:message).with("Release notes are valid: Valid release notes")
+ subject
+ end
end
end
end