* fix(draw-image): conditional slot bg + optional subtitle in buildSvg * test(draw-image): optional badge/subtitle unit+integration+e2e * feat(docker): self-healing entrypoint shim + .dockerignore * test(draw-image): pytest assert --subtitle omitted when absent * docs(handoff): optional badge/subtitle + docker entrypoint handoff+ADR * docs(handoff): set PR number 144 * docs(project-map): fix PR refs 143 to 144 --------- Co-authored-by: opencode-agent <agent@opencode.local>
22 lines
No EOL
3.1 KiB
Markdown
22 lines
No EOL
3.1 KiB
Markdown
# ADR-061: Optional badge/subtitle + self-healing docker entrypoint (PR#144)
|
||
|
||
## Статус
|
||
Accepted (2026-07-29)
|
||
|
||
## Контекст
|
||
PR#134 (ADR-060) ввёл SVG template renderer для on-brand covers. При тестовом рендере выявлены три проблемы:
|
||
|
||
1. **Пустой badge рисует квадрат** — `renderSlotBackground()` вызывался для каждого слота с bg-спекой ДО проверки контента. Cover без `--slots badge=...` получал пустой `<rect fill="#121212" stroke="#ccff00">` в районе (780, 780).
|
||
2. **Subtitle оставляет пустой `<text>`** — `{{subtitle}}` заменялся на пустую строку безусловно, но `<text>` элемент оставался в DOM. Большинство cover'ов — title-only.
|
||
3. **Fresh clone = broken** — `node_modules/` (sharp, lucide-static) gitignored, Dockerfile не устанавливал deps для draw-image. После `docker compose up` → `Cannot find module 'sharp'`.
|
||
|
||
## Решение
|
||
1. **Slot bg conditional** — `renderSlotBackground()` перенесён внутрь `if (value)` блока в `buildSvg()`. bg/border рендерится ТОЛЬКО если слот заполнен контентом.
|
||
2. **Subtitle conditional** — если `args.subtitle` falsy, regex `/[^\n]*\{\{subtitle\}\}[^\n]*\n?/g` удаляет всю `<text>` строку с `{{subtitle}}` из шаблона ДО подстановки. При наличии subtitle — обычная подстановка `escapeXml(args.subtitle)`.
|
||
3. **Self-healing entrypoint** — `docker-entrypoint.sh` (POSIX `#!/bin/sh`) проверяет `node_modules/sharp`, при отсутствии запускает `npm ci --prefix` (continue-on-error: WARN в логах, контейнер стартует), затем `exec opencode "$@"`. Dockerfile `ENTRYPOINT` → entrypoint shim. `.dockerignore` исключает `app_data/`, `.git`, `**/node_modules` из build context.
|
||
|
||
## Альтернативы
|
||
- **Install draw-image deps в Dockerfile (RUN npm ci)** — отвергнуто: node_modules gitignored, COPY не сработает; `npm ci` в build-time требует COPY package.json + package-lock.json из `.opencode/draw-image/`, усложняет Dockerfile. Self-healing entrypoint — одноразовая задержка при первом старте, потом skip.
|
||
- **Pre-build hook (postinstall sync-lucide в Dockerfile)** — уже работает через entrypoint: `npm ci` запускает postinstall (`sync-lucide.mjs`), восстанавливает 2007 иконок.
|
||
- **Удалить `<text>` subtitle через DOM-парсер (xmldom)** — overkill для одной строки. Regex по строке — достаточно, шаблон контролируемый.
|
||
- **`set -euo pipefail` в entrypoint** — отвергнуто: `npm ci` failure (нет сети) должен быть continue-on-error, иначе контейнер не стартует. POSIX `#!/bin/sh` для портативности в slim-образах. |