{
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/ https://git.sr.ht
'';
services.hydra-dev = {
enable = true;
hydraURL = "https://${cfg.domain}";
notificationSender = "hydra@mothership.fleet";
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
mkdir -p /var/lib/hydra/.ssh
cp /home/ccr/.ssh/id_rsa* /var/lib/hydra/.ssh/
chown -R hydra:hydra /var/lib/hydra/.ssh
mkdir -p /var/lib/hydra/queue-runner/.ssh
cp /home/ccr/.ssh/id_rsa* /var/lib/hydra/queue-runner/.ssh/
chown -R hydra-queue-runner:hydra /var/lib/hydra/queue-runner/.ssh
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}";
};
};
};
}