Merge pull request #99 from sbidoul/modernize
Modernize, require Python 3.12
This commit is contained in:
commit
aca17a06f5
17 changed files with 127 additions and 119 deletions
|
|
@ -1,11 +1,11 @@
|
||||||
RUNBOAT_REPOS=[{"repo": "^oca/.*", "branch": "^15.0$", "builds": [{"image": "ghcr.io/oca/oca-ci/py3.8-odoo15.0:latest"}]}]
|
RUNBOAT_REPOS='[{"repo": "^oca/.*", "branch": "^15.0$", "builds": [{"image": "ghcr.io/oca/oca-ci/py3.8-odoo15.0:latest"}]}]'
|
||||||
RUNBOAT_API_ADMIN_USER="admin"
|
RUNBOAT_API_ADMIN_USER="admin"
|
||||||
RUNBOAT_API_ADMIN_PASSWD="admin"
|
RUNBOAT_API_ADMIN_PASSWD="admin"
|
||||||
RUNBOAT_BUILD_NAMESPACE=runboat-builds
|
RUNBOAT_BUILD_NAMESPACE=runboat-builds
|
||||||
RUNBOAT_BUILD_DOMAIN=runboat-builds.odoo-community.org
|
RUNBOAT_BUILD_DOMAIN=runboat-builds.odoo-community.org
|
||||||
RUNBOAT_BUILD_ENV={"PGHOST": "postgres14.runboat-builds-db", "PGPORT": "5432", "PGUSER": "runboat-build"}
|
RUNBOAT_BUILD_ENV='{"PGHOST": "postgres14.runboat-builds-db", "PGPORT": "5432", "PGUSER": "runboat-build"}'
|
||||||
RUNBOAT_BUILD_SECRET_ENV={"PGPASSWORD": "..."}
|
RUNBOAT_BUILD_SECRET_ENV='{"PGPASSWORD": "..."}'
|
||||||
RUNBOAT_BUILD_TEMPLATE_VARS={"storageClassName": "my-storage-class"}
|
RUNBOAT_BUILD_TEMPLATE_VARS='{"storageClassName": "my-storage-class"}'
|
||||||
RUNBOAT_BUILD_DEFAULT_KUBEFILES_PATH=
|
RUNBOAT_BUILD_DEFAULT_KUBEFILES_PATH=
|
||||||
RUNBOAT_GITHUB_TOKEN=
|
RUNBOAT_GITHUB_TOKEN=
|
||||||
RUNBOAT_LOG_CONFIG=log-config.yaml
|
RUNBOAT_LOG_CONFIG=log-config.yaml
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
RUNBOAT_REPOS=[{"repo": "^oca/.*", "branch": "^15.0$", "builds": [{"image": "ghcr.io/oca/oca-ci/py3.8-odoo15.0:latest"}]}, {"repo": "^oca/.*", "branch": "^16.0$", "builds": [{"image": "ghcr.io/oca/oca-ci/py3.10-odoo16.0:latest", "kubefiles_path": "/tmp"}]}]
|
RUNBOAT_REPOS='[{"repo": "^oca/.*", "branch": "^15.0$", "builds": [{"image": "ghcr.io/oca/oca-ci/py3.8-odoo15.0:latest"}]}, {"repo": "^oca/.*", "branch": "^16.0$", "builds": [{"image": "ghcr.io/oca/oca-ci/py3.10-odoo16.0:latest", "kubefiles_path": "/tmp"}]}]'
|
||||||
RUNBOAT_API_ADMIN_USER="admin"
|
RUNBOAT_API_ADMIN_USER="admin"
|
||||||
RUNBOAT_API_ADMIN_PASSWD="admin"
|
RUNBOAT_API_ADMIN_PASSWD="admin"
|
||||||
RUNBOAT_BUILD_NAMESPACE=runboat-builds
|
RUNBOAT_BUILD_NAMESPACE=runboat-builds
|
||||||
RUNBOAT_BUILD_DOMAIN=runboat.odoo-community.org
|
RUNBOAT_BUILD_DOMAIN=runboat.odoo-community.org
|
||||||
RUNBOAT_BUILD_ENV={}
|
RUNBOAT_BUILD_ENV={}
|
||||||
RUNBOAT_BUILD_SECRET_ENV={"PGPASSWORD": "thepgpassword"}
|
RUNBOAT_BUILD_SECRET_ENV='{"PGPASSWORD": "thepgpassword"}'
|
||||||
RUNBOAT_BUILD_TEMPLATE_VARS={"storageClassName": "my-storage-class"}
|
RUNBOAT_BUILD_TEMPLATE_VARS='{"storageClassName": "my-storage-class"}'
|
||||||
RUNBOAT_GITHUB_TOKEN=
|
RUNBOAT_GITHUB_TOKEN=
|
||||||
RUNBOAT_GITHUB_WEBHOOK_SECRET=
|
RUNBOAT_GITHUB_WEBHOOK_SECRET=
|
||||||
RUNBOAT_LOG_CONFIG=log-config.yaml
|
RUNBOAT_LOG_CONFIG=log-config.yaml
|
||||||
|
|
|
||||||
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
|
|
@ -10,10 +10,10 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: [ "3.10", "3.11" ]
|
python-version: ["3.12"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v2
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
- name: Install project
|
- name: Install project
|
||||||
|
|
@ -32,15 +32,15 @@ jobs:
|
||||||
if: ${{ github.repository_owner == 'sbidoul' && github.ref == 'refs/heads/main' }}
|
if: ${{ github.repository_owner == 'sbidoul' && github.ref == 'refs/heads/main' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image
|
- name: Build image
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
tags: |
|
tags: |
|
||||||
ghcr.io/${{ github.repository }}:latest
|
ghcr.io/${{ github.repository }}:latest
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,15 @@
|
||||||
default_language_version:
|
default_language_version:
|
||||||
python: python3
|
python: python3
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/psf/black
|
|
||||||
rev: 23.1.0
|
|
||||||
hooks:
|
|
||||||
- id: black
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.4.0
|
rev: v4.5.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-toml
|
- id: check-toml
|
||||||
- id: check-yaml
|
- id: check-yaml
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||||
rev: v0.0.252
|
rev: v0.1.6
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
|
- id: ruff-format
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
FROM python:3.10
|
FROM python:3.12
|
||||||
|
|
||||||
LABEL maintainer="Stéphane Bidoul"
|
LABEL maintainer="Stéphane Bidoul"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,18 @@ classifiers = [
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi2html",
|
"ansi2html",
|
||||||
"fastapi",
|
"fastapi>=0.93",
|
||||||
"gunicorn",
|
"gunicorn",
|
||||||
"httpx",
|
"httpx",
|
||||||
"jinja2",
|
"jinja2",
|
||||||
"kubernetes",
|
"kubernetes",
|
||||||
|
"pydantic>=2",
|
||||||
|
"pydantic-settings",
|
||||||
"rich",
|
"rich",
|
||||||
"sse-starlette",
|
"sse-starlette",
|
||||||
"uvicorn",
|
"uvicorn",
|
||||||
]
|
]
|
||||||
|
requires-python = "==3.12.*"
|
||||||
dynamic = ["version", "description"]
|
dynamic = ["version", "description"]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
|
@ -38,6 +41,8 @@ mypy = [
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Home = "https://github.com/sbidoul/runboat"
|
Home = "https://github.com/sbidoul/runboat"
|
||||||
|
|
||||||
|
# ruff
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
fix = true
|
fix = true
|
||||||
target-version = "py310"
|
target-version = "py310"
|
||||||
|
|
@ -60,19 +65,30 @@ max-complexity = 15
|
||||||
[tool.ruff.isort]
|
[tool.ruff.isort]
|
||||||
known-first-party = ["runboat"]
|
known-first-party = ["runboat"]
|
||||||
|
|
||||||
|
# pytest
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
env_override_existing_values = 1
|
env_override_existing_values = 1
|
||||||
env_files = [".env.test"]
|
env_files = [".env.test"]
|
||||||
asyncio_mode = "strict"
|
asyncio_mode = "strict"
|
||||||
# flake8 config is in .flake8
|
|
||||||
|
# mypy
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
strict = true
|
strict = true
|
||||||
show_error_codes = true
|
show_error_codes = true
|
||||||
|
plugins = ["pydantic.mypy"]
|
||||||
|
|
||||||
[[tool.mypy.overrides]]
|
[[tool.mypy.overrides]]
|
||||||
module = ["uvicorn.*", "kubernetes.*", "ansi2html"]
|
module = ["kubernetes.*"]
|
||||||
ignore_missing_imports = true
|
ignore_missing_imports = true
|
||||||
|
|
||||||
|
[tool.pydantic-mypy]
|
||||||
|
init_forbid_extra = true
|
||||||
|
init_typed = true
|
||||||
|
warn_required_dynamic_aliases = true
|
||||||
|
|
||||||
|
# pip-deepfreeze
|
||||||
|
|
||||||
[tool.pip-deepfreeze.sync]
|
[tool.pip-deepfreeze.sync]
|
||||||
extras = "test,mypy"
|
extras = "test,mypy"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
# frozen requirements generated by pip-deepfreeze
|
# frozen requirements generated by pip-deepfreeze
|
||||||
mypy==0.991
|
mypy==1.7.0
|
||||||
mypy-extensions==0.4.3
|
mypy-extensions==1.0.0
|
||||||
tomli==2.0.1
|
types-urllib3==1.26.25.14
|
||||||
types-urllib3==1.26.25.4
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,9 @@
|
||||||
# frozen requirements generated by pip-deepfreeze
|
# frozen requirements generated by pip-deepfreeze
|
||||||
attrs==22.1.0
|
coverage==7.3.2
|
||||||
coverage==6.5.0
|
iniconfig==2.0.0
|
||||||
exceptiongroup==1.0.4
|
pluggy==1.3.0
|
||||||
iniconfig==1.1.1
|
pytest==7.4.3
|
||||||
packaging==21.3
|
pytest-asyncio==0.21.1
|
||||||
pluggy==1.0.0
|
pytest-cov==4.1.0
|
||||||
pyparsing==3.0.9
|
|
||||||
pytest==7.2.0
|
|
||||||
pytest-asyncio==0.20.2
|
|
||||||
pytest-cov==4.0.0
|
|
||||||
pytest-dotenv==0.5.2
|
pytest-dotenv==0.5.2
|
||||||
pytest-mock==3.10.0
|
pytest-mock==3.12.0
|
||||||
python-dotenv==0.21.0
|
|
||||||
tomli==2.0.1
|
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,43 @@
|
||||||
# frozen requirements generated by pip-deepfreeze
|
# frozen requirements generated by pip-deepfreeze
|
||||||
|
annotated-types==0.6.0
|
||||||
ansi2html==1.8.0
|
ansi2html==1.8.0
|
||||||
anyio==3.6.2
|
anyio==3.7.1
|
||||||
cachetools==5.2.0
|
cachetools==5.3.2
|
||||||
certifi==2022.9.24
|
certifi==2023.11.17
|
||||||
charset-normalizer==2.1.1
|
charset-normalizer==3.3.2
|
||||||
click==8.1.3
|
click==8.1.7
|
||||||
commonmark==0.9.1
|
fastapi==0.104.1
|
||||||
fastapi==0.87.0
|
google-auth==2.23.4
|
||||||
google-auth==2.14.1
|
gunicorn==21.2.0
|
||||||
gunicorn==20.1.0
|
|
||||||
h11==0.14.0
|
h11==0.14.0
|
||||||
httpcore==0.16.1
|
httpcore==1.0.2
|
||||||
httpx==0.23.1
|
httpx==0.25.1
|
||||||
idna==3.4
|
idna==3.4
|
||||||
Jinja2==3.1.2
|
Jinja2==3.1.2
|
||||||
kubernetes==25.3.0
|
kubernetes==28.1.0
|
||||||
MarkupSafe==2.1.1
|
markdown-it-py==3.0.0
|
||||||
|
MarkupSafe==2.1.3
|
||||||
|
mdurl==0.1.2
|
||||||
oauthlib==3.2.2
|
oauthlib==3.2.2
|
||||||
pyasn1==0.4.8
|
packaging==23.2
|
||||||
pyasn1-modules==0.2.8
|
pyasn1==0.5.0
|
||||||
pydantic==1.10.2
|
pyasn1-modules==0.3.0
|
||||||
Pygments==2.13.0
|
pydantic==2.5.1
|
||||||
|
pydantic-settings==2.1.0
|
||||||
|
pydantic_core==2.14.3
|
||||||
|
Pygments==2.17.1
|
||||||
python-dateutil==2.8.2
|
python-dateutil==2.8.2
|
||||||
PyYAML==6.0
|
python-dotenv==1.0.0
|
||||||
requests==2.28.1
|
PyYAML==6.0.1
|
||||||
|
requests==2.31.0
|
||||||
requests-oauthlib==1.3.1
|
requests-oauthlib==1.3.1
|
||||||
rfc3986==1.5.0
|
rich==13.7.0
|
||||||
rich==12.6.0
|
|
||||||
rsa==4.9
|
rsa==4.9
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
sniffio==1.3.0
|
sniffio==1.3.0
|
||||||
sse-starlette==1.2.1
|
sse-starlette==1.6.5
|
||||||
starlette==0.21.0
|
starlette==0.27.0
|
||||||
typing_extensions==4.4.0
|
typing_extensions==4.8.0
|
||||||
urllib3==1.26.12
|
urllib3==1.26.18
|
||||||
uvicorn==0.20.0
|
uvicorn==0.24.0.post1
|
||||||
websocket-client==1.4.2
|
websocket-client==1.6.4
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from collections.abc import AsyncGenerator
|
||||||
from ansi2html import Ansi2HTMLConverter
|
from ansi2html import Ansi2HTMLConverter
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel, ConfigDict
|
||||||
from sse_starlette.sse import EventSourceResponse
|
from sse_starlette.sse import EventSourceResponse
|
||||||
from starlette.status import HTTP_404_NOT_FOUND
|
from starlette.status import HTTP_404_NOT_FOUND
|
||||||
|
|
||||||
|
|
@ -18,6 +18,8 @@ router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
class Status(BaseModel):
|
class Status(BaseModel):
|
||||||
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
deployed: int
|
deployed: int
|
||||||
max_deployed: int
|
max_deployed: int
|
||||||
failed: int
|
failed: int
|
||||||
|
|
@ -29,19 +31,17 @@ class Status(BaseModel):
|
||||||
max_initializing: int
|
max_initializing: int
|
||||||
undeploying: int
|
undeploying: int
|
||||||
|
|
||||||
class Config:
|
|
||||||
orm_mode = True
|
|
||||||
|
|
||||||
|
|
||||||
class Repo(BaseModel):
|
class Repo(BaseModel):
|
||||||
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
link: str
|
link: str
|
||||||
|
|
||||||
class Config:
|
|
||||||
orm_mode = True
|
|
||||||
|
|
||||||
|
|
||||||
class Build(BaseModel):
|
class Build(BaseModel):
|
||||||
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
commit_info: github.CommitInfo
|
commit_info: github.CommitInfo
|
||||||
deploy_link: str
|
deploy_link: str
|
||||||
|
|
@ -54,9 +54,6 @@ class Build(BaseModel):
|
||||||
created: datetime.datetime
|
created: datetime.datetime
|
||||||
last_scaled: datetime.datetime
|
last_scaled: datetime.datetime
|
||||||
|
|
||||||
class Config:
|
|
||||||
orm_mode = True
|
|
||||||
|
|
||||||
|
|
||||||
class BuildEvent(BaseModel):
|
class BuildEvent(BaseModel):
|
||||||
event: models.BuildEvent
|
event: models.BuildEvent
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,27 @@
|
||||||
|
from collections.abc import AsyncGenerator
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
|
||||||
from . import __version__, api, controller, k8s, webhooks, webui
|
from . import __version__, api, controller, k8s, webhooks, webui
|
||||||
|
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
||||||
|
await k8s.load_kube_config()
|
||||||
|
await controller.controller.start()
|
||||||
|
yield
|
||||||
|
await controller.controller.stop()
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="Runboat", description="Runbot on Kubernetes ☸️", version=__version__
|
title="Runboat",
|
||||||
|
description="Runbot on Kubernetes ☸️",
|
||||||
|
version=__version__,
|
||||||
|
lifespan=lifespan,
|
||||||
)
|
)
|
||||||
app.include_router(api.router, prefix="/api/v1", tags=["api"])
|
app.include_router(api.router, prefix="/api/v1", tags=["api"])
|
||||||
app.include_router(webhooks.router, tags=["webhooks"])
|
app.include_router(webhooks.router, tags=["webhooks"])
|
||||||
app.include_router(webui.router, tags=["webui"])
|
app.include_router(webui.router, tags=["webui"])
|
||||||
|
|
||||||
webui.mount(app)
|
webui.mount(app)
|
||||||
|
|
||||||
|
|
||||||
@app.on_event("startup")
|
|
||||||
async def startup() -> None:
|
|
||||||
await k8s.load_kube_config()
|
|
||||||
await controller.controller.start()
|
|
||||||
|
|
||||||
|
|
||||||
@app.on_event("shutdown")
|
|
||||||
async def shutdown() -> None:
|
|
||||||
await controller.controller.stop()
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from enum import Enum
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from pydantic import BaseModel, validator
|
from pydantic import BaseModel, field_validator
|
||||||
|
|
||||||
from .exceptions import NotFoundOnGitHub
|
from .exceptions import NotFoundOnGitHub
|
||||||
from .settings import settings
|
from .settings import settings
|
||||||
|
|
@ -32,7 +32,7 @@ class CommitInfo(BaseModel):
|
||||||
pr: int | None
|
pr: int | None
|
||||||
git_commit: str
|
git_commit: str
|
||||||
|
|
||||||
@validator("repo")
|
@field_validator("repo")
|
||||||
def validate_repo(cls, v: str) -> str:
|
def validate_repo(cls, v: str) -> str:
|
||||||
return v.lower()
|
return v.lower()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,9 @@ def _get_kubefiles_path(kubefiles_path: Path | None) -> Generator[Path, None, No
|
||||||
if kubefiles_path:
|
if kubefiles_path:
|
||||||
yield kubefiles_path
|
yield kubefiles_path
|
||||||
else:
|
else:
|
||||||
with resources.path(__package__, "kubefiles") as default_kubefiles_path:
|
with resources.as_file(
|
||||||
|
resources.files(__package__).joinpath("kubefiles")
|
||||||
|
) as default_kubefiles_path:
|
||||||
yield default_kubefiles_path
|
yield default_kubefiles_path
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from enum import Enum
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from kubernetes.client.models.v1_deployment import V1Deployment
|
from kubernetes.client.models.v1_deployment import V1Deployment
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel, ConfigDict
|
||||||
|
|
||||||
from . import github, k8s
|
from . import github, k8s
|
||||||
from .github import CommitInfo, GitHubStatusState
|
from .github import CommitInfo, GitHubStatusState
|
||||||
|
|
@ -38,6 +38,8 @@ class BuildInitStatus(str, Enum):
|
||||||
|
|
||||||
|
|
||||||
class Build(BaseModel):
|
class Build(BaseModel):
|
||||||
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
deployment_name: str
|
deployment_name: str
|
||||||
commit_info: CommitInfo
|
commit_info: CommitInfo
|
||||||
|
|
@ -47,9 +49,6 @@ class Build(BaseModel):
|
||||||
last_scaled: datetime.datetime
|
last_scaled: datetime.datetime
|
||||||
created: datetime.datetime
|
created: datetime.datetime
|
||||||
|
|
||||||
class Config:
|
|
||||||
read_with_orm_mode = True
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"{self.slug} ({self.name})"
|
return f"{self.slug} ({self.name})"
|
||||||
|
|
||||||
|
|
@ -377,11 +376,10 @@ class Build(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
class Repo(BaseModel):
|
class Repo(BaseModel):
|
||||||
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def link(self) -> str:
|
def link(self) -> str:
|
||||||
return f"https://github.com/{self.name}"
|
return f"https://github.com/{self.name}"
|
||||||
|
|
||||||
class Config:
|
|
||||||
read_with_orm_mode = True
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
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
|
from .exceptions import RepoOrBranchNotSupported
|
||||||
|
|
||||||
|
|
@ -21,11 +23,7 @@ class BuildSettings(BaseModel):
|
||||||
env: dict[str, str] = {}
|
env: dict[str, str] = {}
|
||||||
secret_env: dict[str, str] = {}
|
secret_env: dict[str, str] = {}
|
||||||
template_vars: dict[str, str] = {}
|
template_vars: dict[str, str] = {}
|
||||||
kubefiles_path: Path | None
|
kubefiles_path: Annotated[Path | None, BeforeValidator(validate_path)] = None
|
||||||
|
|
||||||
validate_kubefiles_path = validator("kubefiles_path", allow_reuse=True, pre=True)(
|
|
||||||
validate_path
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class RepoSettings(BaseModel):
|
class RepoSettings(BaseModel):
|
||||||
|
|
@ -33,7 +31,7 @@ class RepoSettings(BaseModel):
|
||||||
branch: str # regex
|
branch: str # regex
|
||||||
builds: list[BuildSettings]
|
builds: list[BuildSettings]
|
||||||
|
|
||||||
@validator("builds")
|
@field_validator("builds")
|
||||||
def validate_builds(cls, v: list[BuildSettings]) -> list[BuildSettings]:
|
def validate_builds(cls, v: list[BuildSettings]) -> list[BuildSettings]:
|
||||||
if len(v) != 1:
|
if len(v) != 1:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
|
|
@ -43,6 +41,8 @@ class RepoSettings(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
|
model_config = SettingsConfigDict(env_prefix="RUNBOAT_")
|
||||||
|
|
||||||
# Configuration for supported repositories and branches.
|
# Configuration for supported repositories and branches.
|
||||||
repos: list[RepoSettings]
|
repos: list[RepoSettings]
|
||||||
# A user and password to protect the most sensitive operations of the API.
|
# A user and password to protect the most sensitive operations of the API.
|
||||||
|
|
@ -59,15 +59,17 @@ class Settings(BaseSettings):
|
||||||
# The wildcard domain where the builds will be reacheable.
|
# The wildcard domain where the builds will be reacheable.
|
||||||
build_domain: str
|
build_domain: str
|
||||||
# A dictionary of environment variables to set in the build container and jobs.
|
# A dictionary of environment variables to set in the build container and jobs.
|
||||||
build_env: dict[str, str] = {}
|
build_env: dict[str, str] = {} # noqa: RUF012
|
||||||
# A dictionary of secret environment variables to set in the build container and
|
# A dictionary of secret environment variables to set in the build container and
|
||||||
# jobs.
|
# jobs.
|
||||||
build_secret_env: dict[str, str] = {}
|
build_secret_env: dict[str, str] = {} # noqa: RUF012
|
||||||
# A dictionary of variables to be set in the jinja rendering context for the
|
# A dictionary of variables to be set in the jinja rendering context for the
|
||||||
# kubefiles.
|
# kubefiles.
|
||||||
build_template_vars: dict[str, str] = {}
|
build_template_vars: dict[str, str] = {} # noqa: RUF012
|
||||||
# The path of the default kubefiles to be used.
|
# 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,
|
# The token to use for the GitHub api calls (to query branches and pull requests,
|
||||||
# and report build statuses).
|
# and report build statuses).
|
||||||
github_token: str | None
|
github_token: str | None
|
||||||
|
|
@ -83,10 +85,6 @@ class Settings(BaseSettings):
|
||||||
# Disable posting of statuses to GitHub commits
|
# Disable posting of statuses to GitHub commits
|
||||||
disable_commit_statuses: bool = False
|
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]:
|
def get_build_settings(self, repo: str, target_branch: str) -> list[BuildSettings]:
|
||||||
for repo_settings in self.repos:
|
for repo_settings in self.repos:
|
||||||
if not re.match(repo_settings.repo, repo, re.IGNORECASE):
|
if not re.match(repo_settings.repo, repo, re.IGNORECASE):
|
||||||
|
|
@ -106,8 +104,5 @@ class Settings(BaseSettings):
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
class Config:
|
|
||||||
env_prefix = "RUNBOAT_"
|
|
||||||
|
|
||||||
|
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,9 @@ def mount(app: FastAPI) -> None:
|
||||||
directory, which is then mounted under the /webui route.
|
directory, which is then mounted under the /webui route.
|
||||||
"""
|
"""
|
||||||
webui_path = Path(__file__).parent / "webui"
|
webui_path = Path(__file__).parent / "webui"
|
||||||
with resources.path("runboat", "webui-templates") as webui_template_path:
|
with resources.as_file(
|
||||||
|
resources.files(__package__).joinpath("webui-templates")
|
||||||
|
) as webui_template_path:
|
||||||
for path in webui_template_path.iterdir():
|
for path in webui_template_path.iterdir():
|
||||||
if path.name.endswith(".jinja"):
|
if path.name.endswith(".jinja"):
|
||||||
template = jinja2.Template(path.read_text())
|
template = jinja2.Template(path.read_text())
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ def _make_build(
|
||||||
pr=pr or None,
|
pr=pr or None,
|
||||||
git_commit="0d35a10f161b410f2baa3d416a338d191b6dabc0",
|
git_commit="0d35a10f161b410f2baa3d416a338d191b6dabc0",
|
||||||
),
|
),
|
||||||
image="ghcr.io/oca/oca-ci:py3.8-odoo15.0",
|
|
||||||
status=status or BuildStatus.starting,
|
status=status or BuildStatus.starting,
|
||||||
init_status=init_status or BuildInitStatus.todo,
|
init_status=init_status or BuildInitStatus.todo,
|
||||||
desired_replicas=0,
|
desired_replicas=0,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue