diff --git a/nix/hosts/cc-ci-hetzner/configuration.nix b/nix/hosts/cc-ci-hetzner/configuration.nix index 1bf3e8b..388f293 100644 --- a/nix/hosts/cc-ci-hetzner/configuration.nix +++ b/nix/hosts/cc-ci-hetzner/configuration.nix @@ -22,6 +22,7 @@ ../../modules/drone-runner.nix ../../modules/bridge.nix ../../modules/dashboard.nix + ../../modules/reports.nix ../../modules/backupbot.nix ../../modules/harness.nix ../../modules/warm-keycloak.nix diff --git a/nix/modules/reports.nix b/nix/modules/reports.nix new file mode 100644 index 0000000..e734bfc --- /dev/null +++ b/nix/modules/reports.nix @@ -0,0 +1,73 @@ +# Recipe Report static site (report.ci.commoninternet.net): a public nginx serving the weekly +# "Recipe Report" HTML pages written to /var/lib/cc-ci-reports by the /recipe-report skill. No app, +# no secrets — just static files behind traefik + the wildcard TLS (same pattern as dashboard.nix, +# but a plain nginx:alpine since there's nothing to render server-side). Content is updated by writing +# files into /var/lib/cc-ci-reports; nginx serves them live (no redeploy needed). +{ pkgs, ... }: +let + reportsDir = "/var/lib/cc-ci-reports"; + + stack = pkgs.writeText "cc-ci-reports-stack.yml" '' + version: "3.8" + services: + app: + image: nginx:alpine + volumes: + - type: bind + source: ${reportsDir} + target: /usr/share/nginx/html + read_only: true + networks: + - proxy + deploy: + replicas: 1 + restart_policy: + condition: any + labels: + - "traefik.enable=true" + - "traefik.http.services.ccci-reports.loadbalancer.server.port=80" + - "traefik.http.routers.ccci-reports.rule=Host(`report.ci.commoninternet.net`)" + - "traefik.http.routers.ccci-reports.entrypoints=web-secure" + - "traefik.http.routers.ccci-reports.tls=true" + networks: + proxy: + external: true + ''; + + reconcile = pkgs.writeShellApplication { + name = "cc-ci-reconcile-reports"; + runtimeInputs = with pkgs; [ docker coreutils ]; + text = '' + mkdir -p ${reportsDir} + # Seed a placeholder index so the site serves something before the first report is generated. + if [ ! -f ${reportsDir}/index.html ]; then + cat > ${reportsDir}/index.html <<'HTML' + + + The Recipe Report + +

🌻 The Recipe Report

+

No reports yet — the first one is generated after the weekly recipe-upgrade run.

+ + HTML + fi + docker stack deploy --detach=true -c ${stack} ccci-reports + ''; + }; +in +{ + systemd.services.deploy-reports = { + description = "Reconcile the cc-ci Recipe Report static site (report.ci.commoninternet.net)"; + # Ordering-only: chain after the dashboard (proxy→…→dashboard→reports) to avoid concurrent + # docker-init races on a fresh host. + after = [ "deploy-dashboard.service" "deploy-proxy.service" "swarm-init.service" "docker.service" "network-online.target" ]; + requires = [ "swarm-init.service" "docker.service" ]; + wants = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "${reconcile}/bin/cc-ci-reconcile-reports"; + }; + }; +}