notification_service.py 8.33 KB
Newer Older
Lysandre Debut's avatar
Lysandre Debut committed
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
# Copyright 2020 The HuggingFace Team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import re
import sys

from slack_sdk import WebClient


def handle_test_results(test_results):
    expressions = test_results.split(" ")

    failed = 0
    success = 0

    # When the output is short enough, the output is surrounded by = signs: "== OUTPUT =="
    # When it is too long, those signs are not present.
    time_spent = expressions[-2] if "=" in expressions[-1] else expressions[-1]

    for i, expression in enumerate(expressions):
        if "failed" in expression:
            failed += int(expressions[i - 1])
        if "passed" in expression:
            success += int(expressions[i - 1])

    return failed, success, time_spent


Lysandre Debut's avatar
Lysandre Debut committed
41
def format_for_slack(total_results, results, scheduled: bool, title: str):
42
    print(total_results, results)
Lysandre Debut's avatar
Lysandre Debut committed
43
44
45
46
    header = {
        "type": "header",
        "text": {
            "type": "plain_text",
Lysandre Debut's avatar
Lysandre Debut committed
47
            "text": title,
Lysandre Debut's avatar
Lysandre Debut committed
48
49
50
51
            "emoji": True,
        },
    }

52
53
    if total_results["failed"] > 0:
        total = {
Lysandre Debut's avatar
Lysandre Debut committed
54
55
56
57
58
59
            "type": "section",
            "fields": [
                {"type": "mrkdwn", "text": f"*Failures:*\n{total_results['failed']} failures."},
                {"type": "mrkdwn", "text": f"*Passed:*\n{total_results['success']} tests passed."},
            ],
        }
60
61
    else:
        total = {
Lysandre Debut's avatar
Lysandre Debut committed
62
            "type": "section",
63
64
65
            "fields": [
                {"type": "mrkdwn", "text": "\n🌞 All tests passed."},
            ],
Lysandre Debut's avatar
Lysandre Debut committed
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
        }

    blocks = [header, total]

    if total_results["failed"] > 0:
        for key, result in results.items():
            print(key, result)
            blocks.append({"type": "header", "text": {"type": "plain_text", "text": key, "emoji": True}})
            blocks.append(
                {
                    "type": "section",
                    "fields": [
                        {
                            "type": "mrkdwn",
                            "text": f"*Results:*\n{result['failed']} failed, {result['success']} passed.",
                        },
                        {"type": "mrkdwn", "text": f"*Time spent:*\n{result['time_spent']}"},
                    ],
                }
            )
86
    elif not scheduled:
Lysandre Debut's avatar
Lysandre Debut committed
87
88
89
90
91
92
93
94
95
        for key, result in results.items():
            blocks.append(
                {"type": "section", "fields": [{"type": "mrkdwn", "text": f"*{key}*\n{result['time_spent']}."}]}
            )

    footer = {
        "type": "section",
        "text": {
            "type": "mrkdwn",
Lysandre Debut's avatar
Lysandre Debut committed
96
            "text": f"<https://github.com/huggingface/transformers/actions/runs/{os.environ['GITHUB_RUN_ID']}|View on GitHub>",
Lysandre Debut's avatar
Lysandre Debut committed
97
98
99
100
101
102
103
104
105
106
107
        },
    }

    blocks.append(footer)

    blocks = {"blocks": blocks}

    return blocks


if __name__ == "__main__":
Lysandre Debut's avatar
Lysandre Debut committed
108
109
110
111
112
113
114
    arguments = sys.argv[1:]

    if "scheduled" in arguments:
        arguments.remove("scheduled")
        scheduled = True
    else:
        scheduled = False
