commit
c09566cfba
|
@ -1,150 +0,0 @@
|
||||||
import base64
|
|
||||||
import re
|
|
||||||
import socket
|
|
||||||
import ssl
|
|
||||||
import threading
|
|
||||||
from typing import Any, Generator, Optional
|
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
from buildbot.reporters.base import ReporterBase
|
|
||||||
from buildbot.reporters.generators.build import BuildStatusGenerator
|
|
||||||
from buildbot.reporters.message import MessageFormatter
|
|
||||||
from twisted.internet import defer
|
|
||||||
|
|
||||||
DEBUG = False
|
|
||||||
|
|
||||||
|
|
||||||
def _irc_send(
|
|
||||||
server: str,
|
|
||||||
nick: str,
|
|
||||||
channel: str,
|
|
||||||
sasl_password: Optional[str] = None,
|
|
||||||
server_password: Optional[str] = None,
|
|
||||||
tls: bool = True,
|
|
||||||
port: int = 6697,
|
|
||||||
messages: list[str] = [],
|
|
||||||
) -> None:
|
|
||||||
if not messages:
|
|
||||||
return
|
|
||||||
|
|
||||||
# don't give a shit about legacy ip
|
|
||||||
sock = socket.socket(family=socket.AF_INET6)
|
|
||||||
if tls:
|
|
||||||
sock = ssl.wrap_socket(
|
|
||||||
sock, cert_reqs=ssl.CERT_NONE, ssl_version=ssl.PROTOCOL_TLSv1_2
|
|
||||||
)
|
|
||||||
|
|
||||||
def _send(command: str) -> int:
|
|
||||||
if DEBUG:
|
|
||||||
print(command)
|
|
||||||
return sock.send((f"{command}\r\n").encode())
|
|
||||||
|
|
||||||
def _pong(ping: str) -> None:
|
|
||||||
if ping.startswith("PING"):
|
|
||||||
sock.send(ping.replace("PING", "PONG").encode("ascii"))
|
|
||||||
|
|
||||||
recv_file = sock.makefile(mode="r")
|
|
||||||
|
|
||||||
print(f"connect {server}:{port}")
|
|
||||||
sock.connect((server, port))
|
|
||||||
if server_password:
|
|
||||||
_send(f"PASS {server_password}")
|
|
||||||
_send(f"USER {nick} 0 * :{nick}")
|
|
||||||
_send(f"NICK {nick}")
|
|
||||||
for line in recv_file.readline():
|
|
||||||
if re.match(r"^:[^ ]* (MODE|221|376|422) ", line):
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
_pong(line)
|
|
||||||
|
|
||||||
if sasl_password:
|
|
||||||
_send("CAP REQ :sasl")
|
|
||||||
_send("AUTHENTICATE PLAIN")
|
|
||||||
auth = base64.encodebytes(f"{nick}\0{nick}\0{sasl_password}".encode("ascii"))
|
|
||||||
_send(f"AUTHENTICATE {auth.decode('ascii')}")
|
|
||||||
_send("CAP END")
|
|
||||||
_send(f"JOIN :{channel}")
|
|
||||||
|
|
||||||
for m in messages:
|
|
||||||
_send(f"PRIVMSG {channel} :{m}")
|
|
||||||
|
|
||||||
_send("INFO")
|
|
||||||
for line in recv_file:
|
|
||||||
if DEBUG:
|
|
||||||
print(line, end="")
|
|
||||||
# Assume INFO reply means we are done
|
|
||||||
if "End of /INFO" in line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
_pong(line)
|
|
||||||
|
|
||||||
sock.send(b"QUIT")
|
|
||||||
print("disconnect")
|
|
||||||
sock.close()
|
|
||||||
|
|
||||||
|
|
||||||
def irc_send(
|
|
||||||
url: str, notifications: list[str], password: Optional[str] = None
|
|
||||||
) -> None:
|
|
||||||
parsed = urlparse(f"{url}")
|
|
||||||
username = parsed.username or "prometheus"
|
|
||||||
server = parsed.hostname or "chat.freenode.net"
|
|
||||||
if parsed.fragment != "":
|
|
||||||
channel = f"#{parsed.fragment}"
|
|
||||||
else:
|
|
||||||
channel = "#krebs-announce"
|
|
||||||
port = parsed.port or 6697
|
|
||||||
if not password:
|
|
||||||
password = parsed.password
|
|
||||||
if len(notifications) == 0:
|
|
||||||
return
|
|
||||||
# put this in a thread to not block buildbot
|
|
||||||
t = threading.Thread(
|
|
||||||
target=_irc_send,
|
|
||||||
kwargs=dict(
|
|
||||||
server=server,
|
|
||||||
nick=username,
|
|
||||||
sasl_password=password,
|
|
||||||
channel=channel,
|
|
||||||
port=port,
|
|
||||||
messages=notifications,
|
|
||||||
tls=parsed.scheme == "irc+tls",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
t.start()
|
|
||||||
|
|
||||||
|
|
||||||
subject_template = """\
|
|
||||||
{{ '☠' if result_names[results] == 'failure' else '☺' if result_names[results] == 'success' else '☝' }} \
|
|
||||||
{{ build['properties'].get('project', ['whole buildset'])[0] if is_buildset else buildername }} \
|
|
||||||
- \
|
|
||||||
{{ build['state_string'] }} \
|
|
||||||
{{ '(%s)' % (build['properties']['branch'][0] if (build['properties']['branch'] and build['properties']['branch'][0]) else build['properties'].get('got_revision', ['(unknown revision)'])[0]) }} \
|
|
||||||
({{ build_url }})
|
|
||||||
""" # # noqa pylint: disable=line-too-long
|
|
||||||
|
|
||||||
|
|
||||||
class NotifyFailedBuilds(ReporterBase):
|
|
||||||
def _generators(self) -> list[BuildStatusGenerator]:
|
|
||||||
formatter = MessageFormatter(template_type="plain", subject=subject_template)
|
|
||||||
return [BuildStatusGenerator(message_formatter=formatter)]
|
|
||||||
|
|
||||||
def checkConfig(self, url: str) -> None:
|
|
||||||
super().checkConfig(generators=self._generators())
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def reconfigService(self, url: str) -> Generator[Any, object, Any]:
|
|
||||||
self.url = url
|
|
||||||
yield super().reconfigService(generators=self._generators())
|
|
||||||
|
|
||||||
def sendMessage(self, reports: list) -> None:
|
|
||||||
msgs = []
|
|
||||||
for r in reports:
|
|
||||||
build = r["builds"][0]
|
|
||||||
buildername = build["builder"]["name"]
|
|
||||||
# We don't want to report individual failures here to not spam the channel.
|
|
||||||
if buildername != "nix-eval":
|
|
||||||
continue
|
|
||||||
if build["state_string"] != "build successful":
|
|
||||||
msgs.append(r["subject"])
|
|
||||||
irc_send(self.url, notifications=msgs)
|
|
|
@ -20,7 +20,6 @@ from buildbot_nix import ( # noqa: E402
|
||||||
nix_update_flake_config,
|
nix_update_flake_config,
|
||||||
)
|
)
|
||||||
from github_projects import GithubProject, load_projects # noqa: E402
|
from github_projects import GithubProject, load_projects # noqa: E402
|
||||||
from irc_notify import NotifyFailedBuilds # noqa: E402
|
|
||||||
|
|
||||||
|
|
||||||
def read_secret_file(secret_name: str) -> str:
|
def read_secret_file(secret_name: str) -> str:
|
||||||
|
@ -192,9 +191,7 @@ def build_config() -> dict[str, Any]:
|
||||||
# we use `virtual_builder_name` in the webinterface
|
# we use `virtual_builder_name` in the webinterface
|
||||||
# so that we distinguish what has beeing build
|
# so that we distinguish what has beeing build
|
||||||
context=Interpolate("buildbot/%(prop:status_name)s"),
|
context=Interpolate("buildbot/%(prop:status_name)s"),
|
||||||
),
|
)
|
||||||
# Notify on irc
|
|
||||||
NotifyFailedBuilds("irc://buildbot|mic92@irc.r:6667/#xxx"),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
systemd_secrets = secrets.SecretInAFile(dirname=credentials)
|
systemd_secrets = secrets.SecretInAFile(dirname=credentials)
|
||||||
|
|
Loading…
Reference in a new issue