Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
branches: [master]

env:
POETRY_VERSION: "2.3.0"
POETRY_VERSION: "2.5.1"
POETRY_VIRTUALENVS_IN_PROJECT: true

jobs:
Expand Down
12 changes: 9 additions & 3 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
FROM python:3.14-slim AS base
# Pin the exact patch release and Debian suite. A new tag invalidates the CI
# build cache, so the apt-get upgrade below runs again and picks up security fixes.
FROM python:3.14.9-slim-trixie AS base

RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends wget && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends ca-certificates wget && rm -rf /var/lib/apt/lists/*

FROM base AS builder

ENV PIP_DISABLE_PIP_VERSION_CHECK=on \
PYTHONUNBUFFERED=1 \
POETRY_VERSION=2.4.0 \
POETRY_VERSION=2.5.1 \
POETRY_HOME="/opt/poetry" \
POETRY_NO_INTERACTION=1 \
POETRY_VIRTUALENVS_IN_PROJECT=true \
POETRY_VIRTUALENVS_CREATE=true \
POETRY_VIRTUALENVS_OPTIONS_NO_PIP=true \
POETRY_CACHE_DIR=/tmp/poetry_cache

# Install build dependencies
Expand Down Expand Up @@ -43,6 +46,9 @@ ENV PIP_DISABLE_PIP_VERSION_CHECK=on \

WORKDIR /app

# pip is not used at runtime, and its vendored packages carry CVEs
RUN python -m pip uninstall -y pip

# Copy application and virtual environment from builder
COPY --from=builder /app ./

Expand Down
2,012 changes: 991 additions & 1,021 deletions poetry.lock

Large diffs are not rendered by default.

4 changes: 1 addition & 3 deletions pybot/endpoints/slack/message_templates/tech.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,7 @@ def _compile_regex_from_parts(self) -> Pattern[str]:

return re.compile(regex_string)

def _filter_matches(
self, lines: list[str], two_col_org_row: Pattern[str]
) -> Generator[dict, None, None]:
def _filter_matches(self, lines: list[str], two_col_org_row: Pattern[str]) -> Generator[dict]:
for line in lines:
match = two_col_org_row.match(line).groupdict()
if match.get("term") and match.get("definition"):
Expand Down
2 changes: 1 addition & 1 deletion pybot/plugins/api/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def __len__(self):
def __repr__(self):
return "API Request: " + str(self.request)

def clone(self) -> "SlackApiRequest":
def clone(self) -> SlackApiRequest:
return self.__class__(
copy.deepcopy(self.request),
copy.deepcopy(self.resource),
Expand Down
10 changes: 3 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ readme = "README.md"
packages = [{include = "pybot"}]

[tool.poetry.dependencies]
python = "^3.12"
python = "^3.14"
aiohttp = "^3.9"
python-dotenv = "^1.0"
pyyaml = "^6.0"
sentry-sdk = "^2.0"
zipcodes = "^1.2"
multidict = "^6.9.1"

[tool.poetry.group.dev.dependencies]
pytest = "^9.0"
pytest-asyncio = "^1.0"
pytest-aiohttp = "^1.0"
pytest-mock = "^3.12"
black = "^26.0"
ruff = "^0.15"
requests = "^2.31"
pytest-cov = "^7.0.0"
Expand All @@ -39,13 +39,9 @@ testpaths = ["tests"]

[tool.ruff]
line-length = 100
target-version = "py312"
target-version = "py314"
extend-exclude = ["pybot/_vendor"]

[tool.ruff.lint]
select = ["E", "F", "I", "UP"]
ignore = ["F401", "E501"] # Allow re-exports and longer lines for now

[tool.black]
line-length = 100
target-version = ["py312"]
16 changes: 8 additions & 8 deletions tests/fixtures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def setup_user_info(
email: str | None = None,
name: str = "Test User",
real_name: str = "Test User",
) -> "SlackMock":
) -> SlackMock:
"""Configure response for users.info API call."""
profile = {"real_name": real_name}
if email:
Expand All @@ -120,17 +120,17 @@ def setup_user_info(
}
return self

def setup_method_response(self, method: str, response: Any) -> "SlackMock":
def setup_method_response(self, method: str, response: Any) -> SlackMock:
"""Configure response for a specific Slack API method."""
self._method_responses[method] = response
return self

def setup_error_response(self, method: str, error: Exception) -> "SlackMock":
def setup_error_response(self, method: str, error: Exception) -> SlackMock:
"""Configure an error to be raised for a specific method."""
self._error_responses[method] = error
return self

def setup_lookup_by_email(self, email: str, user_id: str) -> "SlackMock":
def setup_lookup_by_email(self, email: str, user_id: str) -> SlackMock:
"""Configure users.lookupByEmail response."""
self._email_lookup[email] = user_id
return self
Expand Down Expand Up @@ -287,7 +287,7 @@ async def _handle_get_all_records(self, table: str, field: str) -> list[str]:
return [m.get(field) for m in self._mentors.values() if m.get(field)]
return []

def setup_service(self, name: str, record_id: str | None = None) -> "AirtableMock":
def setup_service(self, name: str, record_id: str | None = None) -> AirtableMock:
"""Add a service to the mock."""
record_id = record_id or f"recsvc{len(self._services)}"
self._services[record_id] = {"Name": name}
Expand All @@ -300,7 +300,7 @@ def setup_mentor(
skillsets: list[str] | None = None,
record_id: str | None = None,
slack_name: str | None = None,
) -> "AirtableMock":
) -> AirtableMock:
"""Add a mentor to the mock."""
record_id = record_id or f"recmentor{len(self._mentors)}"
self._mentors[record_id] = {
Expand All @@ -311,7 +311,7 @@ def setup_mentor(
}
return self

def setup_error(self, table: str, operation: str, error: Exception | str) -> "AirtableMock":
def setup_error(self, table: str, operation: str, error: Exception | str) -> AirtableMock:
"""Configure an error for a specific table/operation combination."""
if isinstance(error, str):
error = Exception(error)
Expand Down Expand Up @@ -388,7 +388,7 @@ async def _handle_query(self, method: str, data: dict | None = None, **kwargs) -

return {"ok": True}

def setup_invite_error(self, error: Exception) -> "AdminSlackMock":
def setup_invite_error(self, error: Exception) -> AdminSlackMock:
"""Configure an error for channel invites."""
from pybot._vendor.slack import methods

Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_roll_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def test_roll_input_validation(self, input_text, expected_valid):
text = input_text.lower()
numdice, typedice = [int(num) for num in text.split("d")]
valid = 0 < numdice <= 10 and 0 < typedice <= 20
except (ValueError, IndexError):
except ValueError, IndexError:
valid = False

assert valid == expected_valid
Loading