GitHub status now point back to a dedicagted page

It automatically redirects with the build homepage,
or show a page with button to start/stop the build.
This commit is contained in:
Stéphane Bidoul 2021-11-06 20:11:30 +01:00
parent 24d9be3b0c
commit bc2740029d
No known key found for this signature in database
GPG key ID: BCAB2555446B5B92
8 changed files with 83 additions and 13 deletions

View file

@ -2,8 +2,9 @@ RUNBOAT_SUPPORTED_REPOS=["OCA/mis-builder", "shopinvader/odoo-shopinvader", "OCA
RUNBOAT_API_ADMIN_USER="admin"
RUNBOAT_API_ADMIN_PASSWD="admin"
RUNBOAT_BUILD_NAMESPACE=runboat-builds
RUNBOAT_BUILD_DOMAIN=runboat.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_SECRET_ENV={"PGPASSWORD": "..."}
RUNBOAT_GITHUB_TOKEN=
RUNBOAT_LOG_CONFIG=log-config.yaml
RUNBOAT_BASE_URL=https://runboat.odoo-community.org

View file

@ -16,11 +16,12 @@ ENV RUNBOAT_SUPPORTED_REPOS='["OCA/mis-builder", "shopinvader/odoo-shopinvader",
ENV RUNBOAT_API_ADMIN_USER="admin"
ENV RUNBOAT_API_ADMIN_PASSWD="admin"
ENV RUNBOAT_BUILD_NAMESPACE=runboat-builds
ENV RUNBOAT_BUILD_DOMAIN=runboat.example.com
ENV RUNBOAT_BUILD_DOMAIN=runboat-builds.example.com
ENV RUNBOAT_BUILD_ENV='{"PGHOST": "postgres14.runboat-builds-db", "PGPORT": "5432", "PGUSER": "runboat-build"}'
ENV RUNBOAT_BUILD_SECRET_ENV='{"PGPASSWORD": "..."}'
ENV RUNBOAT_GITHUB_TOKEN=
ENV RUNBOAT_LOG_CONFIG=/etc/runboat-log-config.yaml
ENV RUNBOAT_BASE_URL=https://runboat.example.com
ENV KUBECONFIG=/run/kubeconfig

View file

@ -28,7 +28,7 @@ Runboat has the following main components:
accessible;
- when the initializaiton job fails, flag the deployment as failed;
- when there are too many deployments started, stop the oldest started;
- when there are too many deployments, deleted the oldest created;
- when there are too many deployments, delete the oldest created;
- when a deployment is deleted, run a cleanp job to drop the database and delete
all kubernetes resources associated with the deployment.
@ -114,6 +114,8 @@ actually deploy. It expects the following to hold true:
- `runboat/pr`: the pull request number if this build is for a pull request;
- `runboat/git-commit`: the commit sha.
- the home page of a running build is exposed at `http://{build_slug}.{build_domain}`.
During the lifecycle of a build, the controller does the following on the deployed
resources:
@ -139,9 +141,6 @@ MVP:
- look at other TODO in code to see if anything important remains
- basic UI (single page with a combo box to select repo and show builds by branch/pr,
with start/stop buttons)
- better target_url in GitHub status: instead of providing the link to the ingress,
provide a link to the build, which redirects to the ingress if the build is started,
or to a build details page with action buttons (start, stop, view log, etc)
- secure github webhooks
- deployment and more load testing

View file

@ -1,12 +1,13 @@
from fastapi import FastAPI
from . import __version__, api, controller, k8s, webhooks
from . import __version__, api, controller, k8s, webhooks, webui
app = FastAPI(
title="Runboat", description="Runbot on Kubernetes ☸️", version=__version__
)
app.include_router(api.router, prefix="/api/v1")
app.include_router(webhooks.router)
app.include_router(api.router, prefix="/api/v1", tags=["api"])
app.include_router(webhooks.router, tags=["webhooks"])
app.include_router(webui.router, tags=["webui"])
@app.on_event("startup")

View file

@ -132,6 +132,18 @@ class Build(BaseModel):
def link(self) -> str:
return f"http://{self.slug}.{settings.build_domain}"
@property
def repo_link(self) -> str:
link = f"https://github.com/{self.repo}"
if self.pr:
return f"{link}/pull/{self.pr}"
else:
return f"{link}/tree/{self.target_branch}"
@property
def live_link(self) -> str:
return f"{settings.base_url}/builds/{self.name}?live"
@classmethod
async def deploy(
cls, repo: str, target_branch: str, pr: int | None, git_commit: str
@ -178,7 +190,7 @@ class Build(BaseModel):
self.repo,
self.git_commit,
GitHubStatusState.pending,
target_url=None,
target_url=self.live_link,
)
elif self.status in (BuildStatus.stopped, BuildStatus.stopping):
_logger.info(f"Starting {self} that was last scaled on {self.last_scaled}.")
@ -246,7 +258,7 @@ class Build(BaseModel):
self.repo,
self.git_commit,
GitHubStatusState.pending,
target_url=None,
target_url=self.live_link,
)
async def on_initialize_succeeded(self) -> None:
@ -260,7 +272,7 @@ class Build(BaseModel):
self.repo,
self.git_commit,
GitHubStatusState.success,
target_url=self.link,
target_url=self.live_link,
)
async def on_initialize_failed(self) -> None:
@ -274,7 +286,7 @@ class Build(BaseModel):
self.repo,
self.git_commit,
GitHubStatusState.failure,
target_url=None,
target_url=self.live_link,
)
async def on_cleanup_started(self) -> None:

