Skip to content

Commit ae2af6a

Browse files
committed
Introduce a CPT backend version
Version information is compiled at build time using a Python script, producing a transient JSON file that will be copied into the backend container for access at runtime via `GET /api/version`.
1 parent cd82ccc commit ae2af6a

File tree

12 files changed

+150
-15
lines changed

12 files changed

+150
-15
lines changed

.github/workflows/build-push.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ on:
33
push:
44
branches:
55
- main
6-
- revamp
76

87
jobs:
98
build:
@@ -12,6 +11,9 @@ jobs:
1211

1312
steps:
1413
- uses: actions/checkout@v2
14+
- uses: actions/setup-python@v4
15+
with:
16+
python-version: "3.11"
1517

1618
- name: Build Frontend Image
1719
id: build-front
@@ -33,6 +35,9 @@ jobs:
3335
username: ${{ secrets.QUAY_USER }}
3436
password: ${{ secrets.QUAY_TOKEN }}
3537

38+
- name: Generate version file
39+
run: scripts/version.py
40+
3641
- name: Build Backend Image
3742
id: build-back
3843
uses: redhat-actions/buildah-build@v2

.github/workflows/release-build-push.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ jobs:
1212

1313
steps:
1414
- uses: actions/checkout@v2
15+
- uses: actions/setup-python@v4
16+
with:
17+
python-version: "3.11"
1518

1619
- name: Build Frontend Image
1720
id: build-front
@@ -33,6 +36,9 @@ jobs:
3336
username: ${{ secrets.QUAY_USER }}
3437
password: ${{ secrets.QUAY_TOKEN }}
3538

39+
- name: Generate version file
40+
run: scripts/version.py
41+
3642
- name: Build Backend Image
3743
id: build-back
3844
uses: redhat-actions/buildah-build@v2

