# Comment-bridge (§4.1): the `!testme` webhook receiver. Packaged as a Nix-built OCI image # (no Docker Hub pull) and run as a swarm service on `proxy`, routed by traefik at # ci.commoninternet.net/hook. Deployed by an idempotent-reconcile oneshot (same pattern as # proxy/drone). Secrets come from sops (/run/secrets) → swarm secrets the container mounts. { pkgs, ... }: let # bridge.py placed at /app/bridge.py inside the image. bridgeApp = pkgs.runCommand "cc-ci-bridge-app" { } '' mkdir -p $out/app cp ${../bridge/bridge.py} $out/app/bridge.py ''; image = pkgs.dockerTools.buildLayeredImage { name = "cc-ci-bridge"; tag = "latest"; contents = [ pkgs.python3 pkgs.cacert bridgeApp ]; config = { Cmd = [ "${pkgs.python3}/bin/python3" "/app/bridge.py" ]; Env = [ "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" ]; ExposedPorts = { "8080/tcp" = { }; }; }; }; stack = pkgs.writeText "cc-ci-bridge-stack.yml" '' version: "3.8" services: app: image: cc-ci-bridge:latest environment: - GITEA_API=https://git.autonomic.zone/api/v1 - DRONE_URL=https://drone.ci.commoninternet.net - CI_REPO=recipe-maintainers/cc-ci - BRIDGE_LISTEN=0.0.0.0:8080 # Polling is PRIMARY (outbound, read-only, always on); the /hook webhook is an optional # admin-registered push optimization deduped against the poller (§4.1). Enrollment = add # the repo to POLL_REPOS (csv) + ensure tests// exists. - POLL_INTERVAL=30 - POLL_REPOS=recipe-maintainers/cc-ci - HMAC_FILE=/run/secrets/webhook_hmac - DRONE_TOKEN_FILE=/run/secrets/drone_token - GITEA_TOKEN_FILE=/run/secrets/gitea_token secrets: - webhook_hmac - drone_token - gitea_token networks: - proxy deploy: replicas: 1 restart_policy: condition: any labels: - "traefik.enable=true" - "traefik.http.services.ccci-bridge.loadbalancer.server.port=8080" - "traefik.http.routers.ccci-bridge.rule=Host(`ci.commoninternet.net`) && PathPrefix(`/hook`)" - "traefik.http.routers.ccci-bridge.entrypoints=web-secure" - "traefik.http.routers.ccci-bridge.tls=true" networks: proxy: external: true secrets: webhook_hmac: external: true name: cc_ci_bridge_webhook_hmac_v1 drone_token: external: true name: cc_ci_bridge_drone_token_v1 gitea_token: external: true name: cc_ci_bridge_gitea_token_v1 ''; reconcile = pkgs.writeShellApplication { name = "cc-ci-reconcile-bridge"; runtimeInputs = with pkgs; [ docker coreutils ]; text = '' for s in webhook_hmac drone_token gitea_token; do if [ ! -r "/run/secrets/bridge_$s" ]; then echo "FATAL: /run/secrets/bridge_$s missing (rebuild ordering?)" >&2 exit 1 fi done # Load the Nix-built image into the local docker (idempotent; layers cached). docker load -i ${image} # Materialise swarm secrets from sops (immutable; create once at v1). ensure_secret() { docker secret inspect "$2" >/dev/null 2>&1 || docker secret create "$2" "$1" >/dev/null } ensure_secret /run/secrets/bridge_webhook_hmac cc_ci_bridge_webhook_hmac_v1 ensure_secret /run/secrets/bridge_drone_token cc_ci_bridge_drone_token_v1 ensure_secret /run/secrets/bridge_gitea_token cc_ci_bridge_gitea_token_v1 docker stack deploy --detach=true -c ${stack} ccci-bridge ''; }; in { systemd.services.deploy-bridge = { description = "Reconcile the cc-ci comment-bridge (!testme webhook) swarm service"; after = [ "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-bridge"; }; }; }