tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

slack_notifier.py (7290B)


      1 #!/usr/bin/env python3
      2 
      3 # This Source Code Form is subject to the terms of the Mozilla Public
      4 # License, v. 2.0. If a copy of the MPL was not distributed with this
      5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
      6 """
      7 This module provides functionalities for sending notifications to Slack channels, specifically designed for use in automated testing and release processes. It includes capabilities for sending both success and error notifications with customizable message templates. The module leverages Taskcluster for notification services and integrates with Slack's API to deliver real-time updates.
      8 
      9 Key Features:
     10 - SLACK_SUCCESS_MESSAGE_TEMPLATE: A predefined template for formatting success messages to be sent to Slack. This template includes placeholders for dynamic content such as product version and release details.
     11 - SLACK_ERROR_MESSAGE_TEMPLATE: A template for error messages, used to notify about failures or issues in automated processes, particularly with TestRail API interactions.
     12 - send_slack_notification: A function that sends a Slack notification based on a provided template and value dictionary. It handles the construction of the message payload and interfaces with Taskcluster's Slack notification service.
     13 - get_taskcluster_options: Retrieves configuration options for Taskcluster based on the current runtime environment, ensuring appropriate setup for notification delivery.
     14 - send_error_notification: A higher-level function that formats and sends error notifications to a specified Slack channel.
     15 - send_success_notification: Similarly, this function sends success notifications to a specified Slack channel, using the success message template.
     16 
     17 Usage:
     18 The module is intended to be integrated into automated testing and release workflows, where Slack notifications are required to report the status of various processes, such as test executions or release milestones.
     19 
     20 Required Values for Notifications:
     21 
     22 These values are required when calling the `send_success_notification` and `send_slack_notification` functions.
     23 They must be passed as an object with the following keys and their respective values.
     24 
     25 Required Keys and Expected Values:
     26 - RELEASE_TYPE: <string> Release Type or Stage (e.g., Alpha, Beta, RC).
     27 - RELEASE_VERSION: <string> Release Version from versions.txt (e.g., '124.0b5').
     28 - SHIPPING_PRODUCT: <string> Release Tag Name (e.g., fennec, focus).
     29 - TESTRAIL_PROJECT_ID: <int> Project ID for TestRail Project (e.g., Fenix Browser).
     30 - TESTRAIL_PRODUCT_TYPE: <string> Name for the official release product (e.g., Firefox, not fennec).
     31 
     32 These values are used as arguments for `success_values` and `values` when calling the respective functions.
     33 
     34 Example Usage:
     35 
     36 success_values = {
     37    "RELEASE_TYPE": "Beta",
     38    "RELEASE_VERSION": "124.0b5",
     39    "SHIPPING_PRODUCT": "fennec",
     40    "TESTRAIL_PROJECT_ID": 59, # Fenix Browser
     41    "TESTRAIL_PRODUCT_TYPE": "Firefox"
     42 }
     43 
     44 send_success_notification(success_values, 'channel_id', taskcluster_options)
     45 
     46 values = {
     47        "timestamp": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
     48        "error_message": error_message,
     49 }
     50 
     51 send_error_notification(values, 'channel_id', taskcluster_options)
     52 """
     53 
     54 import json
     55 import os
     56 import time
     57 import traceback
     58 from string import Template
     59 
     60 import taskcluster
     61 
     62 SLACK_SUCCESS_MESSAGE_TEMPLATE = Template(
     63    """
     64 [
     65    {
     66        "type": "header",
     67        "text": {
     68                "type": "plain_text",
     69                "text": "New Release: :android: $PRODUCT_ICON $SHIPPING_PRODUCT-v$RELEASE_VERSION :star:"
     70        }
     71    },
     72    {
     73        "type": "divider"
     74    },
     75    {
     76        "type": "section",
     77        "text": {
     78            "type": "mrkdwn",
     79            "text": "*Testrail Release*: $TESTRAIL_PRODUCT_TYPE $RELEASE_TYPE $RELEASE_VERSION <https://mozilla.testrail.io/index.php?/projects/overview/$TESTRAIL_PROJECT_ID|Milestone> has been created:testrail:"
     80        }
     81    },
     82    {
     83        "type": "section",
     84        "text": {
     85            "type": "mrkdwn",
     86            "text": "*UI Automated Tests*:"
     87        }
     88    },
     89    {
     90        "type": "section",
     91        "text": {
     92            "type": "mrkdwn",
     93            "text": " :white_check_mark: Automated smoke test - Google Pixel 3(Android 11)"
     94        }
     95    },
     96    {
     97        "type": "section",
     98        "text": {
     99            "type": "mrkdwn",
    100            "text": ":white_check_mark: Automated smoke test - Google Pixel 2(Android 9)"
    101        }
    102    },
    103    {
    104        "type": "divider"
    105    },
    106    {
    107        "type": "context",
    108        "elements": [
    109            {
    110                "type": "mrkdwn",
    111                "text": ":testops-notify: created by <https://mozilla-hub.atlassian.net/wiki/spaces/MTE/overview|Mobile Test Engineering>"
    112            }
    113        ]
    114    }
    115 ]
    116 """
    117 )
    118 
    119 SLACK_ERROR_MESSAGE_TEMPLATE = Template(
    120    """
    121 [
    122    {
    123        "type": "section",
    124        "text": {
    125            "type": "mrkdwn",
    126            "text": "Failed to call TestRail API at $timestamp with error: $error_message"
    127        }
    128    }
    129 ]
    130 """
    131 )
    132 
    133 
    134 def send_slack_notification(template, values, channel_id, options):
    135    """
    136    Sends a Slack notification based on the provided template and values.
    137 
    138    :param template: Template object for the Slack message.
    139    :param values: Dictionary containing values to substitute in the template.
    140    :param channel_id: Slack channel ID to send the message to.
    141    :param options: Taskcluster options for the notification service.
    142    """
    143    slack_message = json.loads(template.safe_substitute(**values))
    144    # workaround for https://github.com/taskcluster/taskcluster/issues/6801
    145    duplicate_message_workaround = str(int(time.time()))
    146    payload = {
    147        "channelId": channel_id,
    148        "text": duplicate_message_workaround,
    149        "blocks": slack_message,
    150    }
    151 
    152    try:
    153        response = taskcluster.Notify(options).slack(payload)
    154        print("Response from API:", response)
    155    except Exception as e:
    156        print(f"Error sending Slack message: {e}")
    157        traceback.print_exc()
    158 
    159        if hasattr(e, "response"):
    160            print("Response content:", e.response.text)
    161 
    162 
    163 def get_taskcluster_options():
    164    """
    165    Retrieves the Taskcluster setup options according to the current environment.
    166 
    167    :return: A dictionary of Taskcluster options.
    168    """
    169    options = taskcluster.optionsFromEnvironment()
    170    proxy_url = os.environ.get("TASKCLUSTER_PROXY_URL")
    171 
    172    if proxy_url is not None:
    173        # Always use proxy url when available
    174        options["rootUrl"] = proxy_url
    175 
    176    if "rootUrl" not in options:
    177        # Always have a value in root url
    178        options["rootUrl"] = "https://community-tc.services.mozilla.com"
    179 
    180    return options
    181 
    182 
    183 def send_error_notification(error_message, channel_id, options):
    184    values = {
    185        "timestamp": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
    186        "error_message": error_message,
    187    }
    188    send_slack_notification(SLACK_ERROR_MESSAGE_TEMPLATE, values, channel_id, options)
    189 
    190 
    191 def send_success_notification(success_values, channel_id, options):
    192    send_slack_notification(
    193        SLACK_SUCCESS_MESSAGE_TEMPLATE, success_values, channel_id, options
    194    )
    195 
    196 
    197 def get_product_icon(product):
    198    product_map = {"fenix": ":firefox:", "focus": ":firefox_focus:"}
    199    return product_map.get(product.lower(), ":firefox:")