Remove legacy container and GitLab scaffolding and add ruff and pyright checks.
This commit is contained in:
parent
6870186009
commit
fac0de33e6
14 changed files with 118 additions and 310 deletions
6
.envrc
6
.envrc
|
|
@ -1,3 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
if [[ $(type -t use_flake) != function ]]; then
|
||||
echo "ERROR: direnv's use_flake function missing. update direnv to v2.30.0 or later." && exit 1
|
||||
fi
|
||||
export TAILSCALESD_ENV_FILE=./dev.env
|
||||
use nix
|
||||
use flake
|
||||
dotenv
|
||||
|
|
|
|||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -9,3 +9,5 @@ build
|
|||
htmlcov
|
||||
.direnv/
|
||||
*.env
|
||||
.env*
|
||||
!.envrc
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
image: python:3.11-bookworm
|
||||
|
||||
stages:
|
||||
- test
|
||||
- build
|
||||
test:
|
||||
image: python:3.11-bookworm
|
||||
stage: test
|
||||
variables:
|
||||
TAILSCALESD_API_KEY: tskey-dummy
|
||||
TAILSCALESD_TAILNET: example.com
|
||||
script:
|
||||
- apt-get update
|
||||
- apt-get install -y make
|
||||
- pip install uv
|
||||
- uv sync --frozen --all-groups
|
||||
- make check
|
||||
|
||||
docker-build:
|
||||
image: docker:latest
|
||||
stage: build
|
||||
services:
|
||||
- docker:dind
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
before_script:
|
||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
||||
# Default branch leaves tag empty (= latest tag)
|
||||
# All other branches are tagged with the escaped branch name (commit ref slug)
|
||||
script:
|
||||
- |
|
||||
if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then
|
||||
tag=""
|
||||
echo "Running on default branch '$CI_DEFAULT_BRANCH': tag = 'latest'"
|
||||
else
|
||||
tag=":$CI_COMMIT_REF_SLUG"
|
||||
echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag"
|
||||
fi
|
||||
- docker build --build-arg=APP_VERSION=$CI_COMMIT_REF_SLUG -f docker/Dockerfile --pull -t "$CI_REGISTRY_IMAGE${tag}" .
|
||||
- docker push "$CI_REGISTRY_IMAGE${tag}"
|
||||
# Run this job in a branch where a Dockerfile exists
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
exists:
|
||||
- docker/Dockerfile
|
||||
11
Makefile
11
Makefile
|
|
@ -2,17 +2,6 @@ UV ?= uv run
|
|||
SRC := tailscalesd
|
||||
TESTS := tests
|
||||
SHELL := $(shell which bash)
|
||||
APP_VERSION := $(shell git rev-parse --short HEAD)
|
||||
DOCKER ?= docker
|
||||
|
||||
docker-build:
|
||||
DOCKER_BUILDKIT=1 $(DOCKER) build -f docker/Dockerfile \
|
||||
--build-arg=$(APP_VERSION) \
|
||||
-t tailscalesd:latest \
|
||||
.
|
||||
|
||||
freeze:
|
||||
uv export --format requirements-txt --no-hashes --no-emit-project > requirements.frozen.txt
|
||||
|
||||
fmt:
|
||||
$(UV) black $(SRC)
|
||||
|
|
|
|||
25
README.md
25
README.md
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
> Service discovery for tailscale
|
||||
|
||||
Repository: https://guardianproject.dev/ops/tailscalesd
|
||||
|
||||
Returns a list of services for Prometheus to scrape, with some extra smarts for Guardian Project Ops.
|
||||
|
||||
See [the prometheus docs][0] for more information on the HTTP service discovery format.
|
||||
|
|
@ -47,22 +49,17 @@ See [the prometheus docs][0] for more information on the HTTP service discovery
|
|||
|
||||
## Usage
|
||||
|
||||
### Deploy with a container
|
||||
### Deploy on NixOS
|
||||
|
||||
* Container image: `registry.gitlab.com/guardianproject-ops/tailscalesd:VERSION`
|
||||
* See versions at: https://gitlab.com/guardianproject-ops/tailscalesd/container_registry/5749401
|
||||
Use the exported NixOS module from this repository:
|
||||
|
||||
``` shell
|
||||
docker run \
|
||||
--name=tailscalesd \
|
||||
--rm \
|
||||
--cap-drop=all \
|
||||
--user 1000:1000 \
|
||||
--publish 127.0.0.1:9242:9242 \
|
||||
--env TAILSCALESD_ENV_FILE=/tailscalesd.env \
|
||||
--mount type=bind,dst=/tailscalesd.env,ro=true,src=/path/to/tailscalesd.env \
|
||||
registry.gitlab.com/guardianproject-ops/tailscalesd:latest
|
||||
```
|
||||
- `nixosModules.tailscalesd`
|
||||
|
||||
Provide secrets via systemd credentials (`LoadCredential`) through:
|
||||
|
||||
- `services.tailscalesd.credentials.bearerTokenFile`
|
||||
- `services.tailscalesd.credentials.clientIdFile`
|
||||
- `services.tailscalesd.credentials.clientSecretFile`
|
||||
|
||||
### Configuration
|
||||
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
FROM docker.io/library/python:3.11-slim
|
||||
RUN set -ex; \
|
||||
export DEBIAN_FRONTEND=noninteractive; \
|
||||
apt-get update; \
|
||||
apt-get install --no-install-recommends -y \
|
||||
curl \
|
||||
; \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# UID and GID might be read-only values, so use non-conflicting ones
|
||||
ARG CONTAINER_UID="${CONTAINER_UID:-1000}"
|
||||
ARG CONTAINER_GID="${CONTAINER_GID:-1000}"
|
||||
ENV APP="tailscalesd"
|
||||
ENV APP_BASE="/srv"
|
||||
RUN set -ex; \
|
||||
groupadd -r -g ${CONTAINER_GID} ${APP}; \
|
||||
useradd --no-log-init -r -u ${CONTAINER_UID} -g ${APP} ${APP}; \
|
||||
mkdir -p /home/${APP} && chown -R ${APP}. /home/${APP}; \
|
||||
mkdir -p ${APP_BASE}/${APP}; \
|
||||
chown ${APP}. ${APP_BASE}/${APP};
|
||||
|
||||
COPY --chmod=0774 docker/entrypoint.sh /entrypoint.sh
|
||||
USER ${APP}
|
||||
|
||||
WORKDIR ${APP_BASE}/${APP}
|
||||
COPY --chown=${APP}:${APP} requirements.frozen.txt requirements.frozen.txt
|
||||
RUN pip3 install -r requirements.frozen.txt
|
||||
COPY --chown=${APP}:${APP} tailscalesd tailscalesd
|
||||
|
||||
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
|
||||
USER ${APP}
|
||||
ARG APP_VERSION
|
||||
ENV APP_VERSION="${APP_VERSION}"
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -e;
|
||||
|
||||
cd /srv/tailscalesd;
|
||||
exec /usr/bin/env python3 -m tailscalesd.main "$@"
|
||||
41
flake.nix
41
flake.nix
|
|
@ -152,6 +152,47 @@
|
|||
touch $out/passed
|
||||
'';
|
||||
};
|
||||
|
||||
tailscalesd-ruff = pkgs.stdenv.mkDerivation {
|
||||
name = "tailscalesd-ruff";
|
||||
inherit src;
|
||||
dontConfigure = true;
|
||||
dontBuild = true;
|
||||
nativeBuildInputs = [ testEnv ];
|
||||
checkPhase = ''
|
||||
runHook preCheck
|
||||
ruff check tailscalesd/ tests/
|
||||
ruff format --check tailscalesd/ tests/
|
||||
runHook postCheck
|
||||
'';
|
||||
doCheck = true;
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
touch $out/passed
|
||||
'';
|
||||
};
|
||||
|
||||
tailscalesd-pyright = pkgs.stdenv.mkDerivation {
|
||||
name = "tailscalesd-pyright";
|
||||
inherit src;
|
||||
dontConfigure = true;
|
||||
dontBuild = true;
|
||||
nativeBuildInputs = [
|
||||
testEnv
|
||||
pkgs.nodejs
|
||||
];
|
||||
checkPhase = ''
|
||||
runHook preCheck
|
||||
export HOME=$(mktemp -d)
|
||||
pyright tailscalesd/ tests/
|
||||
runHook postCheck
|
||||
'';
|
||||
doCheck = true;
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
touch $out/passed
|
||||
'';
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ dependencies = [
|
|||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://gitlab.com/guardianproject-ops/tailscalesd"
|
||||
Repository = "https://gitlab.com/guardianproject-ops/tailscalesd"
|
||||
Homepage = "https://guardianproject.dev/ops/tailscalesd"
|
||||
Repository = "https://guardianproject.dev/ops/tailscalesd"
|
||||
|
||||
[project.scripts]
|
||||
tailscalesd = "tailscalesd.main:main"
|
||||
|
|
@ -30,6 +30,8 @@ dev = [
|
|||
"pytest-asyncio",
|
||||
"flake8",
|
||||
"black",
|
||||
"ruff",
|
||||
"pyright",
|
||||
"bandit",
|
||||
"isort",
|
||||
"mypy>=1.2.0",
|
||||
|
|
|
|||
|
|
@ -1,159 +0,0 @@
|
|||
# This file was autogenerated by uv via the following command:
|
||||
# uv export --format requirements-txt --no-hashes --no-emit-project
|
||||
aiohappyeyeballs==2.6.1
|
||||
# via aiohttp
|
||||
aiohttp==3.13.3
|
||||
# via tailscale
|
||||
aiosignal==1.4.0
|
||||
# via aiohttp
|
||||
annotated-types==0.7.0
|
||||
# via pydantic
|
||||
anyio==3.7.1
|
||||
# via
|
||||
# fastapi
|
||||
# httpx
|
||||
# starlette
|
||||
attrs==25.4.0
|
||||
# via aiohttp
|
||||
bandit==1.9.4
|
||||
black==26.1.0
|
||||
certifi==2026.2.25
|
||||
# via
|
||||
# httpcore
|
||||
# httpx
|
||||
click==8.3.1
|
||||
# via
|
||||
# black
|
||||
# uvicorn
|
||||
colorama==0.4.6 ; sys_platform == 'win32'
|
||||
# via
|
||||
# bandit
|
||||
# click
|
||||
# pytest
|
||||
fastapi==0.104.1
|
||||
# via
|
||||
# prometheus-fastapi-instrumentator
|
||||
# tailscalesd
|
||||
flake8==7.3.0
|
||||
frozenlist==1.8.0
|
||||
# via
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
h11==0.16.0
|
||||
# via
|
||||
# httpcore
|
||||
# uvicorn
|
||||
httpcore==1.0.9
|
||||
# via httpx
|
||||
httpx==0.25.2
|
||||
# via tailscalesd
|
||||
idna==3.11
|
||||
# via
|
||||
# anyio
|
||||
# httpx
|
||||
# yarl
|
||||
iniconfig==2.3.0
|
||||
# via pytest
|
||||
isort==8.0.1
|
||||
json-logging==1.5.1
|
||||
# via tailscalesd
|
||||
librt==0.8.1 ; platform_python_implementation != 'PyPy'
|
||||
# via mypy
|
||||
markdown-it-py==4.0.0
|
||||
# via rich
|
||||
mashumaro==3.20
|
||||
# via tailscale
|
||||
mccabe==0.7.0
|
||||
# via flake8
|
||||
mdurl==0.1.2
|
||||
# via markdown-it-py
|
||||
multidict==6.7.1
|
||||
# via
|
||||
# aiohttp
|
||||
# yarl
|
||||
mypy==1.19.1
|
||||
mypy-extensions==1.1.0
|
||||
# via
|
||||
# black
|
||||
# mypy
|
||||
orjson==3.11.7
|
||||
# via tailscale
|
||||
packaging==26.0
|
||||
# via
|
||||
# black
|
||||
# pytest
|
||||
pathspec==1.0.4
|
||||
# via
|
||||
# black
|
||||
# mypy
|
||||
platformdirs==4.9.2
|
||||
# via black
|
||||
pluggy==1.6.0
|
||||
# via pytest
|
||||
prometheus-client==0.18.0
|
||||
# via
|
||||
# prometheus-fastapi-instrumentator
|
||||
# tailscalesd
|
||||
prometheus-fastapi-instrumentator==6.1.0
|
||||
# via tailscalesd
|
||||
propcache==0.4.1
|
||||
# via
|
||||
# aiohttp
|
||||
# yarl
|
||||
pycodestyle==2.14.0
|
||||
# via flake8
|
||||
pydantic==2.12.5
|
||||
# via
|
||||
# fastapi
|
||||
# pydantic-settings
|
||||
pydantic-core==2.41.5
|
||||
# via pydantic
|
||||
pydantic-settings==2.13.1
|
||||
# via tailscalesd
|
||||
pyflakes==3.4.0
|
||||
# via flake8
|
||||
pygments==2.19.2
|
||||
# via
|
||||
# pytest
|
||||
# rich
|
||||
pytest==9.0.2
|
||||
# via pytest-asyncio
|
||||
pytest-asyncio==1.3.0
|
||||
python-dotenv==1.2.2
|
||||
# via pydantic-settings
|
||||
pytokens==0.4.1
|
||||
# via black
|
||||
pyyaml==6.0.3
|
||||
# via bandit
|
||||
rich==14.3.3
|
||||
# via bandit
|
||||
sniffio==1.3.1
|
||||
# via
|
||||
# anyio
|
||||
# httpx
|
||||
starlette==0.27.0
|
||||
# via fastapi
|
||||
stevedore==5.7.0
|
||||
# via bandit
|
||||
tailscale==0.6.2
|
||||
# via tailscalesd
|
||||
typing-extensions==4.15.0
|
||||
# via
|
||||
# aiosignal
|
||||
# fastapi
|
||||
# mashumaro
|
||||
# mypy
|
||||
# pydantic
|
||||
# pydantic-core
|
||||
# pytest-asyncio
|
||||
# typing-inspection
|
||||
typing-inspection==0.4.2
|
||||
# via
|
||||
# pydantic
|
||||
# pydantic-settings
|
||||
uvicorn==0.24.0.post1
|
||||
# via tailscalesd
|
||||
yarl==1.23.0
|
||||
# via
|
||||
# aiohttp
|
||||
# tailscale
|
||||
31
shell.nix
31
shell.nix
|
|
@ -1,31 +0,0 @@
|
|||
{
|
||||
system ? "x86_64-linux",
|
||||
pkgs ? import <nixpkgs> { inherit system; },
|
||||
}:
|
||||
|
||||
let
|
||||
packages = [
|
||||
pkgs.python311
|
||||
pkgs.uv
|
||||
pkgs.zsh
|
||||
];
|
||||
|
||||
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [
|
||||
pkgs.stdenv.cc.cc
|
||||
# Add any missing library needed
|
||||
# You can use the nix-index package to locate them, e.g. nix-locate -w --top-level --at-root /lib/libudev.so.1
|
||||
];
|
||||
|
||||
# Keep the virtual environment in-repo for direnv usage.
|
||||
UV_PROJECT_ENVIRONMENT = ".venv";
|
||||
in
|
||||
pkgs.mkShell {
|
||||
buildInputs = packages;
|
||||
shellHook = ''
|
||||
export SHELL=${pkgs.zsh}
|
||||
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}"
|
||||
export UV_PROJECT_ENVIRONMENT="${UV_PROJECT_ENVIRONMENT}"
|
||||
export UV_PYTHON_DOWNLOADS=never
|
||||
export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring
|
||||
'';
|
||||
}
|
||||
|
|
@ -191,12 +191,6 @@ def namespace(device) -> Optional[str]:
|
|||
|
||||
|
||||
def tailscale_labels(tailnet, device, tag) -> Dict[str, str]:
|
||||
data_tags = {
|
||||
"tag:ns-": "namespace",
|
||||
"tag:tenant-": "tenant",
|
||||
"tag:stage-": "stage",
|
||||
"tag:opsTeam-": "opsTeam",
|
||||
}
|
||||
labels = {
|
||||
"__meta_tailscale_device_client_version": device["clientVersion"],
|
||||
"__meta_tailscale_device_hostname": device["hostname"],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
from fastapi.testclient import TestClient
|
||||
from pydantic import SecretStr
|
||||
from tailscalesd.main import Settings, app, get_settings
|
||||
|
||||
client = TestClient(app)
|
||||
|
|
@ -8,9 +9,9 @@ def get_settings_override() -> Settings:
|
|||
return Settings(
|
||||
test_mode=True,
|
||||
tailnet="test",
|
||||
client_id="test",
|
||||
client_secret="test",
|
||||
bearer_token="test",
|
||||
client_id=SecretStr("test"),
|
||||
client_secret=SecretStr("test"),
|
||||
bearer_token=SecretStr("test"),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
51
uv.lock
generated
51
uv.lock
generated
|
|
@ -727,6 +727,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" },
|
||||
]
|
||||
|
||||
[[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]]
|
||||
name = "orjson"
|
||||
version = "3.11.7"
|
||||
|
|
@ -1105,6 +1114,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" },
|
||||
]
|
||||
|
||||
[[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]]
|
||||
name = "pytest"
|
||||
version = "9.0.2"
|
||||
|
|
@ -1245,6 +1267,31 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" },
|
||||
]
|
||||
|
||||
[[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]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
|
|
@ -1312,8 +1359,10 @@ dev = [
|
|||
{ name = "flake8" },
|
||||
{ name = "isort" },
|
||||
{ name = "mypy" },
|
||||
{ name = "pyright" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "ruff" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
|
|
@ -1335,8 +1384,10 @@ dev = [
|
|||
{ name = "flake8" },
|
||||
{ name = "isort" },
|
||||
{ name = "mypy", specifier = ">=1.2.0" },
|
||||
{ name = "pyright" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "ruff" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue