diff --git a/pyproject.toml b/pyproject.toml index cb0fdc3..41a2cb3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,8 @@ dependencies = [ "httpx", "jinja2", "kubernetes", + "pydantic>=2", + "pydantic-settings", "rich", "sse-starlette", "uvicorn", diff --git a/src/runboat/api.py b/src/runboat/api.py index 45d0ef9..074fb31 100644 --- a/src/runboat/api.py +++ b/src/runboat/api.py @@ -5,7 +5,7 @@ from collections.abc import AsyncGenerator from ansi2html import Ansi2HTMLConverter from fastapi import APIRouter, Depends, HTTPException, Request, status from fastapi.responses import HTMLResponse -from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict from sse_starlette.sse import EventSourceResponse from starlette.status import HTTP_404_NOT_FOUND @@ -18,6 +18,8 @@ router = APIRouter() class Status(BaseModel): + model_config = ConfigDict(from_attributes=True) + deployed: int max_deployed: int failed: int @@ -29,19 +31,17 @@ class Status(BaseModel): max_initializing: int undeploying: int - class Config: - orm_mode = True - class Repo(BaseModel): + model_config = ConfigDict(from_attributes=True) + name: str link: str - class Config: - orm_mode = True - class Build(BaseModel): + model_config = ConfigDict(from_attributes=True) + name: str commit_info: github.CommitInfo deploy_link: str @@ -54,9 +54,6 @@ class Build(BaseModel): created: datetime.datetime last_scaled: datetime.datetime - class Config: - orm_mode = True - class BuildEvent(BaseModel): event: models.BuildEvent diff --git a/src/runboat/github.py b/src/runboat/github.py index db388f9..63ed7ed 100644 --- a/src/runboat/github.py +++ b/src/runboat/github.py @@ -3,7 +3,7 @@ from enum import Enum from typing import Any import httpx -from pydantic import BaseModel, validator +from pydantic import BaseModel, field_validator from .exceptions import NotFoundOnGitHub from .settings import settings @@ -32,7 +32,7 @@ class CommitInfo(BaseModel): pr: int | None git_commit: str - @validator("repo") + @field_validator("repo") def validate_repo(cls, v: str) -> str: return v.lower() diff --git a/src/runboat/models.py b/src/runboat/models.py index 5729b91..62475ab 100644 --- a/src/runboat/models.py +++ b/src/runboat/models.py @@ -5,7 +5,7 @@ from enum import Enum from typing import Optional from kubernetes.client.models.v1_deployment import V1Deployment -from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict from . import github, k8s from .github import CommitInfo, GitHubStatusState @@ -38,6 +38,8 @@ class BuildInitStatus(str, Enum): class Build(BaseModel): + model_config = ConfigDict(from_attributes=True) + name: str deployment_name: str commit_info: CommitInfo @@ -47,9 +49,6 @@ class Build(BaseModel): last_scaled: datetime.datetime created: datetime.datetime - class Config: - read_with_orm_mode = True - def __str__(self) -> str: return f"{self.slug} ({self.name})" @@ -377,11 +376,10 @@ class Build(BaseModel): class Repo(BaseModel): + model_config = ConfigDict(from_attributes=True) + name: str @property def link(self) -> str: return f"https://github.com/{self.name}" - - class Config: - read_with_orm_mode = True diff --git a/src/runboat/settings.py b/src/runboat/settings.py index 3481580..21ff68d 100644 --- a/src/runboat/settings.py +++ b/src/runboat/settings.py @@ -1,7 +1,9 @@ import re from pathlib import Path +from typing import Annotated -from pydantic import BaseModel, BaseSettings, validator +from pydantic import BaseModel, BeforeValidator, field_validator +from pydantic_settings import BaseSettings, SettingsConfigDict from .exceptions import RepoOrBranchNotSupported @@ -21,11 +23,7 @@ class BuildSettings(BaseModel): env: dict[str, str] = {} secret_env: dict[str, str] = {} template_vars: dict[str, str] = {} - kubefiles_path: Path | None - - validate_kubefiles_path = validator("kubefiles_path", allow_reuse=True, pre=True)( - validate_path - ) + kubefiles_path: Annotated[Path | None, BeforeValidator(validate_path)] = None class RepoSettings(BaseModel): @@ -33,7 +31,7 @@ class RepoSettings(BaseModel): branch: str # regex builds: list[BuildSettings] - @validator("builds") + @field_validator("builds") def validate_builds(cls, v: list[BuildSettings]) -> list[BuildSettings]: if len(v) != 1: raise ValueError( @@ -43,6 +41,8 @@ class RepoSettings(BaseModel): class Settings(BaseSettings): + model_config = SettingsConfigDict(env_prefix="RUNBOAT_") + # Configuration for supported repositories and branches. repos: list[RepoSettings] # A user and password to protect the most sensitive operations of the API. @@ -67,7 +67,9 @@ class Settings(BaseSettings): # kubefiles. build_template_vars: dict[str, str] = {} # The path of the default kubefiles to be used. - build_default_kubefiles_path: Path | None + build_default_kubefiles_path: Annotated[ + Path | None, BeforeValidator(validate_path) + ] = None # The token to use for the GitHub api calls (to query branches and pull requests, # and report build statuses). github_token: str | None @@ -83,10 +85,6 @@ class Settings(BaseSettings): # Disable posting of statuses to GitHub commits disable_commit_statuses: bool = False - validate_build_default_kubefiles_path = validator( - "build_default_kubefiles_path", allow_reuse=True, pre=True - )(validate_path) - def get_build_settings(self, repo: str, target_branch: str) -> list[BuildSettings]: for repo_settings in self.repos: if not re.match(repo_settings.repo, repo, re.IGNORECASE): @@ -106,8 +104,5 @@ class Settings(BaseSettings): else: return True - class Config: - env_prefix = "RUNBOAT_" - settings = Settings()