P18: config consolidation — fix #132 #133 #134 #137 #138 #139 #141

- stack.toml: add [platform] (domain, registry, repo, ci_email,
  host_ip, image_prefix) and [services] (gitea, woodpecker,
  rustfs, nessie, polaris, trino, grafana) sections
- Python clients: default URLs from cfg.services.* instead of
  hardcoded strings (gitea, woodpecker, rustfs, admin clients)
- diag reporter: resolve labels by name via Gitea API instead
  of hardcoded IDs; repo owner/name from cfg.platform.repo
- CI pipelines: YAML anchors &registry/&org for single source
  of truth; $REGISTRY/$ORG env vars in scan commands
- ci.yml: use $CI_REPO for git push URL and S3 badge target
- rebuild-all.yml: $IMAGE_PREFIX, $STACK_ROOT env vars
- deploy.yml: $CI_WORKSPACE for host volume mounts
This commit is contained in:
kert
2026-03-23 19:49:47 -04:00
parent 9f6683ab10
commit e801b987b6
12 changed files with 154 additions and 86 deletions

View File

@@ -55,11 +55,11 @@ steps:
from_secret: registry_pass from_secret: registry_pass
commands: commands:
- git config user.name woodpecker-ci - git config user.name woodpecker-ci
- git config user.email ci@fhirworx.io - git config user.email ci@${CI_REPO_OWNER}.io
- git add coverage.svg - git add coverage.svg
- git diff --cached --quiet && echo "no change" && exit 0 - git diff --cached --quiet && echo "no change" && exit 0
- git commit -m "update coverage badge [skip ci]" - git commit -m "update coverage badge [skip ci]"
- git push http://$REGISTRY_USER:$REGISTRY_PASS@gitea:3000/homelab/stack.git HEAD:main - git push http://$REGISTRY_USER:$REGISTRY_PASS@gitea:3000/${CI_REPO}.git HEAD:main
when: when:
- branch: main - branch: main
event: push event: push
@@ -77,7 +77,7 @@ steps:
secret_key: secret_key:
from_secret: s3_secret_key from_secret: s3_secret_key
source: "coverage.svg" source: "coverage.svg"
target: /badges/homelab/stack/ target: /badges/${CI_REPO}/
path_style: true path_style: true
overwrite: true overwrite: true
depends_on: depends_on:

View File

@@ -16,17 +16,22 @@ when:
variables: variables:
- &buildx_image woodpeckerci/plugin-docker-buildx:5-insecure - &buildx_image woodpeckerci/plugin-docker-buildx:5-insecure
- &registry gitea.homelab.fhirworx.io
- &org homelab
- &buildx_base - &buildx_base
buildkit_config: | buildkit_config: |
[registry."gitea.homelab.fhirworx.io"] [registry."gitea.homelab.fhirworx.io"]
http = true http = true
registry: gitea.homelab.fhirworx.io registry: *registry
username: username:
from_secret: registry_user from_secret: registry_user
password: password:
from_secret: registry_pass from_secret: registry_pass
- &trivy_scan - &trivy_scan
image: aquasec/trivy:latest image: aquasec/trivy:latest
environment:
REGISTRY: *registry
ORG: *org
- &diag_env - &diag_env
UV_PYTHON_PREFERENCE: only-system UV_PYTHON_PREFERENCE: only-system
UV_LINK_MODE: copy UV_LINK_MODE: copy
@@ -73,7 +78,7 @@ steps:
commands: commands:
- trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json - trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json
-o notebooks-scan.json -o notebooks-scan.json
"gitea.homelab.fhirworx.io/homelab/notebooks:sha-${CI_COMMIT_SHA:0:8}" "$REGISTRY/$ORG/notebooks:${CI_COMMIT_SHA:0:8}"
depends_on: depends_on:
- build-push-notebooks - build-push-notebooks
@@ -94,7 +99,7 @@ steps:
commands: commands:
- trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json - trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json
-o zotero-scan.json -o zotero-scan.json
"gitea.homelab.fhirworx.io/homelab/zotero:sha-${CI_COMMIT_SHA:0:8}" "$REGISTRY/$ORG/zotero:${CI_COMMIT_SHA:0:8}"
depends_on: depends_on:
- build-push-zotero - build-push-zotero
@@ -102,7 +107,7 @@ steps:
- name: prep-docs-context - name: prep-docs-context
image: alpine:3 image: alpine:3
volumes: volumes:
- /home/kert/stack/data:/host-data:ro - ${CI_WORKSPACE}/data:/host-data:ro
commands: commands:
- mkdir -p data - mkdir -p data
- cp /host-data/bib.sqlite data/ 2>/dev/null || true - cp /host-data/bib.sqlite data/ 2>/dev/null || true
@@ -125,7 +130,7 @@ steps:
commands: commands:
- trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json - trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json
-o docs-scan.json -o docs-scan.json
"gitea.homelab.fhirworx.io/homelab/docs:sha-${CI_COMMIT_SHA:0:8}" "$REGISTRY/$ORG/docs:${CI_COMMIT_SHA:0:8}"
depends_on: depends_on:
- build-push-docs - build-push-docs
@@ -146,7 +151,7 @@ steps:
commands: commands:
- trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json - trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json
-o api-scan.json -o api-scan.json
"gitea.homelab.fhirworx.io/homelab/api:sha-${CI_COMMIT_SHA:0:8}" "$REGISTRY/$ORG/api:${CI_COMMIT_SHA:0:8}"
depends_on: depends_on:
- build-push-api - build-push-api

