summaryrefslogtreecommitdiffstats
path: root/taskcluster/scripts/slack_notifier.py
blob: c4377f0ed08aad53bb82d7db5bc554e84c5c8c40 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#!/usr/bin/env python3

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
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.

Key Features:
- 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.
- SLACK_ERROR_MESSAGE_TEMPLATE: A template for error messages, used to notify about failures or issues in automated processes, particularly with TestRail API interactions.
- 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.
- get_taskcluster_options: Retrieves configuration options for Taskcluster based on the current runtime environment, ensuring appropriate setup for notification delivery.
- send_error_notification: A higher-level function that formats and sends error notifications to a specified Slack channel.
- send_success_notification: Similarly, this function sends success notifications to a specified Slack channel, using the success message template.

Usage:
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.

Required Values for Notifications:

These values are required when calling the `send_success_notification` and `send_slack_notification` functions.
They must be passed as an object with the following keys and their respective values.

Required Keys and Expected Values:
- RELEASE_TYPE: <string> Release Type or Stage (e.g., Alpha, Beta, RC).
- RELEASE_VERSION: <string> Release Version from versions.txt (e.g., '124.0b5').
- SHIPPING_PRODUCT: <string> Release Tag Name (e.g., fennec, focus).
- TESTRAIL_PROJECT_ID: <int> Project ID for TestRail Project (e.g., Fenix Browser).
- TESTRAIL_PRODUCT_TYPE: <string> Name for the official release product (e.g., Firefox, not fennec).

These values are used as arguments for `success_values` and `values` when calling the respective functions.

Example Usage:

success_values = {
    "RELEASE_TYPE": "Beta",
    "RELEASE_VERSION": "124.0b5",
    "SHIPPING_PRODUCT": "fennec",
    "TESTRAIL_PROJECT_ID": 59, # Fenix Browser
    "TESTRAIL_PRODUCT_TYPE": "Firefox"
}

send_success_notification(success_values, 'channel_id', taskcluster_options)

values = {
        "timestamp": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
        "error_message": error_message,
}

send_error_notification(values, 'channel_id', taskcluster_options)
"""

import json
import os
import time
import traceback
from string import Template

import taskcluster

SLACK_SUCCESS_MESSAGE_TEMPLATE = Template(
    """
[
    {
        "type": "header",
        "text": {
                "type": "plain_text",
                "text": "New Release: :firefox: $SHIPPING_PRODUCT-v$RELEASE_VERSION :star:"
        }
    },
    {
        "type": "divider"
    },
    {
        "type": "section",
        "text": {
            "type": "mrkdwn",
            "text": "*Testrail Release*: $TESTRAIL_PRODUCT_TYPE $RELEASE_TYPE $RELEASE_VERSION <https://testrail.stage.mozaws.net/index.php?/projects/overview/$TESTRAIL_PROJECT_ID|Milestone> has been created:testrail:"
        }
    },
    {
        "type": "section",
        "text": {
            "type": "mrkdwn",
            "text": "*UI Automated Tests*:"
        }
    },
    {
        "type": "section",
        "text": {
            "type": "mrkdwn",
            "text": " :white_check_mark: Automated smoke test - Google Pixel 3(Android 11)"
        }
    },
    {
        "type": "section",
        "text": {
            "type": "mrkdwn",
            "text": ":white_check_mark: Automated smoke test - Google Pixel 2(Android 9)"
        }
    },
    {
        "type": "divider"
    },
    {
        "type": "context",
        "elements": [
            {
                "type": "mrkdwn",
                "text": ":testops-notify: created by <https://mozilla-hub.atlassian.net/wiki/spaces/MTE/overview|Mobile Test Engineering>"
            }
        ]
    }
]
"""
)

SLACK_ERROR_MESSAGE_TEMPLATE = Template(
    """
[
    {
        "type": "section",
        "text": {
            "type": "mrkdwn",
            "text": "Failed to call TestRail API at $timestamp with error: $error_message"
        }
    }
]
"""
)


def send_slack_notification(template, values, channel_id, options):
    """
    Sends a Slack notification based on the provided template and values.

    :param template: Template object for the Slack message.
    :param values: Dictionary containing values to substitute in the template.
    :param channel_id: Slack channel ID to send the message to.
    :param options: Taskcluster options for the notification service.
    """
    slack_message = json.loads(template.safe_substitute(**values))
    # workaround for https://github.com/taskcluster/taskcluster/issues/6801
    duplicate_message_workaround = str(int(time.time()))
    payload = {
        "channelId": channel_id,
        "text": duplicate_message_workaround,
        "blocks": slack_message,
    }

    try:
        response = taskcluster.Notify(options).slack(payload)
        print("Response from API:", response)
    except Exception as e:
        print(f"Error sending Slack message: {e}")
        traceback.print_exc()

        if hasattr(e, "response"):
            print("Response content:", e.response.text)


def get_taskcluster_options():
    """
    Retrieves the Taskcluster setup options according to the current environment.

    :return: A dictionary of Taskcluster options.
    """
    options = taskcluster.optionsFromEnvironment()
    proxy_url = os.environ.get("TASKCLUSTER_PROXY_URL")

    if proxy_url is not None:
        # Always use proxy url when available
        options["rootUrl"] = proxy_url

    if "rootUrl" not in options:
        # Always have a value in root url
        options["rootUrl"] = "https://community-tc.services.mozilla.com"

    return options


def send_error_notification(error_message, channel_id, options):
    values = {
        "timestamp": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
        "error_message": error_message,
    }
    send_slack_notification(SLACK_ERROR_MESSAGE_TEMPLATE, values, channel_id, options)


def send_success_notification(success_values, channel_id, options):
    send_slack_notification(
        SLACK_SUCCESS_MESSAGE_TEMPLATE, success_values, channel_id, options
    )