Lysandre Debut's avatar
Lysandre Debut committed
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135

    if scheduled:
        # The scheduled run has several artifacts for each job.
        file_paths = {
            "TF Single GPU": {
                "common": "run_all_tests_tf_gpu_test_reports/tests_tf_gpu_[].txt",
                "pipeline": "run_all_tests_tf_gpu_test_reports/tests_tf_pipeline_gpu_[].txt",
            },
            "Torch Single GPU": {
                "common": "run_all_tests_torch_gpu_test_reports/tests_torch_gpu_[].txt",
                "pipeline": "run_all_tests_torch_gpu_test_reports/tests_torch_pipeline_gpu_[].txt",
                "examples": "run_all_tests_torch_gpu_test_reports/examples_torch_gpu_[].txt",
            },
            "TF Multi GPU": {
                "common": "run_all_tests_tf_multi_gpu_test_reports/tests_tf_multi_gpu_[].txt",
                "pipeline": "run_all_tests_tf_multi_gpu_test_reports/tests_tf_pipeline_multi_gpu_[].txt",
            },
            "Torch Multi GPU": {
                "common": "run_all_tests_torch_multi_gpu_test_reports/tests_torch_multi_gpu_[].txt",
                "pipeline": "run_all_tests_torch_multi_gpu_test_reports/tests_torch_pipeline_multi_gpu_[].txt",
            },
Lysandre Debut's avatar
Lysandre Debut committed
136
137
138
139
140
141
            "Torch Cuda Extensions Single GPU": {
                "common": "run_tests_torch_cuda_extensions_gpu_test_reports/tests_torch_cuda_extensions_gpu_[].txt"
            },
            "Torch Cuda Extensions Multi GPU": {
                "common": "run_tests_torch_cuda_extensions_multi_gpu_test_reports/tests_torch_cuda_extensions_multi_gpu_[].txt"
            },
Lysandre Debut's avatar
Lysandre Debut committed
142
143
144
145
146
147
148
        }
    else:
        file_paths = {
            "TF Single GPU": {"common": "run_all_tests_tf_gpu_test_reports/tests_tf_gpu_[].txt"},
            "Torch Single GPU": {"common": "run_all_tests_torch_gpu_test_reports/tests_torch_gpu_[].txt"},
            "TF Multi GPU": {"common": "run_all_tests_tf_multi_gpu_test_reports/tests_tf_multi_gpu_[].txt"},
            "Torch Multi GPU": {"common": "run_all_tests_torch_multi_gpu_test_reports/tests_torch_multi_gpu_[].txt"},
Lysandre Debut's avatar
Lysandre Debut committed
149
150
151
152
153
154
            "Torch Cuda Extensions Single GPU": {
                "common": "run_tests_torch_cuda_extensions_gpu_test_reports/tests_torch_cuda_extensions_gpu_[].txt"
            },
            "Torch Cuda Extensions Multi GPU": {
                "common": "run_tests_torch_cuda_extensions_multi_gpu_test_reports/tests_torch_cuda_extensions_multi_gpu_[].txt"
            },
Lysandre Debut's avatar
Lysandre Debut committed
155
156
157
        }

    client = WebClient(token=os.environ["CI_SLACK_BOT_TOKEN"])
Lysandre Debut's avatar
Lysandre Debut committed
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172

    if not scheduled:
        channel_id = os.environ["CI_SLACK_CHANNEL_ID"]
    elif scheduled and len(arguments):
        channel_id = os.environ["CI_SLACK_CHANNEL_ID_PAST_FUTURE"]
    else:
        channel_id = os.environ["CI_SLACK_CHANNEL_ID_DAILY"]

    if scheduled:
        title = "🤗 Results of the scheduled tests."
    else:
        title = "🤗 Self-push results"

    if len(arguments):
        title = f"{arguments} " + title
Lysandre Debut's avatar
Lysandre Debut committed
173
174
175
176
177
178
179
180
181

    try:
        results = {}
        for job, file_dict in file_paths.items():

            # Single return value for failed/success across steps of a same job
            results[job] = {"failed": 0, "success": 0, "time_spent": "", "failures": ""}

            for key, file_path in file_dict.items():
Lysandre's avatar
Lysandre committed
182
183
184
185
186
187
188
189
190
191
192
193
                try:
                    with open(file_path.replace("[]", "stats")) as f:
                        failed, success, time_spent = handle_test_results(f.read())
                        results[job]["failed"] += failed
                        results[job]["success"] += success
                        results[job]["time_spent"] += time_spent[1:-1] + ", "
                    with open(file_path.replace("[]", "summary_short")) as f:
                        for line in f:
                            if re.search("FAILED", line):
                                results[job]["failures"] += line
                except FileNotFoundError:
                    print("Artifact was not found, job was probably canceled.")
Lysandre Debut's avatar
Lysandre Debut committed
194
195
196
197
198
199
200
201
202
203

            # Remove the trailing ", "
            results[job]["time_spent"] = results[job]["time_spent"][:-2]

        test_results_keys = ["failed", "success"]
        total = {"failed": 0, "success": 0}
        for job, job_result in results.items():
            for result_key in test_results_keys:
                total[result_key] += job_result[result_key]

204
        if total["failed"] != 0 or scheduled:
Lysandre Debut's avatar
Lysandre Debut committed
205
            to_be_sent_to_slack = format_for_slack(total, results, scheduled, title)
Lysandre Debut's avatar
Lysandre Debut committed
206

207
208
209
210
            result = client.chat_postMessage(
                channel=channel_id,
                blocks=to_be_sent_to_slack["blocks"],
            )
Lysandre Debut's avatar
Lysandre Debut committed
211
212
213
214
215
216
217
218
219
220

        for job, job_result in results.items():
            if len(job_result["failures"]):
                client.chat_postMessage(
                    channel=channel_id, text=f"{job}\n{job_result['failures']}", thread_ts=result["ts"]
                )

    except Exception as e:
        # Voluntarily catch every exception and send it to Slack.
        raise Exception(f"Setup error: no artifacts were found. Error: {e}") from e