View File

@@ -2,23 +2,31 @@
# Rebuilds all images with apt-get/apk upgrade, scans for vulns, # Rebuilds all images with apt-get/apk upgrade, scans for vulns,
# and auto-closes Gitea vuln issues when scans are clean. # and auto-closes Gitea vuln issues when scans are clean.
# Trigger: manual or weekly cron. # Trigger: manual or weekly cron.
#
# Registry host is defined ONCE as &registry — change it here to
# retarget all builds and scans.
when: when:
- event: [manual, cron] - event: [manual, cron]
variables: variables:
- &buildx_image woodpeckerci/plugin-docker-buildx:5-insecure - &buildx_image woodpeckerci/plugin-docker-buildx:5-insecure
- &registry gitea.homelab.fhirworx.io
- &org homelab
- &buildx_base - &buildx_base
buildkit_config: | buildkit_config: |
[registry."gitea.homelab.fhirworx.io"] [registry."gitea.homelab.fhirworx.io"]
http = true http = true
registry: gitea.homelab.fhirworx.io registry: *registry
username: username:
from_secret: registry_user from_secret: registry_user
password: password:
from_secret: registry_pass from_secret: registry_pass
- &trivy_scan - &trivy_scan
image: aquasec/trivy:latest image: aquasec/trivy:latest
environment:
REGISTRY: *registry
ORG: *org
- &diag_env - &diag_env
UV_PYTHON_PREFERENCE: only-system UV_PYTHON_PREFERENCE: only-system
UV_LINK_MODE: copy UV_LINK_MODE: copy
@@ -84,7 +92,7 @@ steps:
commands: commands:
- trivy image --image-src remote --insecure --severity HIGH,CRITICAL - trivy image --image-src remote --insecure --severity HIGH,CRITICAL
--exit-code 0 --format json -o notebooks-scan.json --exit-code 0 --format json -o notebooks-scan.json
"gitea.homelab.fhirworx.io/homelab/notebooks:hardened" "$REGISTRY/$ORG/notebooks:hardened"
depends_on: [build-notebooks] depends_on: [build-notebooks]
- name: scan-zotero - name: scan-zotero
@@ -92,7 +100,7 @@ steps:
commands: commands:
- trivy image --image-src remote --insecure --severity HIGH,CRITICAL - trivy image --image-src remote --insecure --severity HIGH,CRITICAL
--exit-code 0 --format json -o zotero-scan.json --exit-code 0 --format json -o zotero-scan.json
"gitea.homelab.fhirworx.io/homelab/zotero:hardened" "$REGISTRY/$ORG/zotero:hardened"
depends_on: [build-zotero] depends_on: [build-zotero]
- name: scan-docs - name: scan-docs
@@ -100,7 +108,7 @@ steps:
commands: commands:
- trivy image --image-src remote --insecure --severity HIGH,CRITICAL - trivy image --image-src remote --insecure --severity HIGH,CRITICAL
--exit-code 0 --format json -o docs-scan.json --exit-code 0 --format json -o docs-scan.json
"gitea.homelab.fhirworx.io/homelab/docs:hardened" "$REGISTRY/$ORG/docs:hardened"
depends_on: [build-docs] depends_on: [build-docs]
- name: scan-api - name: scan-api
@@ -108,7 +116,7 @@ steps:
commands: commands:
- trivy image --image-src remote --insecure --severity HIGH,CRITICAL - trivy image --image-src remote --insecure --severity HIGH,CRITICAL
--exit-code 0 --format json -o api-scan.json --exit-code 0 --format json -o api-scan.json
"gitea.homelab.fhirworx.io/homelab/api:hardened" "$REGISTRY/$ORG/api:hardened"
depends_on: [build-api] depends_on: [build-api]
# ── Close resolved vuln issues OR file new ones ──────────── # ── Close resolved vuln issues OR file new ones ────────────

