WIP
- commit/rollback transactions - identify supported branch prefixes - query builds api - better exceptions
This commit is contained in:
parent
c2c2e4bac9
commit
488d620f40
6 changed files with 73 additions and 26 deletions
|
|
@ -1,11 +1,11 @@
|
|||
import datetime
|
||||
from enum import Enum
|
||||
from typing import List
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi import Depends, HTTPException
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy.orm import Session
|
||||
from starlette.status import HTTP_404_NOT_FOUND
|
||||
|
||||
from . import github, models
|
||||
from .app import app
|
||||
|
|
@ -45,14 +45,10 @@ class Branch(BaseModel):
|
|||
response_model=List[Branch],
|
||||
)
|
||||
def branches(repo_id: str, db: Session = Depends(get_db)):
|
||||
return db.query(models.Branch).filter(models.Branch.repo_id == repo_id).all()
|
||||
|
||||
|
||||
class BuildStatus(str, Enum):
|
||||
stopped = "stopped"
|
||||
running = "running"
|
||||
deploying = "deploying"
|
||||
not_deployed = "not_deployed"
|
||||
repo = db.query(models.Repo).get(repo_id)
|
||||
if not repo:
|
||||
raise HTTPException(HTTP_404_NOT_FOUND)
|
||||
return db.query(models.Branch).filter(models.Branch.repo == repo).all()
|
||||
|
||||
|
||||
class Build(BaseModel):
|
||||
|
|
@ -60,7 +56,7 @@ class Build(BaseModel):
|
|||
created: datetime.datetime
|
||||
display_name: str
|
||||
display_url: str = Field(title="Link to open the build")
|
||||
status: BuildStatus
|
||||
status: models.BuildStatus
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
|
@ -70,8 +66,18 @@ class Build(BaseModel):
|
|||
"/repos/{repo_id}/branches/{branch_id}/builds",
|
||||
response_model=List[Build],
|
||||
)
|
||||
def builds(repo_id: str, branch_id: str):
|
||||
...
|
||||
def builds(repo_id: str, branch_id: str, db: Session = Depends(get_db)):
|
||||
repo = db.query(models.Repo).get(repo_id)
|
||||
if not repo:
|
||||
raise HTTPException(HTTP_404_NOT_FOUND)
|
||||
branch = (
|
||||
db.query(models.Branch)
|
||||
.filter(models.Branch.id == branch_id, models.Branch.repo == repo)
|
||||
.one_or_none()
|
||||
)
|
||||
if not branch:
|
||||
raise HTTPException(HTTP_404_NOT_FOUND)
|
||||
return db.query(models.Build).filter(models.Build.branch == branch).all()
|
||||
|
||||
|
||||
@app.get(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
from typing import Optional
|
||||
import re
|
||||
|
||||
from .exceptions import BranchNotSupported
|
||||
|
||||
images = {
|
||||
"15.0": "ghcr.io/oca/oca-ci/py3.8-odoo15.0:latest",
|
||||
|
|
@ -10,5 +12,27 @@ images = {
|
|||
}
|
||||
|
||||
|
||||
def get_build_image(target_branch: str) -> Optional[str]:
|
||||
return images.get(target_branch)
|
||||
TARGET_BRANCH_RE = re.compile(r"^(\d+\.\d+)")
|
||||
|
||||
|
||||
def get_target_branch(branch_name: str) -> str:
|
||||
mo = TARGET_BRANCH_RE.match(branch_name)
|
||||
if not mo:
|
||||
raise BranchNotSupported(
|
||||
f"Malformed branch name {branch_name} "
|
||||
f"(it should start with an Odoo branch name)."
|
||||
)
|
||||
if mo:
|
||||
key = mo.group(1)
|
||||
if key not in images:
|
||||
raise BranchNotSupported(
|
||||
f"No build image configured for {key} (from {branch_name})."
|
||||
)
|
||||
return key
|
||||
|
||||
|
||||
check_branch_supported = get_target_branch
|
||||
|
||||
|
||||
def get_build_image(branch_name: str) -> str:
|
||||
return images[get_target_branch(branch_name)]
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
from typing import Generator
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from .settings import settings
|
||||
|
||||
|
|
@ -15,9 +13,14 @@ def create_tables() -> None:
|
|||
Base.metadata.create_all(engine)
|
||||
|
||||
|
||||
def get_db() -> Generator[Session]:
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
except Exception:
|
||||
db.rollback()
|
||||
raise
|
||||
else:
|
||||
db.commit()
|
||||
finally:
|
||||
db.close()
|
||||
|
|
|
|||
|
|
@ -12,3 +12,7 @@ class BranchNotFound(ClientError):
|
|||
|
||||
class NotFoundOnGithub(ClientError):
|
||||
pass
|
||||
|
||||
|
||||
class BranchNotSupported(ClientError):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ def _github_get(url: str) -> Any:
|
|||
}
|
||||
response = requests.get(full_url, headers)
|
||||
if response.status_code == 404:
|
||||
raise NotFoundOnGithub(full_url)
|
||||
raise NotFoundOnGithub(f"GitHub URL not found: {full_url}.")
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from enum import Enum
|
||||
from typing import List
|
||||
|
||||
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, func
|
||||
|
|
@ -5,7 +6,7 @@ from sqlalchemy.ext.compiler import compiles
|
|||
from sqlalchemy.orm import Session, relationship
|
||||
from sqlalchemy.sql import expression
|
||||
|
||||
from .build_images import get_build_image
|
||||
from .build_images import check_branch_supported, get_build_image
|
||||
from .db import Base
|
||||
from .exceptions import RepoNotFound
|
||||
from .github import BranchInfo, PullRequestInfo
|
||||
|
|
@ -55,7 +56,7 @@ class Repo(Base):
|
|||
.one_or_none()
|
||||
)
|
||||
if repo is None:
|
||||
raise RepoNotFound()
|
||||
raise RepoNotFound(f"Repo {org}/{name} not supported by this runboat.")
|
||||
return repo
|
||||
|
||||
|
||||
|
|
@ -106,6 +107,7 @@ class Branch(Base):
|
|||
.one_or_none()
|
||||
)
|
||||
if branch is None:
|
||||
check_branch_supported(branch_info.name)
|
||||
branch = Branch(
|
||||
repo=repo,
|
||||
target_branch=branch_info.name,
|
||||
|
|
@ -128,6 +130,7 @@ class Branch(Base):
|
|||
.one_or_none()
|
||||
)
|
||||
if branch is None:
|
||||
check_branch_supported(pr_info.target_branch)
|
||||
branch = Branch(
|
||||
repo=repo,
|
||||
target_branch=pr_info.target_branch,
|
||||
|
|
@ -138,6 +141,13 @@ class Branch(Base):
|
|||
return branch
|
||||
|
||||
|
||||
class BuildStatus(str, Enum):
|
||||
stopped = "stopped"
|
||||
running = "running"
|
||||
deploying = "deploying"
|
||||
not_deployed = "not_deployed"
|
||||
|
||||
|
||||
class Build(Base):
|
||||
__tablename__ = "build"
|
||||
|
||||
|
|
@ -147,9 +157,9 @@ class Build(Base):
|
|||
branch_id = Column(Integer, ForeignKey("branch.id"), nullable=False, index=True)
|
||||
branch: Branch = relationship(Branch, back_populates="builds")
|
||||
|
||||
build_image = Column(String, nullable=False)
|
||||
build_image: str = Column(String, nullable=False)
|
||||
git_sha: str = Column(String, nullable=False)
|
||||
status: str = Column(String, nullable=False)
|
||||
status: BuildStatus = Column(String, nullable=False)
|
||||
# ressource_label = Column(String, nullable=False, unique=True, index=True)
|
||||
|
||||
# TODO: add unique constraint on branch_id + git_sha
|
||||
|
|
@ -182,7 +192,7 @@ class Build(Base):
|
|||
branch=branch,
|
||||
git_sha=git_sha,
|
||||
build_image=build_image,
|
||||
status="not_deployed", # TODO use same enum as in API
|
||||
status=BuildStatus.not_deployed,
|
||||
)
|
||||
db.add(build)
|
||||
db.flush()
|
||||
|
|
|
|||
Loading…
Reference in a new issue