opencode-config/docs/decisions/061-pr-144-optional-badge-subtitle-entrypoint.md
Sergey 0d16b028fa
fix(draw-image): optional badge/subtitle + self-healing docker entrypoint (#144)
* 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>
2026-07-30 01:23:25 +03:00

3.1 KiB
Raw Blame History

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 = brokennode_modules/ (sharp, lucide-static) gitignored, Dockerfile не устанавливал deps для draw-image. После docker compose upCannot find module 'sharp'.

Решение

  1. Slot bg conditionalrenderSlotBackground() перенесён внутрь 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 entrypointdocker-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-образах.