View File

@@ -3,17 +3,23 @@ when:
variables: variables:
- &buildx_image woodpeckerci/plugin-docker-buildx:5-insecure - &buildx_image woodpeckerci/plugin-docker-buildx:5-insecure
- &registry gitea.homelab.fhirworx.io
- &org homelab
- &image_prefix fhirworx
- &buildx_base - &buildx_base
buildkit_config: | buildkit_config: |
[registry."gitea.homelab.fhirworx.io"] [registry."gitea.homelab.fhirworx.io"]
http = true http = true
registry: gitea.homelab.fhirworx.io registry: *registry
username: username:
from_secret: registry_user from_secret: registry_user
password: password:
from_secret: registry_pass from_secret: registry_pass
- &trivy_scan - &trivy_scan
image: aquasec/trivy:latest image: aquasec/trivy:latest
environment:
REGISTRY: *registry
ORG: *org
- &diag_env - &diag_env
UV_PYTHON_PREFERENCE: only-system UV_PYTHON_PREFERENCE: only-system
UV_LINK_MODE: copy UV_LINK_MODE: copy
@@ -46,11 +52,12 @@ steps:
- name: prep-docs-context - name: prep-docs-context
image: alpine:3 image: alpine:3
volumes: environment:
- /home/kert/stack/data:/host-data:ro STACK_ROOT:
from_secret: stack_root
commands: commands:
- mkdir -p data - mkdir -p data
- cp /host-data/bib.sqlite data/ 2>/dev/null || true - cp ${STACK_ROOT:-/home/kert/stack}/data/bib.sqlite data/ 2>/dev/null || true
- name: build-push-docs - name: build-push-docs
image: *buildx_image image: *buildx_image
@@ -92,7 +99,7 @@ steps:
commands: commands:
- trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json - trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json
-o notebooks-scan.json -o notebooks-scan.json
"gitea.homelab.fhirworx.io/homelab/notebooks:sha-${CI_COMMIT_SHA:0:8}" "$REGISTRY/$ORG/notebooks:${CI_COMMIT_SHA:0:8}"
depends_on: depends_on:
- build-push-notebooks - build-push-notebooks
@@ -101,7 +108,7 @@ steps:
commands: commands:
- trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json - trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json
-o zotero-scan.json -o zotero-scan.json
"gitea.homelab.fhirworx.io/homelab/zotero:sha-${CI_COMMIT_SHA:0:8}" "$REGISTRY/$ORG/zotero:${CI_COMMIT_SHA:0:8}"
depends_on: depends_on:
- build-push-zotero - build-push-zotero
@@ -110,7 +117,7 @@ steps:
commands: commands:
- trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json - trivy image --image-src remote --insecure --severity HIGH,CRITICAL --exit-code 0 --format json
-o api-scan.json -o api-scan.json
"gitea.homelab.fhirworx.io/homelab/api:sha-${CI_COMMIT_SHA:0:8}" "$REGISTRY/$ORG/api:${CI_COMMIT_SHA:0:8}"
depends_on: depends_on:
- build-push-api - build-push-api
@@ -132,19 +139,27 @@ steps:
- name: deploy - name: deploy
image: docker:cli image: docker:cli
environment:
REGISTRY: *registry
ORG: *org
IMAGE_PREFIX: *image_prefix
STACK_ROOT:
from_secret: stack_root
volumes: volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock - /run/user/1000/docker.sock:/var/run/docker.sock
- /home/kert/stack:/home/kert/stack
commands: commands:
- cd /home/kert/stack - STACK_ROOT=${STACK_ROOT:-/home/kert/stack}
- cd $STACK_ROOT
- TAG=${CI_COMMIT_SHA:0:8} - TAG=${CI_COMMIT_SHA:0:8}
- FQDN=gitea.homelab.fhirworx.io
- for SVC in notebooks zotero docs api mc; do - for SVC in notebooks zotero docs api mc; do
docker pull $FQDN/homelab/$SVC:$TAG && docker pull $REGISTRY/$ORG/$SVC:$TAG &&
docker tag $FQDN/homelab/$SVC:$TAG fhirworx/$SVC:$TAG; docker tag $REGISTRY/$ORG/$SVC:$TAG $IMAGE_PREFIX/$SVC:$TAG;
done done
- sed -i "s/^COMMIT_SHA=.*/COMMIT_SHA=$TAG/" .env 2>/dev/null || echo "COMMIT_SHA=$TAG" >> .env - sed -i "s/^COMMIT_SHA=.*/COMMIT_SHA=$TAG/" .env 2>/dev/null || echo "COMMIT_SHA=$TAG" >> .env
- docker compose up -d --remove-orphans - docker compose up -d --remove-orphans
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
- /home/kert/stack:/home/kert/stack
depends_on: depends_on:
- scan-notebooks - scan-notebooks
- scan-zotero - scan-zotero

