consolidate css/, badges into styles/ directory at project root
Some checks failed
ci/woodpecker/push/infra-ci Pipeline failed
ci/woodpecker/push/deploy Pipeline failed
coverage 99% coverage
ci/woodpecker/push/ci Pipeline was successful

Rename css/ → styles/ and add coverage badge SVG. CI updates
styles/coverage.svg in the repo via Gitea contents API so the
README badge renders without external URL dependencies.

All compose.yml mounts, .dockerignore, README, and docs refs updated.
This commit is contained in:
kert
2026-03-13 11:11:45 -04:00
parent a339d9b4e4
commit 05e8935b4a
19 changed files with 72 additions and 35 deletions

View File

@@ -19,6 +19,6 @@ trino/
traefik/
nginx/
gitea/
css/
styles/
.woodpecker/
.claude/

View File

@@ -1,6 +1,6 @@
# Homelab Stack
![coverage](http://gitea.homelab.fhirworx.io/api/packages/homelab/generic/coverage/latest/badge.svg)
![coverage](styles/coverage.svg)
Self-hosted infrastructure stack with GPU support, S3-compatible storage, Git server with container registry, CI/CD, data lakehouse, observability, documentation, and research tools. All services share a unified retro "throwback" theme injected via Traefik middleware.
@@ -100,18 +100,18 @@ Per-service CSS files handle service-specific overrides:
| File | Purpose |
|------|---------|
| `css/inject.css` | Global throwback palette + logo hiding (served as `throwback.css`) |
| `css/dashboard.css` | Dashboard tile grid layout |
| `css/gitea.css` | Gitea theme (registered as `throwback` in Gitea UI settings) |
| `css/woodpecker.css` | Woodpecker custom CSS (via `WOODPECKER_CUSTOM_CSS_FILE`) |
| `css/marimo.css` | Marimo notebook theme |
| `css/fav32.png` | 32x32 PNG favicon (canonical, injected by Traefik) |
| `css/favicon.png` | 64x64 PNG favicon |
| `css/fav16.png` | 16x16 PNG favicon |
| `css/apple-touch-icon.png` | 180x180 Apple touch icon |
| `css/favicon.svg` | SVG favicon (legacy) |
| `css/logo.svg` | Navbar logo for Gitea |
| `css/logo.png` | 256x256 PNG logo |
| `styles/inject.css` | Global throwback palette + logo hiding (served as `throwback.css`) |
| `styles/dashboard.css` | Dashboard tile grid layout |
| `styles/gitea.css` | Gitea theme (registered as `throwback` in Gitea UI settings) |
| `styles/woodpecker.css` | Woodpecker custom CSS (via `WOODPECKER_CUSTOM_CSS_FILE`) |
| `styles/marimo.css` | Marimo notebook theme |
| `styles/fav32.png` | 32x32 PNG favicon (canonical, injected by Traefik) |
| `styles/favicon.png` | 64x64 PNG favicon |
| `styles/fav16.png` | 16x16 PNG favicon |
| `styles/apple-touch-icon.png` | 180x180 Apple touch icon |
| `styles/favicon.svg` | SVG favicon (legacy) |
| `styles/logo.svg` | Navbar logo for Gitea |
| `styles/logo.png` | 256x256 PNG logo |
Grafana favicons are replaced via volume mounts over the default Grafana icons.
@@ -467,7 +467,7 @@ stack/
├── compose.yml # Docker Compose (all services)
├── .env # Environment variables (not in git)
├── README.md
├── css/ # Centralized theme assets
├── styles/ # Centralized theme assets
│ ├── inject.css # Global throwback CSS (served as throwback.css)
│ ├── dashboard.css # Dashboard tile grid
│ ├── gitea.css # Gitea throwback theme

View File

@@ -116,7 +116,7 @@ services:
- gitea_data:/var/lib/gitea
- gitea_config:/etc/gitea
- ./gitea/custom:/var/lib/gitea/custom
- ./css/gitea.css:/var/lib/gitea/custom/public/assets/css/theme-throwback.css:ro
- ./styles/gitea.css:/var/lib/gitea/custom/public/assets/css/theme-throwback.css:ro
ports:
- "2222:2222"
depends_on:
@@ -149,7 +149,7 @@ services:
- WOODPECKER_CUSTOM_CSS_FILE=/etc/woodpecker/custom.css
volumes:
- woodpecker_data:/var/lib/woodpecker
- ./css/woodpecker.css:/etc/woodpecker/custom.css:ro
- ./styles/woodpecker.css:/etc/woodpecker/custom.css:ro
depends_on:
- gitea
- postgres
@@ -185,7 +185,7 @@ services:
volumes:
- ./notebooks:/home/kert/notebooks
- ./notebooks/.marimo-config:/home/kert/.config/marimo
- ./css/marimo.css:/home/kert/.config/marimo/retro-arcade.css:ro
- ./styles/marimo.css:/home/kert/.config/marimo/retro-arcade.css:ro
- ./notebooks/home-page-DUMF8ZY4.js:/home/kert/workspace/.venv/lib/python3.13/site-packages/marimo/_static/assets/home-page-DUMF8ZY4.js:ro
- ./data:/home/kert/data
- ./zotero/data:/home/kert/zotero:ro
@@ -334,9 +334,9 @@ services:
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/index.html:/usr/share/nginx/html/index.html:ro
- ./css/dashboard.css:/usr/share/nginx/html/dashboard.css:ro
- ./css/inject.css:/usr/share/nginx/html/throwback.css:ro
- ./css/fav32.png:/usr/share/nginx/html/fav32.png:ro
- ./styles/dashboard.css:/usr/share/nginx/html/dashboard.css:ro
- ./styles/inject.css:/usr/share/nginx/html/throwback.css:ro
- ./styles/fav32.png:/usr/share/nginx/html/fav32.png:ro
security_opt:
- no-new-privileges:true
restart: unless-stopped
@@ -452,9 +452,9 @@ services:
- ./grafana/provisioning:/etc/grafana/provisioning:ro
- ./grafana/dashboards:/var/lib/grafana/dashboards:ro
- grafana_data:/var/lib/grafana
- ./css/favicon.svg:/usr/share/grafana/public/img/grafana_icon.svg:ro
- ./css/fav32.png:/usr/share/grafana/public/img/fav32.png:ro
- ./css/apple-touch-icon.png:/usr/share/grafana/public/img/apple-touch-icon.png:ro
- ./styles/favicon.svg:/usr/share/grafana/public/img/grafana_icon.svg:ro
- ./styles/fav32.png:/usr/share/grafana/public/img/fav32.png:ro
- ./styles/apple-touch-icon.png:/usr/share/grafana/public/img/apple-touch-icon.png:ro
depends_on:
- loki
- jaeger

View File

@@ -7,14 +7,19 @@ Usage:
# Post coverage as a Gitea commit status (reads GITEA_TOKEN, GITEA_URL,
# CI_COMMIT_SHA from environment):
uv run python dev/scripts/coverage_badge.py --post pytest.out
# Update styles/coverage.svg in the repo via Gitea API:
uv run python dev/scripts/coverage_badge.py --upload coverage.svg
"""
from __future__ import annotations
import base64
import json
import os
import re
import sys
import urllib.error
import urllib.request
TEMPLATE = """\
@@ -39,6 +44,8 @@ TEMPLATE = """\
</g>
</svg>"""
FILE_PATH = "styles/coverage.svg"
def color_for(pct: int) -> str:
if pct >= 95:
@@ -85,30 +92,40 @@ def post_status(pct: int) -> None:
def upload_badge(svg_path: str) -> None:
"""Update styles/coverage.svg in the repo via Gitea contents API."""
token, url = _gitea_env()
endpoint = f"{url}/api/packages/homelab/generic/coverage/latest/badge.svg"
data = open(svg_path, "rb").read()
# Delete existing package version first (ignore 404)
endpoint = f"{url}/api/v1/repos/homelab/stack/contents/{FILE_PATH}"
svg_data = open(svg_path, "rb").read()
content_b64 = base64.b64encode(svg_data).decode()
# Get current file SHA (needed for update)
sha = None
try:
req = urllib.request.Request(
f"{url}/api/packages/homelab/generic/coverage/latest",
endpoint,
headers={"Authorization": f"token {token}"},
method="DELETE",
)
urllib.request.urlopen(req)
with urllib.request.urlopen(req) as resp:
sha = json.loads(resp.read())["sha"]
except urllib.error.HTTPError:
pass
body = {"content": content_b64, "message": "update coverage badge [skip ci]"}
if sha:
body["sha"] = sha
method = "PUT" if sha else "POST"
req = urllib.request.Request(
endpoint,
data=data,
data=json.dumps(body).encode(),
headers={
"Authorization": f"token {token}",
"Content-Type": "image/svg+xml",
"Content-Type": "application/json",
},
method="PUT",
method=method,
)
with urllib.request.urlopen(req) as resp:
print(f"Uploaded badge SVG (HTTP {resp.status})")
print(f"Updated {FILE_PATH} in repo (HTTP {resp.status})")
def main() -> None:

View File

@@ -1,4 +1,4 @@
/* Throwback palette — matches css/inject.css */
/* Throwback palette — matches styles/inject.css */
:root {
--ifm-color-primary: #3366cc;

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

20
styles/coverage.svg Normal file
View File

@@ -0,0 +1,20 @@
<svg xmlns="http://www.w3.org/2000/svg" width="116" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="a">
<rect width="116" height="20" rx="3" fill="#fff"/>
</clipPath>
<g clip-path="url(#a)">
<path fill="#555" d="M0 0h65v20H0z"/>
<path fill="#4c1" d="M65 0h51v20H65z"/>
<path fill="url(#b)" d="M0 0h116v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11">
<text x="32.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
<text x="32.5" y="14">coverage</text>
<text x="90" y="15" fill="#010101" fill-opacity=".3">99%</text>
<text x="90" y="14">99%</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 822 B

View File

Before

Width:  |  Height:  |  Size: 741 B

After

Width:  |  Height:  |  Size: 741 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB