fetch_recipe (SRC+REF/PR path) now read-only fetches published version tags from the public upstream into the mirror clone, so the upgrade stage finds a previous published version (mirror PR branches carry no tags → upgrade would skip). Guardrail-safe: only fetches tags, never pushes to the recipe repo; plain git so the bot token isn't sent to upstream. Adds the 6 D10 recipes to the bridge POLL_REPOS so !testme on their PRs triggers runs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
118 lines
4.6 KiB
Nix
118 lines
4.6 KiB
Nix
# 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
|
|
'';
|
|
|
|
# Content-derived tag so `docker stack deploy` rolls the service whenever bridge.py changes
|
|
# (a fixed `:latest` + unchanged stack spec does NOT roll — swarm sees no change).
|
|
imageTag = builtins.substring 0 12 (builtins.hashString "sha256"
|
|
(builtins.readFile ../bridge/bridge.py));
|
|
|
|
image = pkgs.dockerTools.buildLayeredImage {
|
|
name = "cc-ci-bridge";
|
|
tag = imageTag;
|
|
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:${imageTag}
|
|
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/<recipe>/ exists.
|
|
- POLL_INTERVAL=30
|
|
- POLL_REPOS=recipe-maintainers/cc-ci,recipe-maintainers/custom-html,recipe-maintainers/keycloak,recipe-maintainers/cryptpad,recipe-maintainers/matrix-synapse,recipe-maintainers/lasuite-docs,recipe-maintainers/n8n
|
|
- 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";
|
|
};
|
|
};
|
|
}
|