View File

@@ -106,7 +106,7 @@ def _promote_to_iceberg(
catalog_uri=lake.nessie.catalog_uri, catalog_uri=lake.nessie.catalog_uri,
warehouse=lake.warehouse, warehouse=lake.warehouse,
properties={ properties={
"s3.endpoint": cfg.lake.get("s3_endpoint", "http://rustfs:9000"), "s3.endpoint": cfg.lake.get("s3_endpoint", cfg.services.rustfs),
"s3.path-style-access": "true", "s3.path-style-access": "true",
}, },
) )

View File

@@ -69,11 +69,13 @@ def verify_postgres(
return errors return errors
def verify_gitea( def verify_gitea(values: dict[str, str], *, base_url: str = "") -> list[str]:
values: dict[str, str], *, base_url: str = "http://gitea:3000/api/v1"
) -> list[str]:
"""Verify Gitea API is reachable with the current token.""" """Verify Gitea API is reachable with the current token."""
errors = [] errors = []
if not base_url:
from conf import cfg
base_url = f"{cfg.services.gitea}/api/v1"
token = values.get("GITEA_TOKEN", "") token = values.get("GITEA_TOKEN", "")
if not token: if not token:
errors.append("gitea: no GITEA_TOKEN") errors.append("gitea: no GITEA_TOKEN")
@@ -92,8 +94,12 @@ def verify_gitea(
return errors return errors
def verify_rustfs(*, endpoint: str = "http://rustfs:9000") -> list[str]: def verify_rustfs(*, endpoint: str = "") -> list[str]:
"""Verify RustFS is reachable (returns 403 = alive, needs auth).""" """Verify RustFS is reachable (returns 403 = alive, needs auth)."""
if not endpoint:
from conf import cfg
endpoint = cfg.services.rustfs
errors = [] errors = []
try: try:
resp = httpx.get(f"{endpoint}/minio/health/live", timeout=10) resp = httpx.get(f"{endpoint}/minio/health/live", timeout=10)

View File

@@ -209,12 +209,16 @@ def provision_gitea(
values: dict[str, str], values: dict[str, str],
*, *,
admin_user: str = "kert", admin_user: str = "kert",
base_url: str = "http://gitea:3000/api/v1", base_url: str = "",
) -> str: ) -> str:
"""Rotate Gitea admin password and create a fresh API token. """Rotate Gitea admin password and create a fresh API token.
Uses the Gitea HTTP API exclusively — no docker exec needed. Uses the Gitea HTTP API exclusively — no docker exec needed.
""" """
if not base_url:
from conf import cfg
base_url = f"{cfg.services.gitea}/api/v1"
pw = values["GITEA_ADMIN_PASSWORD"] pw = values["GITEA_ADMIN_PASSWORD"]
current_token = values.get("GITEA_TOKEN", "") current_token = values.get("GITEA_TOKEN", "")
@@ -297,9 +301,13 @@ def provision_woodpecker(
values: dict[str, str], values: dict[str, str],
*, *,
repo_id: int = 1, repo_id: int = 1,
base_url: str = "http://woodpecker-server:8000/api", base_url: str = "",
) -> None: ) -> None:
"""Sync rotated credentials to Woodpecker repo secrets.""" """Sync rotated credentials to Woodpecker repo secrets."""
if not base_url:
from conf import cfg
base_url = f"{cfg.services.woodpecker}/api"
token = _get_woodpecker_token(values) token = _get_woodpecker_token(values)
wp = WoodpeckerClient(token, base_url=base_url) wp = WoodpeckerClient(token, base_url=base_url)

