mirror of
https://github.com/melgarafael/DeskcommCRM.git
synced 2026-10-02 09:34:46 +08:00
O serviço `worker` do compose de produção não tinha `image:`, só `build:`. As
consequências, todas medidas, encadeiam:
1. `docker compose pull` PULA serviço build-only ("Skipped - No image to be
pulled"), então o `dc pull` do install/update nunca o trazia;
2. `up -d` sem `--build` recria o contêiner sobre a imagem velha — provei num
compose stub: Dockerfile alterado, `up -d`, e o contêiner segue imprimindo
VERSAO-1;
3. logo o worker era construído na VPS de cada cliente (pnpm install de 82
pacotes) e NUNCA mais atualizado.
E ele não é acessório: `app/api/v1/cron/agent-dispatcher` é no-op permanente, o
agent-worker é o único consumidor de `ai_agent.dispatch_requested`. O runtime do
agente de IA — a feature-título — era a única peça que não recebia correção. O
QA de instalação real até registrou o sintoma sem tirar a conclusão
("a VPS realmente compila o worker", user-journey-map.md:295).
A mudança é ADITIVA por construção: `build:` continua ao lado de `image:`, e
medi que o Compose constrói localmente quando a imagem não existe no registry —
com e sem pull_policy. Registry fora do ar, ou clone anterior à primeira
publicação, caem no comportamento de hoje em vez de quebrar.
Também:
- scheduler vira imagem e para de rodar `apk add curl tzdata` a cada start. Num
serviço `restart: unless-stopped` isso amarrava a volta do cron à internet da
VPS e ao mirror do Alpine, justamente quando a máquina se recupera de algo;
- WAHA pinado em `latest-2026.7.2` — MESMO digest de `latest` hoje
(65e593e30bb7), então zero mudança de conteúdo; muda quem decide quando o
WhatsApp do cliente troca de motor;
- `srh` pinado por DIGEST, não por tag: `latest` (5b0bb923) e `0.0.10`
(65128347) são imagens DIFERENTES, então `:0.0.10` trocaria o binário do
parque — o risco que a pendência T6 mandava validar antes. O digest congela o
que já roda, sem incorrer nesse risco;
- `/api/v1/health` para de mentir. Lia `npm_package_version`, que é `undefined`
sob `CMD ["node","server.js"]`: toda instalação do mundo reportava "0.1.0". O
fallback agora é "desconhecido" — um campo que responde errado com confiança
desliga a pergunta, e é pior que um campo ausente.
O gate novo tem guarda do próprio instrumento: a primeira versão do teste de
pull_policy passava sem avaliar um único serviço (só cobrava tag imutável, e os
três apontam para tag móvel). Agora ele cobra os dois sentidos e falha se
avaliar menos que os três.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RKm2XcTcfgi1vdMcDYSRWa
29 lines
1.2 KiB
Docker
29 lines
1.2 KiB
Docker
# Scheduler dos crons — busybox crond batendo nas rotas /api/v1/cron/* pela rede
|
|
# interna do compose. Sem docker.sock: o contêiner não fala com o daemon, só HTTP.
|
|
#
|
|
# Existe como imagem (em vez de `alpine:3.20` + `apk add` no command do compose)
|
|
# porque instalar pacote em todo start amarra a recuperação do cron do cliente à
|
|
# internet da VPS e ao mirror do Alpine — dependência de rede num caminho que
|
|
# deveria ser offline. Doutrina: docs/doctrine/packaging.md, invariante 1.
|
|
FROM alpine:3.20
|
|
|
|
LABEL org.opencontainers.image.source="https://github.com/melgarafael/DeskcommCRM" \
|
|
org.opencontainers.image.licenses="MIT" \
|
|
org.opencontainers.image.title="DeskcommCRM scheduler"
|
|
|
|
ARG APP_VERSION=dev
|
|
ENV APP_VERSION=$APP_VERSION \
|
|
TZ=UTC
|
|
|
|
RUN apk add --no-cache curl tzdata
|
|
|
|
COPY docker/scheduler/entrypoint.sh /usr/local/bin/entrypoint.sh
|
|
RUN chmod +x /usr/local/bin/entrypoint.sh
|
|
|
|
# Prova de vida: se o crond morrer, o contêiner cai e o `restart: unless-stopped`
|
|
# o traz de volta. Sem isto, um crond morto fica indistinguível de um cron ocioso.
|
|
HEALTHCHECK --interval=60s --timeout=5s --start-period=10s --retries=3 \
|
|
CMD pgrep crond >/dev/null || exit 1
|
|
|
|
CMD ["/usr/local/bin/entrypoint.sh"]
|