5f2b485390
Editorial-briefing favicon: 32×32 SVG, Australis palette. Cyan masthead-rule across the top echoes the page's aurora-rule, four descending text-line indicators below evoke a newspaper column. Reads cleanly at 16×16 (the typical browser tab size). Static markup only — no script, no animation — so all browsers honor it for tab + bookmark icons. Linked from both digest.html.j2 and archive.html.j2 with the proper type="image/svg+xml" attribute. Served by nginx from the bind-mounted /output dir alongside index.html and style.css. Deploy playbook also updated to copy the favicon into /output at deploy-time so a fresh deploy doesn't 404 on the icon before the first cron fire.
146 lines
5.2 KiB
YAML
146 lines
5.2 KiB
YAML
# Deploy news-digest (LLM-curated daily briefing) to ana-docker.
|
|
#
|
|
# Usage:
|
|
# scripts/elway ana-docker --playbook playbooks/deploy-news-digest.yaml
|
|
#
|
|
# Idempotent — every step is creates-/when-gated; rerun is safe.
|
|
#
|
|
# After first deploy, the .env still has CHANGE_ME for the Miniflux
|
|
# password. Edit it (see stacks/news-digest/README.md), then
|
|
# `docker compose up -d` again — the container will run the first
|
|
# digest at startup if /output is empty, so no need to wait for cron.
|
|
|
|
vars:
|
|
compose_dir: /opt/docker/compose/news-digest
|
|
output_dir: /opt/docker/data/news-digest
|
|
host_port: "8181"
|
|
|
|
steps:
|
|
# ── host-side dirs ──────────────────────────────────────────────────
|
|
|
|
- name: Ensure compose dir exists
|
|
shell: mkdir -p {{ compose_dir }}
|
|
creates: "{{ compose_dir }}"
|
|
|
|
- name: Ensure output dir exists (bind-mounted into both containers)
|
|
shell: mkdir -p {{ output_dir }}
|
|
creates: "{{ output_dir }}"
|
|
|
|
# ── deploy build context (compose, env, dockerfile, app, templates) ──
|
|
|
|
- name: Upload compose.yaml
|
|
upload:
|
|
src: stacks/news-digest/compose.yaml
|
|
dest: "{{ compose_dir }}/compose.yaml"
|
|
mode: "0644"
|
|
|
|
- name: Upload Dockerfile
|
|
upload:
|
|
src: stacks/news-digest/Dockerfile
|
|
dest: "{{ compose_dir }}/Dockerfile"
|
|
mode: "0644"
|
|
|
|
- name: Upload digest.py
|
|
upload:
|
|
src: stacks/news-digest/digest.py
|
|
dest: "{{ compose_dir }}/digest.py"
|
|
mode: "0644"
|
|
|
|
- name: Upload entrypoint.sh
|
|
upload:
|
|
src: stacks/news-digest/entrypoint.sh
|
|
dest: "{{ compose_dir }}/entrypoint.sh"
|
|
mode: "0755"
|
|
|
|
- name: Upload run-digest.sh
|
|
upload:
|
|
src: stacks/news-digest/run-digest.sh
|
|
dest: "{{ compose_dir }}/run-digest.sh"
|
|
mode: "0755"
|
|
|
|
- name: Upload crontab
|
|
upload:
|
|
src: stacks/news-digest/crontab
|
|
dest: "{{ compose_dir }}/crontab"
|
|
mode: "0644"
|
|
|
|
- name: Ensure templates dir exists
|
|
shell: mkdir -p {{ compose_dir }}/templates
|
|
creates: "{{ compose_dir }}/templates"
|
|
|
|
- name: Upload templates/digest.html.j2
|
|
upload:
|
|
src: stacks/news-digest/templates/digest.html.j2
|
|
dest: "{{ compose_dir }}/templates/digest.html.j2"
|
|
mode: "0644"
|
|
|
|
- name: Upload templates/style.css
|
|
upload:
|
|
src: stacks/news-digest/templates/style.css
|
|
dest: "{{ compose_dir }}/templates/style.css"
|
|
mode: "0644"
|
|
|
|
- name: Upload templates/favicon.svg
|
|
upload:
|
|
src: stacks/news-digest/templates/favicon.svg
|
|
dest: "{{ compose_dir }}/templates/favicon.svg"
|
|
mode: "0644"
|
|
|
|
- name: Seed .env from template (only if absent)
|
|
upload:
|
|
src: stacks/news-digest/.env.example
|
|
dest: "{{ compose_dir }}/.env"
|
|
mode: "0644"
|
|
when: "[ ! -f {{ compose_dir }}/.env ]"
|
|
|
|
# ── build + bring up ────────────────────────────────────────────────
|
|
# The Dockerfile expects style.css to be inside the image too (it's
|
|
# copied via `COPY templates /app/templates`). nginx serves the
|
|
# generated index.html alongside its own copy of style.css from
|
|
# /output, so the worker writes a copy of style.css into /output too.
|
|
|
|
- name: docker compose build (~2-3 min first time)
|
|
# DOCKER_BUILDKIT=0 forces the legacy build path. ana-docker is on
|
|
# docker 20.10 (Debian package) which doesn't carry the buildx
|
|
# driver versions our newer client expects → "client version 1.52
|
|
# is too new" without this fallback. Strip the noisy per-step
|
|
# download lines for log readability.
|
|
shell: |
|
|
set -o pipefail
|
|
cd {{ compose_dir }} && DOCKER_BUILDKIT=0 docker compose build 2>&1 \
|
|
| grep -vE '^Step [0-9]+/[0-9]+ : (RUN|COPY)|^Removing intermediate|^ ---> |^ ---> Running|Collecting|Downloading|Requirement|Using cached|Installing collected|Successfully (installed|built)|━'
|
|
|
|
- name: Pre-stage style.css + favicon.svg into /output for nginx
|
|
# The worker writes HTML that references "style.css" and
|
|
# "favicon.svg" (relative). Neither is generated dynamically;
|
|
# nginx serves whichever copy lands at /usr/share/nginx/html/.
|
|
# Copy both from the templates dir at deploy-time.
|
|
shell: |
|
|
cp -f {{ compose_dir }}/templates/style.css {{ output_dir }}/style.css
|
|
cp -f {{ compose_dir }}/templates/favicon.svg {{ output_dir }}/favicon.svg
|
|
|
|
- name: docker compose up -d
|
|
shell: cd {{ compose_dir }} && docker compose up -d
|
|
|
|
- name: Wait for nginx to serve /
|
|
shell: |
|
|
for i in $(seq 1 30); do
|
|
curl -sf -o /dev/null --max-time 3 http://localhost:{{ host_port }}/ && exit 0
|
|
sleep 2
|
|
done
|
|
exit 1
|
|
changed_when: "false"
|
|
|
|
verify:
|
|
- name: nginx returns 200 on /
|
|
shell: curl -sf -o /dev/null http://localhost:{{ host_port }}/
|
|
changed_when: "false"
|
|
|
|
- name: Both containers running
|
|
shell: docker inspect news-digest-worker news-digest-web --format '{{.State.Status}}' | grep -c running | grep -q '^2$'
|
|
changed_when: "false"
|
|
|
|
- name: news-digest-web on traefik-net (homepage discovery)
|
|
shell: docker inspect news-digest-web --format '{{json .NetworkSettings.Networks}}' | grep -q traefik-net
|
|
changed_when: "false"
|