View File

@@ -20,9 +20,13 @@ class GiteaClient(Client):
self, self,
token: str, token: str,
*, *,
base_url: str = "http://gitea:3000/api/v1", base_url: str = "",
**kw, **kw,
) -> None: ) -> None:
if not base_url:
from conf import cfg
base_url = f"{cfg.services.gitea}/api/v1"
super().__init__(base_url, **kw) super().__init__(base_url, **kw)
self._token = token self._token = token
@@ -94,3 +98,13 @@ class GiteaClient(Client):
def create_issue(self, owner: str, repo: str, body: dict) -> dict: def create_issue(self, owner: str, repo: str, body: dict) -> dict:
return self.post(f"/repos/{owner}/{repo}/issues", json=body).json() return self.post(f"/repos/{owner}/{repo}/issues", json=body).json()
def list_labels(self, owner: str, repo: str) -> list[dict]:
return self.get(f"/repos/{owner}/{repo}/labels", params={"limit": 50}).json()
def resolve_labels(self, owner: str, repo: str, names: list[str]) -> list[int]:
"""Resolve label names to IDs. Unknown names are silently skipped."""
if not hasattr(self, "_label_cache"):
labels = self.list_labels(owner, repo)
self._label_cache = {l["name"]: l["id"] for l in labels}
return [self._label_cache[n] for n in names if n in self._label_cache]

View File

@@ -30,9 +30,13 @@ class RustFSClient(Client):
access_key: str, access_key: str,
secret_key: str, secret_key: str,
*, *,
base_url: str = "http://rustfs:9000", base_url: str = "",
**kw, **kw,
) -> None: ) -> None:
if not base_url:
from conf import cfg
base_url = cfg.services.rustfs
super().__init__(base_url, **kw) super().__init__(base_url, **kw)
self._access_key = access_key self._access_key = access_key
self._secret_key = secret_key self._secret_key = secret_key
@@ -103,9 +107,13 @@ class RustFSAdmin(Client):
access_key: str, access_key: str,
secret_key: str, secret_key: str,
*, *,
base_url: str = "http://rustfs:9000/minio/v2", base_url: str = "",
**kw, **kw,
) -> None: ) -> None:
if not base_url:
from conf import cfg
base_url = f"{cfg.services.rustfs}/minio/v2"
super().__init__(base_url, **kw) super().__init__(base_url, **kw)
self._access_key = access_key self._access_key = access_key
self._secret_key = secret_key self._secret_key = secret_key

View File

@@ -20,9 +20,13 @@ class WoodpeckerClient(Client):
self, self,
token: str, token: str,
*, *,
base_url: str = "http://woodpecker-server:8000/api", base_url: str = "",
**kw, **kw,
) -> None: ) -> None:
if not base_url:
from conf import cfg
base_url = f"{cfg.services.woodpecker}/api"
super().__init__(base_url, **kw) super().__init__(base_url, **kw)
self._token = token self._token = token

View File

