Fix deployment verification and clean submission
هذا الالتزام موجود في:
@@ -39,7 +39,9 @@ every request. It is per-process and deliberately resets on restart — a counte
|
||||
that drops to zero in the dashboard is a visible signal that the container was
|
||||
restarted or redeployed.
|
||||
|
||||
Environment variables (all optional): `APP_NAME`, `APP_VERSION`, `PORT` (default `8080`).
|
||||
Environment variables (all optional): `APP_NAME`, `APP_VERSION`, `RELEASE_SHA`,
|
||||
and `PORT` (default `8080`). CI embeds the immutable Git commit in
|
||||
`RELEASE_SHA`, and `/health` exposes it as `release` for deployment verification.
|
||||
|
||||
### Run locally without Docker
|
||||
|
||||
@@ -124,7 +126,7 @@ anonymously from the URL you paste in.
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Container Image URL | `docker.io/agamy74/ghaymah-api:8bc563f` |
|
||||
| Container Image URL | `docker.io/agamy74/ghaymah-api:<git-commit-sha>` |
|
||||
| Application Name | `ghaymah-api` |
|
||||
| Port Number | `8080` (must match the `EXPOSE`d port) |
|
||||
| Public Access | **enabled** |
|
||||
@@ -139,7 +141,8 @@ service a public URL.
|
||||
curl -f https://ghaymah-api-615e99f13665.hosted.ghaymah.systems/health
|
||||
```
|
||||
|
||||
Expected: HTTP 200 with `{"status":"ok","uptime_s":...,"timestamp":"..."}`.
|
||||
Expected: HTTP 200 with
|
||||
`{"status":"ok","release":"<git-commit-sha>","uptime_s":...,"timestamp":"..."}`.
|
||||
|
||||
Also open `https://ghaymah-api-615e99f13665.hosted.ghaymah.systems/docs` in a browser for the OpenAPI page,
|
||||
and screenshot both the running service in the Ghaymah dashboard and the `/health`
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
ARG RELEASE_SHA=local
|
||||
|
||||
# curl is required by the HEALTHCHECK below; everything else stays out of the image.
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends curl \
|
||||
@@ -8,6 +10,7 @@ RUN apt-get update \
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PIP_NO_CACHE_DIR=1 \
|
||||
RELEASE_SHA=${RELEASE_SHA} \
|
||||
PORT=8080
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
@@ -15,6 +15,7 @@ from fastapi.responses import JSONResponse
|
||||
|
||||
APP_NAME = os.getenv("APP_NAME", "ghaymah-api")
|
||||
APP_VERSION = os.getenv("APP_VERSION", "1.0.0")
|
||||
RELEASE_SHA = os.getenv("RELEASE_SHA", "local")
|
||||
PORT = int(os.getenv("PORT", "8080"))
|
||||
|
||||
# Process start time drives both uptime and the /metrics started_at field.
|
||||
@@ -53,6 +54,7 @@ async def root():
|
||||
return {
|
||||
"service": APP_NAME,
|
||||
"version": APP_VERSION,
|
||||
"release": RELEASE_SHA,
|
||||
"message": "Ghaymah deployment demo API",
|
||||
"endpoints": ["/", "/health", "/metrics", "/docs"],
|
||||
"started_at": STARTED_AT_ISO,
|
||||
@@ -68,6 +70,7 @@ async def health():
|
||||
status_code=200,
|
||||
content={
|
||||
"status": "ok",
|
||||
"release": RELEASE_SHA,
|
||||
"uptime_s": _uptime_s(),
|
||||
"timestamp": _now_iso(),
|
||||
},
|
||||
|
||||
@@ -957,5 +957,481 @@
|
||||
"code": 200,
|
||||
"latency_ms": 707.51,
|
||||
"requests": 268
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:03:51.848034+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 892.26,
|
||||
"requests": 270
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:04:25.528579+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 694.95,
|
||||
"requests": 272
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:04:59.231057+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 1861.06,
|
||||
"requests": 274
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:05:30.454738+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 586.48,
|
||||
"requests": 276
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:06:02.324156+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 890.61,
|
||||
"requests": 278
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:06:34.144517+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 905.66,
|
||||
"requests": 280
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:07:05.780266+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 738.98,
|
||||
"requests": 282
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:07:37.395548+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 843.17,
|
||||
"requests": 284
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:08:09.145094+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 843.5,
|
||||
"requests": 286
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:08:42.112423+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 1355.9,
|
||||
"requests": 288
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:09:14.075130+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 917.64,
|
||||
"requests": 290
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:09:45.825068+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 982.26,
|
||||
"requests": 292
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:10:17.588615+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 891.01,
|
||||
"requests": 294
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:10:49.297873+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 855.83,
|
||||
"requests": 296
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:11:20.912121+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 775.26,
|
||||
"requests": 298
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:11:52.499884+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 775.21,
|
||||
"requests": 300
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:12:24.514786+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 1206.22,
|
||||
"requests": 302
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:12:56.158854+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 897.33,
|
||||
"requests": 304
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:13:27.673469+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 727.96,
|
||||
"requests": 306
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:14:00.527846+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 1527.71,
|
||||
"requests": 308
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:14:32.323584+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 880.48,
|
||||
"requests": 310
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:15:03.962668+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 879.14,
|
||||
"requests": 312
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:15:35.833296+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 851.54,
|
||||
"requests": 314
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:16:07.355297+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 760.54,
|
||||
"requests": 316
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:16:39.012633+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 799.64,
|
||||
"requests": 318
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:17:11.074470+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 873.43,
|
||||
"requests": 320
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:17:42.996047+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 1053.03,
|
||||
"requests": 322
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:18:14.894655+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 1185.62,
|
||||
"requests": 324
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:18:46.652731+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 859.36,
|
||||
"requests": 326
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:19:17.573385+00:00",
|
||||
"status": "down",
|
||||
"code": 502,
|
||||
"latency_ms": 895.37,
|
||||
"requests": null
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:19:49.320997+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 866.28,
|
||||
"requests": 2
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:20:21.060460+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 839.12,
|
||||
"requests": 4
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:20:52.829427+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 889.52,
|
||||
"requests": 6
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:21:24.408306+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 782.05,
|
||||
"requests": 8
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:21:55.914426+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 723.7,
|
||||
"requests": 10
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:22:27.534277+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 799.56,
|
||||
"requests": 12
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:22:59.367798+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 979.2,
|
||||
"requests": 14
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:23:30.996828+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 779.25,
|
||||
"requests": 16
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:24:02.747579+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 880.05,
|
||||
"requests": 18
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:24:34.214686+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 717.03,
|
||||
"requests": 20
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:25:05.757052+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 757.22,
|
||||
"requests": 22
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:25:37.276950+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 733.39,
|
||||
"requests": 24
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:26:09.223657+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 1189.45,
|
||||
"requests": 26
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:26:43.045315+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 872.87,
|
||||
"requests": 28
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:27:17.288142+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 3339.91,
|
||||
"requests": 30
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:27:50.892062+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 2525.6,
|
||||
"requests": 32
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:28:22.569143+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 891.36,
|
||||
"requests": 34
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:28:55.514777+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 1051.03,
|
||||
"requests": 36
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:29:27.193474+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 857.1,
|
||||
"requests": 38
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:29:59.036359+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 865.17,
|
||||
"requests": 40
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:30:30.544858+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 714.51,
|
||||
"requests": 42
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:31:02.161454+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 782.59,
|
||||
"requests": 44
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:31:35.413556+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 789.57,
|
||||
"requests": 46
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:32:09.118559+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 901.22,
|
||||
"requests": 48
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:32:40.893263+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 900.57,
|
||||
"requests": 50
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:33:12.679890+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 863.33,
|
||||
"requests": 52
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:33:44.501781+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 1023.98,
|
||||
"requests": 54
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:34:16.365978+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 750.53,
|
||||
"requests": 56
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:34:48.286153+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 960.3,
|
||||
"requests": 58
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:35:19.863722+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 793.72,
|
||||
"requests": 60
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:35:51.502123+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 768.31,
|
||||
"requests": 63
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:36:23.231024+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 849.43,
|
||||
"requests": 65
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:36:55.246965+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 948.16,
|
||||
"requests": 67
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:37:26.935618+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 905.51,
|
||||
"requests": 69
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:38:00.920175+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 2599.19,
|
||||
"requests": 71
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:38:32.921709+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 895.96,
|
||||
"requests": 73
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:39:05.019900+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 1029.07,
|
||||
"requests": 75
|
||||
},
|
||||
{
|
||||
"ts": "2026-07-26T18:39:39.166081+00:00",
|
||||
"status": "up",
|
||||
"code": 200,
|
||||
"latency_ms": 881.37,
|
||||
"requests": 77
|
||||
}
|
||||
]
|
||||
|
||||
المرجع في مشكلة جديدة
حظر مستخدم