ci: wine-apitrace bump to 11.1
[platform/upstream/mesa.git] / .gitlab-ci / report-flakes.py
1 #!/usr/bin/env python3
2 #
3 # Copyright © 2021 Google LLC
4 #
5 # Permission is hereby granted, free of charge, to any person obtaining a
6 # copy of this software and associated documentation files (the "Software"),
7 # to deal in the Software without restriction, including without limitation
8 # the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 # and/or sell copies of the Software, and to permit persons to whom the
10 # Software is furnished to do so, subject to the following conditions:
11 #
12 # The above copyright notice and this permission notice (including the next
13 # paragraph) shall be included in all copies or substantial portions of the
14 # Software.
15 #
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 # IN THE SOFTWARE.
23
24 import argparse
25 import io
26 import re
27 import socket
28 import time
29
30
31 class Connection:
32     def __init__(self, host, port, verbose):
33         self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
34         self.s.connect((host, port))
35         self.s.setblocking(0)
36         self.verbose = verbose
37
38     def send_line(self, line):
39         if self.verbose:
40             print(f"IRC: sending {line}")
41         self.s.sendall((line + '\n').encode())
42
43     def wait(self, secs):
44         for i in range(secs):
45             if self.verbose:
46                 while True:
47                     try:
48                         data = self.s.recv(1024)
49                     except io.BlockingIOError:
50                         break
51                     if data == "":
52                         break
53                     for line in data.decode().split('\n'):
54                         print(f"IRC: received {line}")
55             time.sleep(1)
56
57     def quit(self):
58         self.send_line("QUIT")
59         self.s.shutdown(socket.SHUT_WR)
60         self.s.close()
61
62
63 def read_flakes(results):
64     flakes = []
65     csv = re.compile("(.*),(.*),(.*)")
66     for line in open(results, 'r').readlines():
67         match = csv.match(line)
68         if match.group(2) == "Flake":
69             flakes.append(match.group(1))
70     return flakes
71
72 def main():
73     parser = argparse.ArgumentParser()
74     parser.add_argument('--host', type=str,
75                         help='IRC server hostname', required=True)
76     parser.add_argument('--port', type=int,
77                         help='IRC server port', required=True)
78     parser.add_argument('--results', type=str,
79                         help='results.csv file from deqp-runner or piglit-runner', required=True)
80     parser.add_argument('--known-flakes', type=str,
81                         help='*-flakes.txt file passed to deqp-runner or piglit-runner', required=True)
82     parser.add_argument('--channel', type=str,
83                         help='Known flakes report channel', required=True)
84     parser.add_argument('--url', type=str,
85                         help='$CI_JOB_URL', required=True)
86     parser.add_argument('--runner', type=str,
87                         help='$CI_RUNNER_DESCRIPTION', required=True)
88     parser.add_argument('--branch', type=str,
89                         help='optional branch name')
90     parser.add_argument('--branch-title', type=str,
91                         help='optional branch title')
92     parser.add_argument('--job', type=str,
93                         help='$CI_JOB_ID', required=True)
94     parser.add_argument('--verbose', "-v", action="store_true",
95                         help='log IRC interactions')
96     args = parser.parse_args()
97
98     flakes = read_flakes(args.results)
99     if not flakes:
100         exit(0)
101
102     known_flakes = []
103     for line in open(args.known_flakes).readlines():
104         line = line.strip()
105         if not line or line.startswith("#"):
106             continue
107         known_flakes.append(re.compile(line))
108
109     irc = Connection(args.host, args.port, args.verbose)
110
111     # The nick needs to be something unique so that multiple runners
112     # connecting at the same time don't race for one nick and get blocked.
113     # freenode has a 16-char limit on nicks (9 is the IETF standard, but
114     # various servers extend that).  So, trim off the common prefixes of the
115     # runner name, and append the job ID so that software runners with more
116     # than one concurrent job (think swrast) don't collide.  For freedreno,
117     # that gives us a nick as long as db410c-N-JJJJJJJJ, and it'll be a while
118     # before we make it to 9-digit jobs (we're at 7 so far).
119     nick = args.runner
120     nick = nick.replace('mesa-', '')
121     nick = nick.replace('google-freedreno-', '')
122     nick += f'-{args.job}'
123     irc.send_line(f"NICK {nick}")
124     irc.send_line(f"USER {nick} unused unused: Gitlab CI Notifier")
125     irc.wait(10)
126     irc.send_line(f"JOIN {args.channel}")
127     irc.wait(1)
128
129     branchinfo = ""
130     if args.branch:
131         branchinfo = f" on branch {args.branch} ({args.branch_title})"
132     irc.send_line(
133         f"PRIVMSG {args.channel} :Flakes detected in job {args.url} on {args.runner}{branchinfo}:")
134
135     for flake in flakes:
136         status = "NEW "
137         for known in known_flakes:
138             if known.match(flake):
139                 status = ""
140                 break
141
142         irc.send_line(f"PRIVMSG {args.channel} :{status}{flake}")
143
144     irc.send_line(
145         f"PRIVMSG {args.channel} :See {args.url}/artifacts/browse/results/")
146
147     irc.quit()
148
149
150 if __name__ == '__main__':
151     main()