FROM node:22-alpine WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci --omit=optional --no-audit --no-fund COPY tsconfig.json vitest.config.ts ./ COPY src ./src COPY static ./static ENV BOARD_DIST_DIR=/output ENV BOARD_TICK_SEC=300 # Healthcheck: index.html exists AND was written within the last (tick * 2)s. # If render is stuck, container goes unhealthy → operator sees it in # `docker ps` and `docker compose ps` without parsing logs. HEALTHCHECK --interval=60s --timeout=10s --start-period=120s --retries=2 \ CMD [ -f /output/index.html ] \ && [ $(( $(date +%s) - $(stat -c %Y /output/index.html) )) -lt $((BOARD_TICK_SEC * 2)) ] \ || exit 1 # Failures go to stderr so `docker compose logs` clearly flags them; # `|| true` prevents the loop from exiting (we want it to keep retrying). ENTRYPOINT ["/bin/sh", "-c", "while true; do node --experimental-strip-types src/cli.ts \"$BOARD_CONFIG_PATH\" || >&2 echo \"[$(date -Iseconds)] render failed; will retry in ${BOARD_TICK_SEC}s\"; sleep ${BOARD_TICK_SEC}; done"]