Compare commits

...

2 Commits

Author SHA1 Message Date
root
c900fc476f fix: add forest, mono, cyber to settings API validThemes allowlist
All checks were successful
Release / Test backend (push) Successful in 37s
Release / Check ui (push) Successful in 1m40s
Release / Docker (push) Successful in 5m43s
Release / Gitea Release (push) Successful in 31s
2026-04-07 12:09:56 +05:00
root
d612b40fdb ci: consolidate Docker jobs into one + comment out sourcemaps step
- Merged docker-backend, docker-runner, docker-ui, docker-caddy into a
  single 'docker' job. Docker Hub is now authenticated once; the credential
  in ~/.docker/config.json is reused by all four build-push-action steps.
  Eliminates 3 redundant login, checkout, setup-buildx, and scheduler
  round-trips. Builds still run sequentially within the job so inline layer
  cache pushes don't race each other.

- docker-ui now downloads the plain ui-build artifact from check-ui directly
  (previously it depended on upload-sourcemaps which produced ui-build-injected).

- release job now only needs: [docker] instead of the previous 5-job list.

- Entire upload-sourcemaps job commented out as requested. Re-enable by
  uncommenting the job block and adding 'upload-sourcemaps' back to the
  docker job's needs list (also swap ui-build → ui-build-injected artifact).
2026-04-07 12:01:34 +05:00
2 changed files with 124 additions and 160 deletions

View File