View file

@ -38,6 +38,9 @@ class Settings(BaseSettings):
github_token: Optional[str]
# The file with the python logging configuration to use for the runboat controller.
log_config: Optional[str]
# The base url where the runboat UI and API is exposed on internet.
# Used to generate backlinks in GitHub statuses
base_url: str = "http://localhost:8000"
class Config:
env_prefix = "RUNBOAT_"

24
src/runboat/webui.py Normal file
View file

@ -0,0 +1,24 @@
from pathlib import Path
from typing import Optional
from fastapi import APIRouter, HTTPException, Request, status
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
from .controller import controller
from .models import BuildStatus
router = APIRouter()
templates = Jinja2Templates(directory=Path(__file__).parent / "webui")
@router.get("/builds/{name}", response_class=HTMLResponse)
async def build(request: Request, name: str, live: Optional[str] = None):
build = controller.db.get(name)
if not build:
raise HTTPException(status.HTTP_404_NOT_FOUND)
if live is not None and build.status == BuildStatus.started:
return RedirectResponse(url=build.link)
return templates.TemplateResponse(
"build.html", {"request": request, "build": build}
)

View file

@ -0,0 +1,29 @@
<html>
<head>
<title>Runboat build {{ build.name }} for {{ build.repo }}</title>
<script>
build_name = "{{ build.name }}";
function start() {
fetch(`/api/v1/builds/${build_name}/start`, options={method: 'POST'})
}
function stop() {
fetch(`/api/v1/builds/${build_name}/stop`, options={method: 'POST'})
}
</script>
<body>
<p>Repo: {{ build.repo }}</p>
{% if build.pr %}
<p>PR: <a href="{{ build.repo_link }}">{{ build.pr }}</a> to {{ build.target_branch }}</p>
{% else %}
<p>Branch: <a href="{{ build.repo_link }}">{{ build.target_branch }}</a></p>
{% endif %}
<p>Commit: {{ build.git_commit }}</p>
<p>Status: {{ build.status }}</p>
{% if build.status == 'started' %}
<p><a href="{{ build.live_link }}">=> live</a></p>
<button onclick="stop()">stop</button>
{% else %}
<button onclick="start()">start</button>
{% endif %}
</body>
</html>