add ruff and pyright flake checks

This commit is contained in:
Abel Luck 2026-03-05 16:08:31 +01:00
parent 42cf3f75dc
commit 7c9b42fe56
8 changed files with 125 additions and 19 deletions

View file

@ -172,10 +172,53 @@
let let
system = pkgs.stdenv.hostPlatform.system; system = pkgs.stdenv.hostPlatform.system;
exportedPackage = self.packages.${system}.default; exportedPackage = self.packages.${system}.default;
testVenv = exportedPackage.testVenv;
src = ./.;
ruffCheck = pkgs.stdenv.mkDerivation {
name = "matrix-ops-bot-ruff";
inherit src;
dontConfigure = true;
dontBuild = true;
nativeBuildInputs = [ testVenv ];
checkPhase = ''
runHook preCheck
ruff check ops_bot/ tests/
ruff format --check ops_bot/ tests/
runHook postCheck
'';
doCheck = true;
installPhase = ''
mkdir -p "$out"
touch "$out/passed"
'';
};
pyrightCheck = pkgs.stdenv.mkDerivation {
name = "matrix-ops-bot-pyright";
inherit src;
dontConfigure = true;
dontBuild = true;
nativeBuildInputs = [
testVenv
pkgs.nodejs
];
checkPhase = ''
runHook preCheck
export HOME="$(mktemp -d)"
pyright ops_bot/
runHook postCheck
'';
doCheck = true;
installPhase = ''
mkdir -p "$out"
touch "$out/passed"
'';
};
in in
{ {
package-default = exportedPackage; package-default = exportedPackage;
tests = exportedPackage.tests; tests = exportedPackage.tests;
ruff = ruffCheck;
pyright = pyrightCheck;
} }
); );
@ -184,6 +227,8 @@
packages = [ packages = [
pkgs.python311 pkgs.python311
pkgs.uv pkgs.uv
pkgs.ruff
pkgs.pyright
]; ];
env.LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [ env.LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [
pkgs.stdenv.cc.cc pkgs.stdenv.cc.cc

View file

@ -1,6 +1,6 @@
import logging import logging
import re import re
from typing import Any, List, Optional, Tuple from typing import Any, List, Optional, Tuple, cast
import attr import attr
from fastapi import Request from fastapi import Request
@ -47,8 +47,9 @@ async def handle_event(x_gitlab_event: str, payload: Any) -> List[Tuple[str, str
nonlocal aborted nonlocal aborted
aborted = True aborted = True
action_fields = cast(Any, Action)
base_args = { base_args = {
**{field.key: field for field in Action if field.key.isupper()}, **{field.key: field for field in action_fields if field.key.isupper()},
**OTHER_ENUMS, **OTHER_ENUMS,
"util": TemplateUtil, "util": TemplateUtil,
} }

View file

@ -717,7 +717,7 @@ class GitlabPushEvent(SerializableAttrs, GitlabEvent):
def split_updates( def split_updates(
evt: Union["GitlabIssueEvent", "GitlabMergeRequestEvent"] evt: Union["GitlabIssueEvent", "GitlabMergeRequestEvent"],
) -> List[GitlabEvent]: ) -> List[GitlabEvent]:
if not evt.changes: if not evt.changes:
return [evt] return [evt]

View file

@ -14,7 +14,6 @@ from fastapi.security import (
HTTPBasicCredentials, HTTPBasicCredentials,
HTTPBearer, HTTPBearer,
) )
from prometheus_client import start_http_server
from prometheus_fastapi_instrumentator import Instrumentator from prometheus_fastapi_instrumentator import Instrumentator
from ops_bot import alertmanager, aws, pagerduty from ops_bot import alertmanager, aws, pagerduty
@ -101,8 +100,7 @@ class Authorizer(Protocol):
request: Request, request: Request,
basic_credentials: Optional[HTTPBasicCredentials], basic_credentials: Optional[HTTPBasicCredentials],
bearer_credentials: Optional[HTTPAuthorizationCredentials], bearer_credentials: Optional[HTTPAuthorizationCredentials],
) -> bool: ) -> bool: ...
...
class ParseHandler(Protocol): class ParseHandler(Protocol):
@ -111,8 +109,7 @@ class ParseHandler(Protocol):
route: RoutingKey, route: RoutingKey,
payload: Any, payload: Any,
request: Request, request: Request,
) -> List[Tuple[str, str]]: ) -> List[Tuple[str, str]]: ...
...
handlers: Dict[str, Tuple[Authorizer, ParseHandler]] = { handlers: Dict[str, Tuple[Authorizer, ParseHandler]] = {

View file

@ -21,9 +21,11 @@ class ClientCredentials(BaseModel):
class CredentialStorage(Protocol): class CredentialStorage(Protocol):
def save_config(self, config: ClientCredentials) -> None: def save_config(self, config: ClientCredentials) -> None:
"""Save config""" """Save config"""
...
def read_config(self) -> ClientCredentials: def read_config(self) -> ClientCredentials:
"""Load config""" """Load config"""
...
class LocalCredentialStore: class LocalCredentialStore:
@ -64,7 +66,7 @@ class MatrixClient:
self.store_path.joinpath("credentials.json") self.store_path.joinpath("credentials.json")
) )
self.client: AsyncClient = None self.client: Optional[AsyncClient] = None
self.client_config = AsyncClientConfig( self.client_config = AsyncClientConfig(
max_limit_exceeded=0, max_limit_exceeded=0,
max_timeouts=0, max_timeouts=0,
@ -79,15 +81,19 @@ class MatrixClient:
async def start(self) -> None: async def start(self) -> None:
await self.login() await self.login()
if self.client is None:
raise RuntimeError("Matrix client failed to initialize")
if self.client.should_upload_keys: client = self.client
await self.client.keys_upload()
if client.should_upload_keys:
await client.keys_upload()
for room in self.join_rooms: for room in self.join_rooms:
await self.client.join(room) await client.join(room)
await self.client.joined_rooms() await client.joined_rooms()
await self.client.sync_forever(timeout=300000, full_state=True) await client.sync_forever(timeout=300000, full_state=True)
def save_credentials(self, resp: LoginResponse, homeserver: str) -> None: def save_credentials(self, resp: LoginResponse, homeserver: str) -> None:
credentials = ClientCredentials( credentials = ClientCredentials(
@ -150,6 +156,9 @@ class MatrixClient:
message: str, message: str,
message_formatted: Optional[str] = None, message_formatted: Optional[str] = None,
) -> None: ) -> None:
if self.client is None:
raise RuntimeError("Matrix client failed to initialize")
content = { content = {
"msgtype": "m.text", "msgtype": "m.text",
"body": f"{message}", "body": f"{message}",
@ -168,4 +177,5 @@ class MatrixClient:
) )
async def shutdown(self) -> None: async def shutdown(self) -> None:
if self.client is not None:
await self.client.close() await self.client.close()

View file

@ -123,14 +123,14 @@ class PluginTemplateLoader(BaseLoader):
self.macros = sync_read_file(base_path / "macros.html") self.macros = sync_read_file(base_path / "macros.html")
def get_source( def get_source(
self, environment: Any, name: str self, environment: Any, template: str
) -> Tuple[str, str, Callable[[], bool]]: ) -> Tuple[str, str, Callable[[], bool]]:
path = self.directory / f"{name}.html" path = self.directory / f"{template}.html"
try: try:
tpl = sync_read_file(path) tpl = sync_read_file(path)
except KeyError: except KeyError:
raise TemplateNotFound(name) raise TemplateNotFound(template)
return self.macros + tpl, name, lambda: True return self.macros + tpl, template, lambda: True
def list_templates(self) -> List[str]: def list_templates(self) -> List[str]:
return [ return [

View file

@ -30,6 +30,8 @@ matrix-ops-bot = "ops_bot.main:main"
[dependency-groups] [dependency-groups]
dev = [ dev = [
"pytest>=7.2.0,<8.0.0", "pytest>=7.2.0,<8.0.0",
"ruff>=0.9.0,<1.0.0",
"pyright>=1.1.390,<2.0.0",
"black>=22.10.0,<23.0.0", "black>=22.10.0,<23.0.0",
"isort>=5.10.1,<6.0.0", "isort>=5.10.1,<6.0.0",
"mypy>=1.2.0,<2.0.0", "mypy>=1.2.0,<2.0.0",

51
uv.lock generated
View file

@ -813,8 +813,10 @@ dev = [
{ name = "flake8-black" }, { name = "flake8-black" },
{ name = "isort" }, { name = "isort" },
{ name = "mypy" }, { name = "mypy" },
{ name = "pyright" },
{ name = "pytest" }, { name = "pytest" },
{ name = "pytest-asyncio" }, { name = "pytest-asyncio" },
{ name = "ruff" },
{ name = "types-commonmark" }, { name = "types-commonmark" },
{ name = "types-markdown" }, { name = "types-markdown" },
{ name = "types-termcolor" }, { name = "types-termcolor" },
@ -846,8 +848,10 @@ dev = [
{ name = "flake8-black", specifier = ">=0.3.5,<0.4.0" }, { name = "flake8-black", specifier = ">=0.3.5,<0.4.0" },
{ name = "isort", specifier = ">=5.10.1,<6.0.0" }, { name = "isort", specifier = ">=5.10.1,<6.0.0" },
{ name = "mypy", specifier = ">=1.2.0,<2.0.0" }, { name = "mypy", specifier = ">=1.2.0,<2.0.0" },
{ name = "pyright", specifier = ">=1.1.390,<2.0.0" },
{ name = "pytest", specifier = ">=7.2.0,<8.0.0" }, { name = "pytest", specifier = ">=7.2.0,<8.0.0" },
{ name = "pytest-asyncio", specifier = ">=0.20.2,<0.21.0" }, { name = "pytest-asyncio", specifier = ">=0.20.2,<0.21.0" },
{ name = "ruff", specifier = ">=0.9.0,<1.0.0" },
{ name = "types-commonmark", specifier = ">=0.9.2,<0.10.0" }, { name = "types-commonmark", specifier = ">=0.9.2,<0.10.0" },
{ name = "types-markdown", specifier = ">=3.4.0,<4.0.0" }, { name = "types-markdown", specifier = ">=3.4.0,<4.0.0" },
{ name = "types-termcolor", specifier = ">=1.1.5,<2.0.0" }, { name = "types-termcolor", specifier = ">=1.1.5,<2.0.0" },
@ -1050,6 +1054,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
] ]
[[package]]
name = "nodeenv"
version = "1.10.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" },
]
[[package]] [[package]]
name = "packaging" name = "packaging"
version = "26.0" version = "26.0"
@ -1408,6 +1421,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
] ]
[[package]]
name = "pyright"
version = "1.1.408"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nodeenv" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/74/b2/5db700e52554b8f025faa9c3c624c59f1f6c8841ba81ab97641b54322f16/pyright-1.1.408.tar.gz", hash = "sha256:f28f2321f96852fa50b5829ea492f6adb0e6954568d1caa3f3af3a5f555eb684", size = 4400578, upload-time = "2026-01-08T08:07:38.795Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/82/a2c93e32800940d9573fb28c346772a14778b84ba7524e691b324620ab89/pyright-1.1.408-py3-none-any.whl", hash = "sha256:090b32865f4fdb1e0e6cd82bf5618480d48eecd2eb2e70f960982a3d9a4c17c1", size = 6399144, upload-time = "2026-01-08T08:07:37.082Z" },
]
[[package]] [[package]]
name = "pytest" name = "pytest"
version = "7.4.4" version = "7.4.4"
@ -1660,6 +1686,31 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" },
] ]
[[package]]
name = "ruff"
version = "0.15.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/da/31/d6e536cdebb6568ae75a7f00e4b4819ae0ad2640c3604c305a0428680b0c/ruff-0.15.4.tar.gz", hash = "sha256:3412195319e42d634470cc97aa9803d07e9d5c9223b99bcb1518f0c725f26ae1", size = 4569550, upload-time = "2026-02-26T20:04:14.959Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f2/82/c11a03cfec3a4d26a0ea1e571f0f44be5993b923f905eeddfc397c13d360/ruff-0.15.4-py3-none-linux_armv6l.whl", hash = "sha256:a1810931c41606c686bae8b5b9a8072adac2f611bb433c0ba476acba17a332e0", size = 10453333, upload-time = "2026-02-26T20:04:20.093Z" },
{ url = "https://files.pythonhosted.org/packages/ce/5d/6a1f271f6e31dffb31855996493641edc3eef8077b883eaf007a2f1c2976/ruff-0.15.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5a1632c66672b8b4d3e1d1782859e98d6e0b4e70829530666644286600a33992", size = 10853356, upload-time = "2026-02-26T20:04:05.808Z" },
{ url = "https://files.pythonhosted.org/packages/b1/d8/0fab9f8842b83b1a9c2bf81b85063f65e93fb512e60effa95b0be49bfc54/ruff-0.15.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a4386ba2cd6c0f4ff75252845906acc7c7c8e1ac567b7bc3d373686ac8c222ba", size = 10187434, upload-time = "2026-02-26T20:03:54.656Z" },
{ url = "https://files.pythonhosted.org/packages/85/cc/cc220fd9394eff5db8d94dec199eec56dd6c9f3651d8869d024867a91030/ruff-0.15.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2496488bdfd3732747558b6f95ae427ff066d1fcd054daf75f5a50674411e75", size = 10535456, upload-time = "2026-02-26T20:03:52.738Z" },
{ url = "https://files.pythonhosted.org/packages/fa/0f/bced38fa5cf24373ec767713c8e4cadc90247f3863605fb030e597878661/ruff-0.15.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3f1c4893841ff2d54cbda1b2860fa3260173df5ddd7b95d370186f8a5e66a4ac", size = 10287772, upload-time = "2026-02-26T20:04:08.138Z" },
{ url = "https://files.pythonhosted.org/packages/2b/90/58a1802d84fed15f8f281925b21ab3cecd813bde52a8ca033a4de8ab0e7a/ruff-0.15.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:820b8766bd65503b6c30aaa6331e8ef3a6e564f7999c844e9a547c40179e440a", size = 11049051, upload-time = "2026-02-26T20:04:03.53Z" },
{ url = "https://files.pythonhosted.org/packages/d2/ac/b7ad36703c35f3866584564dc15f12f91cb1a26a897dc2fd13d7cb3ae1af/ruff-0.15.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9fb74bab47139c1751f900f857fa503987253c3ef89129b24ed375e72873e85", size = 11890494, upload-time = "2026-02-26T20:04:10.497Z" },
{ url = "https://files.pythonhosted.org/packages/93/3d/3eb2f47a39a8b0da99faf9c54d3eb24720add1e886a5309d4d1be73a6380/ruff-0.15.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f80c98765949c518142b3a50a5db89343aa90f2c2bf7799de9986498ae6176db", size = 11326221, upload-time = "2026-02-26T20:04:12.84Z" },
{ url = "https://files.pythonhosted.org/packages/ff/90/bf134f4c1e5243e62690e09d63c55df948a74084c8ac3e48a88468314da6/ruff-0.15.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:451a2e224151729b3b6c9ffb36aed9091b2996fe4bdbd11f47e27d8f2e8888ec", size = 11168459, upload-time = "2026-02-26T20:04:00.969Z" },
{ url = "https://files.pythonhosted.org/packages/b5/e5/a64d27688789b06b5d55162aafc32059bb8c989c61a5139a36e1368285eb/ruff-0.15.4-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a8f157f2e583c513c4f5f896163a93198297371f34c04220daf40d133fdd4f7f", size = 11104366, upload-time = "2026-02-26T20:03:48.099Z" },
{ url = "https://files.pythonhosted.org/packages/f1/f6/32d1dcb66a2559763fc3027bdd65836cad9eb09d90f2ed6a63d8e9252b02/ruff-0.15.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:917cc68503357021f541e69b35361c99387cdbbf99bd0ea4aa6f28ca99ff5338", size = 10510887, upload-time = "2026-02-26T20:03:45.771Z" },
{ url = "https://files.pythonhosted.org/packages/ff/92/22d1ced50971c5b6433aed166fcef8c9343f567a94cf2b9d9089f6aa80fe/ruff-0.15.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e9737c8161da79fd7cfec19f1e35620375bd8b2a50c3e77fa3d2c16f574105cc", size = 10285939, upload-time = "2026-02-26T20:04:22.42Z" },
{ url = "https://files.pythonhosted.org/packages/e6/f4/7c20aec3143837641a02509a4668fb146a642fd1211846634edc17eb5563/ruff-0.15.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:291258c917539e18f6ba40482fe31d6f5ac023994ee11d7bdafd716f2aab8a68", size = 10765471, upload-time = "2026-02-26T20:03:58.924Z" },
{ url = "https://files.pythonhosted.org/packages/d0/09/6d2f7586f09a16120aebdff8f64d962d7c4348313c77ebb29c566cefc357/ruff-0.15.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3f83c45911da6f2cd5936c436cf86b9f09f09165f033a99dcf7477e34041cbc3", size = 11263382, upload-time = "2026-02-26T20:04:24.424Z" },
{ url = "https://files.pythonhosted.org/packages/1b/fa/2ef715a1cd329ef47c1a050e10dee91a9054b7ce2fcfdd6a06d139afb7ec/ruff-0.15.4-py3-none-win32.whl", hash = "sha256:65594a2d557d4ee9f02834fcdf0a28daa8b3b9f6cb2cb93846025a36db47ef22", size = 10506664, upload-time = "2026-02-26T20:03:50.56Z" },
{ url = "https://files.pythonhosted.org/packages/d0/a8/c688ef7e29983976820d18710f955751d9f4d4eb69df658af3d006e2ba3e/ruff-0.15.4-py3-none-win_amd64.whl", hash = "sha256:04196ad44f0df220c2ece5b0e959c2f37c777375ec744397d21d15b50a75264f", size = 11651048, upload-time = "2026-02-26T20:04:17.191Z" },
{ url = "https://files.pythonhosted.org/packages/3e/0a/9e1be9035b37448ce2e68c978f0591da94389ade5a5abafa4cf99985d1b2/ruff-0.15.4-py3-none-win_arm64.whl", hash = "sha256:60d5177e8cfc70e51b9c5fad936c634872a74209f934c1e79107d11787ad5453", size = 10966776, upload-time = "2026-02-26T20:03:56.908Z" },
]
[[package]] [[package]]
name = "sniffio" name = "sniffio"
version = "1.3.1" version = "1.3.1"