@@ -62,24 +62,115 @@ jobs:
path: ui/build
retention-days: 1
# ── docker: backend ───────────────────────────────────────────────────────────
docker-backend:
name: Docker / backend
# ── ui: source map upload ─────────────────────────────────────────────────────
# Commented out — re-enable when GlitchTip source map uploads are needed again.
#
# upload-sourcemaps:
# name: Upload source maps
# runs-on: ubuntu-latest
# needs: [check-ui]
# steps:
# - name: Compute release version (strip leading v)
# id: ver
# run: |
# V="${{ gitea.ref_name }}"
# echo "version=${V#v}" >> "$GITHUB_OUTPUT"
#
# - name: Download build artifacts
# uses: actions/download-artifact@v3
# with:
# name: ui-build
# path: build
#
# - name: Install sentry-cli
# run: npm install -g @sentry/cli
#
# - name: Inject debug IDs into build artifacts
# run: sentry-cli sourcemaps inject ./build
# env:
# SENTRY_URL: https://errors.libnovel.cc/
# SENTRY_AUTH_TOKEN: ${{ secrets.GLITCHTIP_AUTH_TOKEN }}
# SENTRY_ORG: libnovel
# SENTRY_PROJECT: ui
#
# - name: Upload injected build (for docker-ui)
# uses: actions/upload-artifact@v3
# with:
# name: ui-build-injected
# path: build
# retention-days: 1
#
# - name: Create GlitchTip release
# run: sentry-cli releases new ${{ steps.ver.outputs.version }}
# env:
# SENTRY_URL: https://errors.libnovel.cc/
# SENTRY_AUTH_TOKEN: ${{ secrets.GLITCHTIP_AUTH_TOKEN }}
# SENTRY_ORG: libnovel
# SENTRY_PROJECT: ui
#
# - name: Upload source maps to GlitchTip
# run: sentry-cli sourcemaps upload ./build --release ${{ steps.ver.outputs.version }}
# env:
# SENTRY_URL: https://errors.libnovel.cc/
# SENTRY_AUTH_TOKEN: ${{ secrets.GLITCHTIP_AUTH_TOKEN }}
# SENTRY_ORG: libnovel
# SENTRY_PROJECT: ui
#
# - name: Finalize GlitchTip release
# run: sentry-cli releases finalize ${{ steps.ver.outputs.version }}
# env:
# SENTRY_URL: https://errors.libnovel.cc/
# SENTRY_AUTH_TOKEN: ${{ secrets.GLITCHTIP_AUTH_TOKEN }}
# SENTRY_ORG: libnovel
# SENTRY_PROJECT: ui
#
# - name: Prune old GlitchTip releases (keep latest 10)
# run: |
# set -euo pipefail
# KEEP=10
# OLD=$(curl -sf \
# -H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
# "$SENTRY_URL/api/0/organizations/$SENTRY_ORG/releases/?project=$SENTRY_PROJECT&per_page=100" \
# | python3 -c "
# import sys, json
# releases = json.load(sys.stdin)
# for r in releases[$KEEP:]:
# print(r['version'])
# " KEEP=$KEEP)
# for ver in $OLD; do
# echo "Deleting old release: $ver"
# sentry-cli releases delete "$ver" || true
# done
# env:
# SENTRY_URL: https://errors.libnovel.cc
# SENTRY_AUTH_TOKEN: ${{ secrets.GLITCHTIP_AUTH_TOKEN }}
# SENTRY_ORG: libnovel
# SENTRY_PROJECT: ui
# ── docker: all images in one job (single login) ──────────────────────────────
# backend, runner, ui, and caddy are built sequentially in one job so Docker
# Hub only needs to be authenticated once. This also eliminates 3 redundant
# checkout + setup-buildx + scheduler round-trips compared to separate jobs.
docker:
name: Docker
runs-on: ubuntu-latest
needs: [test-backend]
needs: [test-backend, check-ui]
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
# Single login — credential is written to ~/.docker/config.json and
# reused by all subsequent build-push-action steps in this job.
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Docker meta
id: meta
# ── backend ──────────────────────────────────────────────────────────────
- name: Docker meta / backend
id: meta-backend
uses: docker/metadata-action@v5
with:
images: ${{ secrets.DOCKER_USER }}/libnovel-backend
@@ -88,38 +179,23 @@ jobs:
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest
- name: Build and push
- name: Build and push / backend
uses: docker/build-push-action@v6
with:
context: backend
target: backend
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
tags: ${{ steps.meta-backend.outputs.tags }}
labels: ${{ steps.meta-backend.outputs.labels }}
build-args: |
VERSION=${{ steps.meta.outputs.version }}
VERSION=${{ steps.meta-backend.outputs.version }}
COMMIT=${{ gitea.sha }}
cache-from: type=registry,ref=${{ secrets.DOCKER_USER }}/libnovel-backend:latest
cache-to: type=inline
# ── docker: runner ────────────────────────────────────────────────────────────
docker-runner:
name: Docker / runner
runs-on: ubuntu-latest
needs: [test-backend]
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Docker meta
id: meta
# ── runner ───────────────────────────────────────────────────────────────
- name: Docker meta / runner
id: meta-runner
uses: docker/metadata-action@v5
with:
images: ${{ secrets.DOCKER_USER }}/libnovel-runner
@@ -128,115 +204,25 @@ jobs:
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest
- name: Build and push
- name: Build and push / runner
uses: docker/build-push-action@v6
with:
context: backend
target: runner
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
tags: ${{ steps.meta-runner.outputs.tags }}
labels: ${{ steps.meta-runner.outputs.labels }}
build-args: |
VERSION=${{ steps.meta.outputs.version }}
VERSION=${{ steps.meta-runner.outputs.version }}
COMMIT=${{ gitea.sha }}
cache-from: type=registry,ref=${{ secrets.DOCKER_USER }}/libnovel-runner:latest
cache-to: type=inline
# ── ui: source map upload ─────────────────────────────────────────────────────
upload-sourcemaps:
name: Upload source maps
runs-on: ubuntu-latest
needs: [check-ui]
steps:
- name: Compute release version (strip leading v)
id: ver
run: |
V="${{ gitea.ref_name }}"
echo "version=${V#v}" >> "$GITHUB_OUTPUT"
- name: Download build artifacts
# ── ui ───────────────────────────────────────────────────────────────────
- name: Download ui build artifacts
uses: actions/download-artifact@v3
with:
name: ui-build
path: build
- name: Install sentry-cli
run: npm install -g @sentry/cli
- name: Inject debug IDs into build artifacts
run: sentry-cli sourcemaps inject ./build
env:
SENTRY_URL: https://errors.libnovel.cc/
SENTRY_AUTH_TOKEN: ${{ secrets.GLITCHTIP_AUTH_TOKEN }}
SENTRY_ORG: libnovel
SENTRY_PROJECT: ui
- name: Upload injected build (for docker-ui)
uses: actions/upload-artifact@v3
with:
name: ui-build-injected
path: build
retention-days: 1
- name: Create GlitchTip release
run: sentry-cli releases new ${{ steps.ver.outputs.version }}
env:
SENTRY_URL: https://errors.libnovel.cc/
SENTRY_AUTH_TOKEN: ${{ secrets.GLITCHTIP_AUTH_TOKEN }}
SENTRY_ORG: libnovel
SENTRY_PROJECT: ui
- name: Upload source maps to GlitchTip
run: sentry-cli sourcemaps upload ./build --release ${{ steps.ver.outputs.version }}
env:
SENTRY_URL: https://errors.libnovel.cc/
SENTRY_AUTH_TOKEN: ${{ secrets.GLITCHTIP_AUTH_TOKEN }}
SENTRY_ORG: libnovel
SENTRY_PROJECT: ui
- name: Finalize GlitchTip release
run: sentry-cli releases finalize ${{ steps.ver.outputs.version }}
env:
SENTRY_URL: https://errors.libnovel.cc/
SENTRY_AUTH_TOKEN: ${{ secrets.GLITCHTIP_AUTH_TOKEN }}
SENTRY_ORG: libnovel
SENTRY_PROJECT: ui
- name: Prune old GlitchTip releases (keep latest 10)
run: |
set -euo pipefail
KEEP=10
OLD=$(curl -sf \
-H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
"$SENTRY_URL/api/0/organizations/$SENTRY_ORG/releases/?project=$SENTRY_PROJECT&per_page=100" \
| python3 -c "
import sys, json
releases = json.load(sys.stdin)
for r in releases[$KEEP:]:
print(r['version'])
" KEEP=$KEEP)
for ver in $OLD; do
echo "Deleting old release: $ver"
sentry-cli releases delete "$ver" || true
done
env:
SENTRY_URL: https://errors.libnovel.cc
SENTRY_AUTH_TOKEN: ${{ secrets.GLITCHTIP_AUTH_TOKEN }}
SENTRY_ORG: libnovel
SENTRY_PROJECT: ui
# ── docker: ui ────────────────────────────────────────────────────────────────
docker-ui:
name: Docker / ui
runs-on: ubuntu-latest
needs: [check-ui, upload-sourcemaps]
steps:
- uses: actions/checkout@v4
- name: Download injected build (debug IDs already embedded)
uses: actions/download-artifact@v3
with:
name: ui-build-injected
path: ui/build
- name: Allow build/ into Docker context (override .dockerignore)
@@ -244,16 +230,8 @@ jobs:
grep -v '^build$' ui/.dockerignore > ui/.dockerignore.tmp
mv ui/.dockerignore.tmp ui/.dockerignore
- uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Docker meta
id: meta
- name: Docker meta / ui
id: meta-ui
uses: docker/metadata-action@v5
with:
images: ${{ secrets.DOCKER_USER }}/libnovel-ui
@@ -262,38 +240,24 @@ jobs:
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest
- name: Build and push
- name: Build and push / ui
uses: docker/build-push-action@v6
with:
context: ui
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
tags: ${{ steps.meta-ui.outputs.tags }}
labels: ${{ steps.meta-ui.outputs.labels }}
build-args: |
BUILD_VERSION=${{ steps.meta.outputs.version }}
BUILD_VERSION=${{ steps.meta-ui.outputs.version }}
BUILD_COMMIT=${{ gitea.sha }}
BUILD_TIME=${{ gitea.event.head_commit.timestamp }}
PREBUILT=1
cache-from: type=registry,ref=${{ secrets.DOCKER_USER }}/libnovel-ui:latest
cache-to: type=inline
# ── docker: caddy ─────────────────────────────────────────────────────────────
docker-caddy:
name: Docker / caddy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Docker meta
id: meta
# ── caddy ────────────────────────────────────────────────────────────────
- name: Docker meta / caddy
id: meta-caddy
uses: docker/metadata-action@v5
with:
images: ${{ secrets.DOCKER_USER }}/libnovel-caddy
@@ -302,13 +266,13 @@ jobs:
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest
- name: Build and push
- name: Build and push / caddy
uses: docker/build-push-action@v6
with:
context: caddy
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
tags: ${{ steps.meta-caddy.outputs.tags }}
labels: ${{ steps.meta-caddy.outputs.labels }}
cache-from: type=registry,ref=${{ secrets.DOCKER_USER }}/libnovel-caddy:latest
cache-to: type=inline
@@ -316,7 +280,7 @@ jobs:
release:
name: Gitea Release
runs-on: ubuntu-latest
needs: [docker-backend, docker-runner, docker-ui, docker-caddy, upload-sourcemaps]
needs: [docker]
steps:
- uses: actions/checkout@v4
with:

View File

@@ -45,7 +45,7 @@ export const PUT: RequestHandler = async ({ request, locals }) => {
}
// theme is optional — if provided (and non-empty) it must be a known value
const validThemes = ['amber', 'slate', 'rose', 'light', 'light-slate', 'light-rose'];
const validThemes = ['amber', 'slate', 'rose', 'forest', 'mono', 'cyber', 'light', 'light-slate', 'light-rose'];
if (body.theme !== undefined && body.theme !== '' && !validThemes.includes(body.theme)) {
error(400, `Invalid theme — must be one of: ${validThemes.join(', ')}`);
}