@@ -16,40 +16,23 @@ import logging
import os import os
import sys import sys
from conf import cfg
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
# ── Label ID mapping ────────────────────────────────────────────── # ── Label name patterns ───────────────────────────────────────────
# These IDs correspond to labels created in the Gitea repo. PIPELINE_NAMES = ["ci", "deploy", "harden", "infra-ci", "rebuild-all", "release"]
PIPELINE_LABELS: dict[str, int] = { STEP_KEYWORDS = [
"ci": 14, "lint",
"deploy": 15, "test",
"harden": 16, "build",
"infra-ci": 17, "scan",
"rebuild-all": 18, "deploy",
"release": 19, "publish",
} "hadolint",
"validate",
STEP_LABELS: dict[str, int] = { ]
"lint": 20, IMAGE_KEYWORDS = ["api", "notebooks", "zotero", "docs", "mc"]
"test": 21,
"build": 22,
"scan": 23,
"deploy": 24,
"publish": 25,
"hadolint": 26,
"validate": 27,
}
IMAGE_LABELS: dict[str, int] = {
"api": 28,
"notebooks": 29,
"zotero": 30,
"docs": 31,
"mc": 32,
}
INFRA_LABEL = 33
CI_LABEL = 13 # existing "ci" label
def _get_env(name: str) -> str: def _get_env(name: str) -> str:
@@ -59,30 +42,30 @@ def _get_env(name: str) -> str:
return val return val
def _resolve_labels(pipeline_name: str, step_name: str) -> list[int]: def _resolve_label_names(pipeline_name: str, step_name: str) -> list[str]:
"""Resolve label IDs from pipeline and step names.""" """Resolve label names from pipeline and step names."""
labels: list[int] = [CI_LABEL] names: list[str] = ["ci"]
# Pipeline label # Pipeline label
for key, lid in PIPELINE_LABELS.items(): for key in PIPELINE_NAMES:
if key in pipeline_name: if key in pipeline_name:
labels.append(lid) names.append(f"pipeline:{key}")
break break
# Step category label — match the broadest category # Step category label
step_lower = step_name.lower() step_lower = step_name.lower()
for key, lid in STEP_LABELS.items(): for key in STEP_KEYWORDS:
if key in step_lower: if key in step_lower:
labels.append(lid) names.append(f"step:{key}")
break break
# Image label — detect which image the step relates to # Image label
for key, lid in IMAGE_LABELS.items(): for key in IMAGE_KEYWORDS:
if key in step_lower: if key in step_lower:
labels.append(lid) names.append(f"image:{key}")
break break
return labels return names
def _pipeline_name_from_env() -> str: def _pipeline_name_from_env() -> str:
@@ -126,7 +109,8 @@ def main() -> int:
if "/" in repo_name: if "/" in repo_name:
owner, repo = repo_name.split("/", 1) owner, repo = repo_name.split("/", 1)
else: else:
owner, repo = "homelab", "stack" parts = cfg.platform.repo.split("/", 1)
owner, repo = parts[0], parts[1]
pipeline_name = _pipeline_name_from_env() pipeline_name = _pipeline_name_from_env()
@@ -135,9 +119,7 @@ def main() -> int:
wp = WoodpeckerClient( wp = WoodpeckerClient(
gitea_token, gitea_token,
base_url=os.environ.get( base_url=os.environ.get("CI_WOODPECKER_URL", f"{cfg.services.woodpecker}/api"),
"CI_WOODPECKER_URL", "http://woodpecker-server:8000/api"
),
) )
rid = int(repo_id) rid = int(repo_id)
@@ -167,7 +149,8 @@ def main() -> int:
log.info("Processing failed step: %s (id=%d)", step_name, step_id) log.info("Processing failed step: %s (id=%d)", step_name, step_id)
labels = _resolve_labels(pipeline_name, step_name) label_names = _resolve_label_names(pipeline_name, step_name)
labels = gitea.resolve_labels(owner, repo, label_names)
# Fetch logs — Woodpecker returns a list of log line dicts # Fetch logs — Woodpecker returns a list of log line dicts
try: try:

View File

@@ -13,6 +13,23 @@
# the [db], [storage], and [lake] sections supply the concrete # the [db], [storage], and [lake] sections supply the concrete
# connection details for the selected backend. # connection details for the selected backend.
[platform]
domain = "homelab.fhirworx.io"
registry = "gitea.homelab.fhirworx.io"
image_prefix = "fhirworx"
repo = "homelab/stack"
ci_email = "ci@fhirworx.io"
host_ip = "192.168.1.192"
[services]
gitea = "http://gitea:3000"
woodpecker = "http://woodpecker-server:8000"
rustfs = "http://rustfs:9000"
nessie = "http://nessie:19120"
polaris = "http://polaris:8181"
trino = "http://trino:8080"
grafana = "http://grafana:3000"
[context] [context]
active = "local" active = "local"