backend/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
**/.idea
22
**/*.cfg
33
ocpperf.toml
4+
version.json
45
**/*.csv
56
**/*.json
67

backend/VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.1.0

backend/app/api/api.py

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import json
2+
from pathlib import Path
13
from fastapi import APIRouter
24

35
from app.api.v1.endpoints.ocp import results
@@ -34,8 +36,8 @@
3436
router.include_router(telcoGraphs.router, tags=["telco"])
3537

3638
# OLS endpoints
37-
router.include_router(olsJobs.router, tags=['ols'])
38-
router.include_router(olsGraphs.router, tags=['ols'])
39+
router.include_router(olsJobs.router, tags=["ols"])
40+
router.include_router(olsGraphs.router, tags=["ols"])
3941

4042
# Jira endpoints
4143
router.include_router(jira.router, tags=["jira"])
@@ -45,3 +47,55 @@
4547

4648
# OCM endpoint
4749
router.include_router(ocmJobs.router, tags=["ocm"])
50+
51+
52+
@router.get(
53+
"/api/version",
54+
summary="Get CPT aggregator version",
55+
description=(
56+
"Return information about the CPT aggregator build, "
57+
"including the version, the last commit SHA, "
58+
"the branch on which this aggregator was built, "
59+
"and the build date, along with a list of the 10 most recent commits"
60+
),
61+
responses={
62+
200: {
63+
"content": {
64+
"application/json": {
65+
"example": {
66+
"version": "0.1.0",
67+
"sha": "xxxxxx",
68+
"branch": "main",
69+
"display_version": "v0.1.0-xxxxxx (main)",
70+
"date": "2025-04-16T19:36:55.179192+00:00",
71+
"changes": [
72+
{
73+
"sha": "xyzzy",
74+
"author": "Easter Bunny",
75+
"email": "bunny_the@example.com",
76+
"date": "2025-04-14T14:52:38-04:00",
77+
"title": "Add an easter egg",
78+
}
79+
],
80+
},
81+
}
82+
},
83+
}
84+
},
85+
)
86+
async def version():
87+
"""Return CPT aggregator version information
88+
89+
A version.json file is created at build time; this API will
90+
fetch it, and return it. If the file doesn't exist, or can't
91+
be read, return a minimal placeholder rather than fail.
92+
"""
93+
version = Path("version.json")
94+
v = {"version": "0.0.0-none", "changes": []}
95+
try:
96+
if version.is_file():
97+
with version.open() as vf:
98+
v = json.load(vf)
99+
except Exception as exc:
100+
print(f"Unable to read {str(version)}: {str(exc)!r}")
101+
return v

backend/backend.containerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ COPY app/ /backend/app
1010
COPY scripts/ /backend/scripts
1111
COPY pyproject.toml /backend
1212
COPY poetry.lock /backend
13+
COPY version.json /backend
1314

1415
WORKDIR /backend
1516

backend/scripts/start-reload.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
#!/usr/bin/bash
2-
2+
scripts/version.py
33
uvicorn --reload --host="0.0.0.0" --port=8000 --forwarded-allow-ips='*' --proxy-headers app.main:app

backend/scripts/start.sh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
#!/usr/bin/bash
2-
32
poetry run uvicorn --host="0.0.0.0" --port=8000 --forwarded-allow-ips='*' --proxy-headers app.main:app

backend/scripts/version.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/usr/bin/env python3
2+
3+
"""Create version information JSON file
4+
5+
This file is created at build time and copied into the backend container so
6+
that we can include information that's not available at runtime.
7+
"""
8+
from datetime import datetime, timezone
9+
import json
10+
from pathlib import Path
11+
import sys
12+
import subprocess
13+
from typing import Optional
14+
15+
16+
def do(cmd: list[str]) -> list[str]:
17+
result = subprocess.run(cmd, text=True, capture_output=True)
18+
if result.returncode != 0:
19+
raise Exception(f"Command {' '.join(cmd)!r} failed: {result.stderr}")
20+
return [line.strip() for line in result.stdout.split("\n") if line.strip()]
21+
22+
23+
def getone(cmd: list[str], if_none: Optional[str] = None) -> str:
24+
res = do(cmd)
25+
if if_none is not None and len(res) == 0:
26+
return if_none
27+
if len(res) != 1:
28+
raise Exception(f"Command {' '.join(cmd)!r} returned {len(res)} lines: {res}")
29+
return res[0]
30+
31+
32+
def main():
33+
top = Path(getone(["git", "rev-parse", "--show-toplevel"]))
34+
backend = top / "backend"
35+
version = (backend / "VERSION").read_text().strip()
36+
sha = getone(["git", "rev-parse", "--short", "HEAD"])
37+
branch = getone(["git", "branch", "--show-current"], if_none="HEAD")
38+
display = f"v{version}-{sha} ({branch})"
39+
print(f"VERSION: {display}")
40+
log = do(["git", "log", "-n10", "--format=%h###%aN###%aE###%aI###%s"])
41+
fields = ("sha", "author", "email", "date", "title")
42+
vj = {
43+
"version": version,
44+
"sha": sha,
45+
"branch": branch,
46+
"display_version": display,
47+
"date": datetime.now(tz=timezone.utc).isoformat(),
48+
"changes": [{f: v for f, v in zip(fields, l.split("###"))} for l in log],
49+
}
50+
with (backend / "version.json").open("w") as verfile:
51+
json.dump(vj, fp=verfile)
52+
return 0
53+
54+
55+
if __name__ == "__main__":
56+
try:
57+
sys.exit(main())
58+
except Exception as exc:
59+
print(f"Failed with {str(exc)!r}", file=sys.stderr)
60+
sys.exit(1)

backend/tests/e2e_backend.sh

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
11
#!/bin/bash
22
set -e
33

4+
TOP=$(git rev-parse --show-toplevel)
5+
BACKEND=${TOP}/backend
6+
cd ${BACKEND}
7+
8+
echo "generating version file"
9+
scripts/version.py
10+
411
echo "building backend image"
5-
podman build -f backend/backend.containerfile --tag backend ./backend
12+
podman build -f backend.containerfile --tag backend .
613

714
echo "building backend functional test image"
8-
podman build -f backend/tests/functional.containerfile --tag functional ./backend
15+
podman build -f tests/functional.containerfile --tag functional .
916

1017
export POD_NAME="e2e"
1118

1219
echo "cleaning up and recreating pod"
1320
podman pod rm -f ${POD_NAME}
1421
podman pod create --name=${POD_NAME} --publish 127.0.0.1:8000:8000 --publish 127.0.0.1:3000:3000
1522

16-
./backend/tests/opensearch_ocp.sh
23+
./tests/opensearch_ocp.sh
1724
echo "seeding db"
1825
podman run --rm --pod=${POD_NAME} --entrypoint python3 functional tests/db_seed.py
1926
echo "deploying backend"
2027
podman run -d --pod=${POD_NAME} \
21-
-v "$(pwd)/backend/tests/ocpperf_test.toml:/backend/ocpperf.toml:z" \
28+
-v "${BACKEND}/tests/ocpperf_test.toml:/backend/ocpperf.toml:z" \
2229
--name=back backend

0 commit comments

Comments
 (0)