{ lib, config, pkgs, ... }: let cfg = config.services.my-hydra; toSpec = { name, owner, ... }: let spec = { enabled = 1; hidden = false; description = "Declarative specification jobset automatically generated"; checkinterval = 120; schedulingshares = 10000; enableemail = false; emailoverride = ""; keepnr = 1; nixexprinput = "src"; nixexprpath = "jobsets.nix"; inputs = { src = { type = "path"; value = pkgs.writeTextFile { name = "src"; text = builtins.readFile ./jobsets.nix; destination = "/jobsets.nix"; }; emailresponsible = false; }; repoInfoPath = { type = "path"; value = pkgs.writeTextFile { name = "repo"; text = builtins.toJSON { inherit name owner; }; }; emailresponsible = false; }; prs = { type = "githubpulls"; value = "${owner} ${name}"; emailresponsible = false; }; }; }; drv = pkgs.writeTextFile { name = "hydra-jobset-specification-${name}"; text = builtins.toJSON spec; destination = "/spec.json"; }; in "${drv}"; in { imports = [ ./config.nix ../nginx-base ]; options.services.my-hydra = { domain = lib.mkOption { type = lib.types.str; default = "hydra.aciceri.dev"; }; repos = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule ({ name, config, ... }: { options = { name = lib.mkOption { type = lib.types.str; default = name; }; owner = lib.mkOption { type = lib.types.str; default = "aciceri"; }; description = lib.mkOption { type = lib.types.str; default = config.homepage; }; homepage = lib.mkOption { type = lib.types.str; default = "https://github.com/${config.owner}/${config.name}"; }; reportStatus = lib.mkOption { type = lib.types.bool; default = true; }; }; })); default = {}; }; }; config = { # TODO manage `hydra` user ssh key declaratively nix.extraOptions = '' allowed-uris = https://github.com/ git://git.savannah.gnu.org/ ''; services.hydra = { enable = true; hydraURL = "https://${cfg.domain}"; notificationSender = "hydra@mothership.fleet"; buildMachinesFiles = []; useSubstitutes = true; extraConfig = '' include ${config.age.secrets.hydra-github-token.path} '' + (lib.concatMapStrings (repo: lib.optionalString repo.reportStatus '' jobs = ${repo.name}.* excludeBuildFromContext = 1 useShortContext = 1 '') (builtins.attrValues cfg.repos)); }; systemd.services.hydra-setup = { description = "Hydra CI setup"; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; wantedBy = ["multi-user.target"]; requires = ["hydra-init.service"]; after = ["hydra-init.service"]; environment = builtins.removeAttrs (config.systemd.services.hydra-init.environment) ["PATH"]; script = '' PATH=$PATH:${lib.makeBinPath (with pkgs; [yq-go curl config.services.hydra.package])} PASSWORD="$(cat ${config.age.secrets.hydra-admin-password.path})" if [ ! -e ~hydra/.setup-is-complete ]; then hydra-create-user admin \ --full-name "Andrea Ciceri" \ --email-address "andrea.ciceri@autistici.org" \ --password "$PASSWORD" \ --role admin touch ~hydra/.setup-is-complete fi curl --head -X GET --retry 5 --retry-connrefused --retry-delay 1 http://localhost:3000 CURRENT_REPOS=$(curl -s -H "Accept: application/json" http://localhost:3000 | yq ".[].name") DECLARED_REPOS="${lib.concatStringsSep " " (builtins.attrNames cfg.repos)}" curl -H "Accept: application/json" \ -H 'Origin: http://localhost:3000' \ -H 'Content-Type: application/json' \ -d "{\"username\": \"admin\", \"password\": \"$PASSWORD\"}" \ --request "POST" localhost:3000/login \ --cookie-jar cookie for repo in $CURRENT_REPOS; do echo $repo [[ ! "$DECLARED_REPOS" =~ (\ |^)$repo(\ |$) ]] && \ curl -H "Accept: application/json" \ --request "DELETE" \ --cookie cookie \ http://localhost:3000/project/$repo done '' + lib.concatMapStrings (repo: '' curl -H "Accept: application/json" \ -H 'Content-Type: application/json' \ --request "PUT" \ localhost:3000/project/${repo.name} \ --cookie cookie \ -d '{ "name": "${repo.name}", "displayname": "${repo.name}", "description": "${repo.description}", "homepage": "${repo.homepage}", "owner": "admin", "enabled": true, "visible": true, "declarative": { "file": "spec.json", "type": "path", "value": "${toSpec repo}" } }' '') (builtins.attrValues cfg.repos) + '' rm cookie ''; }; services.nginx.virtualHosts."${cfg.domain}" = { forceSSL = true; enableACME = true; locations."/" = { proxyPass = "http://127.0.0.1:${builtins.toString config.services.hydra.port}"; }; }; }; }