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