vendored/hydra: update to match latest hydra module

This commit is contained in:
Infinidoge 2025-09-05 07:57:12 -04:00
parent 8d3664ceec
commit 6adab5bc47
Signed by: Infinidoge
SSH key fingerprint: SHA256:oAMyvotlNFraMmZmr+p6AxnNfW/GioTs1pOn3V4tQ7A

View file

@ -4,14 +4,11 @@
lib, lib,
... ...
}: }:
with lib;
let let
cfg = config.services.hydra; cfg = config.services.hydra;
inherit (cfg) baseDir; inherit (cfg) baseDir; # DIFF
hydraConf = pkgs.writeScript "hydra.conf" cfg.extraConfig; hydraConf = pkgs.writeScript "hydra.conf" cfg.extraConfig;
@ -24,11 +21,10 @@ let
env = env =
{ {
NIX_REMOTE = "daemon"; NIX_REMOTE = "daemon";
SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt"; # Remove in 16.03
PGPASSFILE = "${baseDir}/pgpass"; PGPASSFILE = "${baseDir}/pgpass";
NIX_REMOTE_SYSTEMS = concatStringsSep ":" cfg.buildMachinesFiles; NIX_REMOTE_SYSTEMS = lib.concatStringsSep ":" cfg.buildMachinesFiles;
} }
// optionalAttrs (cfg.smtpHost != null) { // lib.optionalAttrs (cfg.smtpHost != null) {
EMAIL_SENDER_TRANSPORT = "SMTP"; EMAIL_SENDER_TRANSPORT = "SMTP";
EMAIL_SENDER_TRANSPORT_host = cfg.smtpHost; EMAIL_SENDER_TRANSPORT_host = cfg.smtpHost;
} }
@ -43,7 +39,7 @@ let
COLUMNS = "80"; COLUMNS = "80";
PGPASSFILE = "${baseDir}/pgpass-www"; # grrr PGPASSFILE = "${baseDir}/pgpass-www"; # grrr
} }
// (optionalAttrs cfg.debugServer { DBIC_TRACE = "1"; }); // (lib.optionalAttrs cfg.debugServer { DBIC_TRACE = "1"; });
localDB = "dbi:Pg:dbname=hydra;user=hydra;"; localDB = "dbi:Pg:dbname=hydra;user=hydra;";
@ -51,8 +47,8 @@ let
hydra-package = hydra-package =
let let
makeWrapperArgs = concatStringsSep " " ( makeWrapperArgs = lib.concatStringsSep " " (
mapAttrsToList (key: value: "--set-default \"${key}\" \"${value}\"") hydraEnv lib.mapAttrsToList (key: value: "--set-default \"${key}\" \"${value}\"") hydraEnv
); );
in in
pkgs.buildEnv rec { pkgs.buildEnv rec {
@ -66,7 +62,7 @@ let
fi fi
mkdir -p "$out/bin" mkdir -p "$out/bin"
for path in ${concatStringsSep " " paths}; do for path in ${lib.concatStringsSep " " paths}; do
if [ -d "$path/bin" ]; then if [ -d "$path/bin" ]; then
cd "$path/bin" cd "$path/bin"
for prg in *; do for prg in *; do
@ -85,23 +81,23 @@ let
in in
{ {
disabledModules = [ "services/continuous-integration/hydra/default.nix" ]; disabledModules = [ "services/continuous-integration/hydra/default.nix" ]; # DIFF
###### interface ###### interface
options = { options = {
services.hydra = { services.hydra = {
enable = mkOption { enable = lib.mkOption {
type = types.bool; type = lib.types.bool;
default = false; default = false;
description = '' description = ''
Whether to run Hydra services. Whether to run Hydra services.
''; '';
}; };
dbi = mkOption { dbi = lib.mkOption {
type = types.str; type = lib.types.str;
default = localDB; default = localDB;
example = "dbi:Pg:dbname=hydra;host=postgres.example.org;user=foo;"; example = "dbi:Pg:dbname=hydra;host=postgres.example.org;user=foo;";
description = '' description = ''
@ -114,33 +110,35 @@ in
''; '';
}; };
package = mkPackageOption pkgs "hydra_unstable" { }; # BEGIN DIFF
package = lib.mkPackageOption pkgs "hydra_unstable" { };
baseDir = mkOption { baseDir = lib.mkOption {
type = types.path; type = lib.types.path;
default = "/var/lib/hydra"; default = "/var/lib/hydra";
description = '' description = ''
Hydra's data directory Hydra's data directory
''; '';
}; };
environmentFile = mkOption { environmentFile = lib.mkOption {
type = types.nullOr types.path; type = lib.types.nullOr lib.types.path;
default = null; default = null;
description = '' description = ''
An environment file to pass to hydra services An environment file to pass to hydra services
''; '';
}; };
# END DIFF
hydraURL = mkOption { hydraURL = lib.mkOption {
type = types.str; type = lib.types.str;
description = '' description = ''
The base URL for the Hydra webserver instance. Used for links in emails. The base URL for the Hydra webserver instance. Used for links in emails.
''; '';
}; };
listenHost = mkOption { listenHost = lib.mkOption {
type = types.str; type = lib.types.str;
default = "*"; default = "*";
example = "localhost"; example = "localhost";
description = '' description = ''
@ -149,39 +147,39 @@ in
''; '';
}; };
port = mkOption { port = lib.mkOption {
type = types.port; type = lib.types.port;
default = 3000; default = 3000;
description = '' description = ''
TCP port the web server should listen to. TCP port the web server should listen to.
''; '';
}; };
minimumDiskFree = mkOption { minimumDiskFree = lib.mkOption {
type = types.int; type = lib.types.int;
default = 0; default = 0;
description = '' description = ''
Threshold of minimum disk space (GiB) to determine if the queue runner should run or not. Threshold of minimum disk space (GiB) to determine if the queue runner should run or not.
''; '';
}; };
minimumDiskFreeEvaluator = mkOption { minimumDiskFreeEvaluator = lib.mkOption {
type = types.int; type = lib.types.int;
default = 0; default = 0;
description = '' description = ''
Threshold of minimum disk space (GiB) to determine if the evaluator should run or not. Threshold of minimum disk space (GiB) to determine if the evaluator should run or not.
''; '';
}; };
notificationSender = mkOption { notificationSender = lib.mkOption {
type = types.str; type = lib.types.str;
description = '' description = ''
Sender email address used for email notifications. Sender email address used for email notifications.
''; '';
}; };
smtpHost = mkOption { smtpHost = lib.mkOption {
type = types.nullOr types.str; type = lib.types.nullOr lib.types.str;
default = null; default = null;
example = "localhost"; example = "localhost";
description = '' description = ''
@ -189,67 +187,67 @@ in
''; '';
}; };
tracker = mkOption { tracker = lib.mkOption {
type = types.str; type = lib.types.str;
default = ""; default = "";
description = '' description = ''
Piece of HTML that is included on all pages. Piece of HTML that is included on all pages.
''; '';
}; };
logo = mkOption { logo = lib.mkOption {
type = types.nullOr types.path; type = lib.types.nullOr lib.types.path;
default = null; default = null;
description = '' description = ''
Path to a file containing the logo of your Hydra instance. Path to a file containing the logo of your Hydra instance.
''; '';
}; };
debugServer = mkOption { debugServer = lib.mkOption {
type = types.bool; type = lib.types.bool;
default = false; default = false;
description = "Whether to run the server in debug mode."; description = "Whether to run the server in debug mode.";
}; };
maxServers = mkOption { maxServers = lib.mkOption {
type = types.int; type = lib.types.int;
default = 25; default = 25;
description = "Maximum number of starman workers to spawn."; description = "Maximum number of starman workers to spawn.";
}; };
minSpareServers = mkOption { minSpareServers = lib.mkOption {
type = types.int; type = lib.types.int;
default = 4; default = 4;
description = "Minimum number of spare starman workers to keep."; description = "Minimum number of spare starman workers to keep.";
}; };
maxSpareServers = mkOption { maxSpareServers = lib.mkOption {
type = types.int; type = lib.types.int;
default = 5; default = 5;
description = "Maximum number of spare starman workers to keep."; description = "Maximum number of spare starman workers to keep.";
}; };
extraConfig = mkOption { extraConfig = lib.mkOption {
type = types.lines; type = lib.types.lines;
description = "Extra lines for the Hydra configuration."; description = "Extra lines for the Hydra configuration.";
}; };
extraEnv = mkOption { extraEnv = lib.mkOption {
type = types.attrsOf types.str; type = lib.types.attrsOf lib.types.str;
default = { }; default = { };
description = "Extra environment variables for Hydra."; description = "Extra environment variables for Hydra.";
}; };
gcRootsDir = mkOption { gcRootsDir = lib.mkOption {
type = types.path; type = lib.types.path;
default = "/nix/var/nix/gcroots/hydra"; default = "/nix/var/nix/gcroots/hydra";
description = "Directory that holds Hydra garbage collector roots."; description = "Directory that holds Hydra garbage collector roots.";
}; };
buildMachinesFiles = mkOption { buildMachinesFiles = lib.mkOption {
type = types.listOf types.path; type = lib.types.listOf lib.types.path;
default = optional (config.nix.buildMachines != [ ]) "/etc/nix/machines"; default = lib.optional (config.nix.buildMachines != [ ]) "/etc/nix/machines";
defaultText = literalExpression ''optional (config.nix.buildMachines != []) "/etc/nix/machines"''; defaultText = lib.literalExpression ''lib.optional (config.nix.buildMachines != []) "/etc/nix/machines"'';
example = [ example = [
"/etc/nix/machines" "/etc/nix/machines"
"/var/lib/hydra/provisioner/machines" "/var/lib/hydra/provisioner/machines"
@ -257,8 +255,8 @@ in
description = "List of files containing build machines."; description = "List of files containing build machines.";
}; };
useSubstitutes = mkOption { useSubstitutes = lib.mkOption {
type = types.bool; type = lib.types.bool;
default = false; default = false;
description = '' description = ''
Whether to use binary caches for downloading store paths. Note that Whether to use binary caches for downloading store paths. Note that
@ -276,7 +274,7 @@ in
###### implementation ###### implementation
config = mkIf cfg.enable { config = lib.mkIf cfg.enable {
assertions = [ assertions = [
{ {
assertion = cfg.maxServers != 0 && cfg.maxSpareServers != 0 && cfg.minSpareServers != 0; assertion = cfg.maxServers != 0 && cfg.maxSpareServers != 0 && cfg.minSpareServers != 0;
@ -284,7 +282,7 @@ in
} }
{ {
assertion = cfg.minSpareServers < cfg.maxSpareServers; assertion = cfg.minSpareServers < cfg.maxSpareServers;
message = "services.hydra.minSpareServers cannot be bigger than servives.hydra.maxSpareServers"; message = "services.hydra.minSpareServers cannot be bigger than services.hydra.maxSpareServers";
} }
]; ];
@ -321,42 +319,48 @@ in
base_uri = ${cfg.hydraURL} base_uri = ${cfg.hydraURL}
notification_sender = ${cfg.notificationSender} notification_sender = ${cfg.notificationSender}
max_servers = ${toString cfg.maxServers} max_servers = ${toString cfg.maxServers}
${optionalString (cfg.logo != null) '' ${lib.optionalString (cfg.logo != null) ''
hydra_logo = ${cfg.logo} hydra_logo = ${cfg.logo}
''} ''}
gc_roots_dir = ${cfg.gcRootsDir} gc_roots_dir = ${cfg.gcRootsDir}
use-substitutes = ${if cfg.useSubstitutes then "1" else "0"} use-substitutes = ${if cfg.useSubstitutes then "1" else "0"}
''; '';
# BEGIN DIFF
environment.systemPackages = [ environment.systemPackages = [
hydra-package hydra-package
pkgs.git pkgs.git
]; ];
# END DIFF
environment.variables = hydraEnv; environment.variables = hydraEnv;
nix.settings = mkMerge [ nix.settings = lib.mkMerge [
{ {
keep-outputs = true; keep-outputs = true;
keep-derivations = true; keep-derivations = true;
extra-trusted-users = [ trusted-users = [ "hydra-queue-runner" ];
"hydra"
"hydra-queue-runner"
"hydra-www"
];
} }
(mkIf (versionOlder (getVersion config.nix.package.out) "2.4pre") { (lib.mkIf (lib.versionOlder (lib.getVersion config.nix.package.out) "2.4pre") {
# The default (`true') slows Nix down a lot since the build farm # The default (`true') slows Nix down a lot since the build farm
# has so many GC roots. # has so many GC roots.
gc-check-reachability = false; gc-check-reachability = false;
}) })
]; ];
systemd.slices.system-hydra = {
description = "Hydra CI Server Slice";
documentation = [
"file://${cfg.package}/share/doc/hydra/index.html"
"https://nixos.org/hydra/manual/"
];
};
systemd.services.hydra-init = { systemd.services.hydra-init = {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
requires = optional haveLocalDB "postgresql.service"; requires = lib.optional haveLocalDB "postgresql.service";
after = optional haveLocalDB "postgresql.service"; after = lib.optional haveLocalDB "postgresql.service";
environment = env // { environment = env // {
HYDRA_DBI = "${env.HYDRA_DBI};application_name=hydra-init"; HYDRA_DBI = "${env.HYDRA_DBI};application_name=hydra-init";
}; };
@ -368,18 +372,18 @@ in
ln -sf ${hydraConf} ${baseDir}/hydra.conf ln -sf ${hydraConf} ${baseDir}/hydra.conf
mkdir -m 0700 -p ${baseDir}/www mkdir -m 0700 ${baseDir}/www || true
chown hydra-www:hydra ${baseDir}/www chown hydra-www:hydra ${baseDir}/www
mkdir -m 0700 -p ${baseDir}/queue-runner mkdir -m 0700 ${baseDir}/queue-runner || true
mkdir -m 0750 -p ${baseDir}/build-logs mkdir -m 0750 ${baseDir}/build-logs || true
mkdir -m 0750 -p ${baseDir}/runcommand-logs mkdir -m 0750 ${baseDir}/runcommand-logs || true
chown hydra-queue-runner.hydra \ chown hydra-queue-runner:hydra \
${baseDir}/queue-runner \ ${baseDir}/queue-runner \
${baseDir}/build-logs \ ${baseDir}/build-logs \
${baseDir}/runcommand-logs ${baseDir}/runcommand-logs
${optionalString haveLocalDB '' ${lib.optionalString haveLocalDB ''
if ! [ -e ${baseDir}/.db-created ]; then if ! [ -e ${baseDir}/.db-created ]; then
runuser -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createuser hydra runuser -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createuser hydra
runuser -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createdb -- -O hydra hydra runuser -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createdb -- -O hydra hydra
@ -400,22 +404,21 @@ in
# Move legacy hydra-www roots. # Move legacy hydra-www roots.
if [ -e /nix/var/nix/gcroots/per-user/hydra-www/hydra-roots ]; then if [ -e /nix/var/nix/gcroots/per-user/hydra-www/hydra-roots ]; then
find /nix/var/nix/gcroots/per-user/hydra-www/hydra-roots/ -type f \ find /nix/var/nix/gcroots/per-user/hydra-www/hydra-roots/ -type f -print0 \
| xargs -r mv -f -t ${cfg.gcRootsDir}/ | xargs -0 -r mv -f -t ${cfg.gcRootsDir}/
rmdir /nix/var/nix/gcroots/per-user/hydra-www/hydra-roots rmdir /nix/var/nix/gcroots/per-user/hydra-www/hydra-roots
fi fi
chown hydra:hydra ${cfg.gcRootsDir} chown hydra:hydra ${cfg.gcRootsDir}
chmod 2775 ${cfg.gcRootsDir} chmod 2775 ${cfg.gcRootsDir}
''; '';
serviceConfig = { serviceConfig.ExecStart = "${hydra-package}/bin/hydra-init";
ExecStart = "${hydra-package}/bin/hydra-init"; serviceConfig.PermissionsStartOnly = true;
PermissionsStartOnly = true; serviceConfig.User = "hydra";
User = "hydra"; serviceConfig.Type = "oneshot";
Type = "oneshot"; serviceConfig.RemainAfterExit = true;
RemainAfterExit = true; serviceConfig.Slice = "system-hydra.slice";
EnvironmentFile = optional (cfg.environmentFile != null) cfg.environmentFile; serviceConfig.EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile; # DIFF
};
}; };
systemd.services.hydra-server = { systemd.services.hydra-server = {
@ -430,18 +433,19 @@ in
ExecStart = ExecStart =
"@${hydra-package}/bin/hydra-server hydra-server -f -h '${cfg.listenHost}' " "@${hydra-package}/bin/hydra-server hydra-server -f -h '${cfg.listenHost}' "
+ "-p ${toString cfg.port} --min_spare_servers ${toString cfg.minSpareServers} --max_spare_servers ${toString cfg.maxSpareServers} " + "-p ${toString cfg.port} --min_spare_servers ${toString cfg.minSpareServers} --max_spare_servers ${toString cfg.maxSpareServers} "
+ "--max_servers ${toString cfg.maxServers} --max_requests 100 ${optionalString cfg.debugServer "-d"}"; + "--max_servers ${toString cfg.maxServers} --max_requests 100 ${lib.optionalString cfg.debugServer "-d"}";
User = "hydra-www"; User = "hydra-www";
PermissionsStartOnly = true; PermissionsStartOnly = true;
Restart = "always"; Restart = "always";
EnvironmentFile = optional (cfg.environmentFile != null) cfg.environmentFile; Slice = "system-hydra.slice";
EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile; # DIFF
}; };
}; };
systemd.services.hydra-queue-runner = { systemd.services.hydra-queue-runner = {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
requires = [ "hydra-init.service" ]; requires = [ "hydra-init.service" ];
wants = [ "network-online.target" ]; wants = [ "network-online.target" ]; # DIFF
after = [ after = [
"hydra-init.service" "hydra-init.service"
"network.target" "network.target"
@ -464,11 +468,12 @@ in
ExecStopPost = "${hydra-package}/bin/hydra-queue-runner --unlock"; ExecStopPost = "${hydra-package}/bin/hydra-queue-runner --unlock";
User = "hydra-queue-runner"; User = "hydra-queue-runner";
Restart = "always"; Restart = "always";
Slice = "system-hydra.slice";
# Ensure we can get core dumps. # Ensure we can get core dumps.
LimitCORE = "infinity"; LimitCORE = "infinity";
WorkingDirectory = "${baseDir}/queue-runner"; WorkingDirectory = "${baseDir}/queue-runner";
EnvironmentFile = optional (cfg.environmentFile != null) cfg.environmentFile; EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile; # DIFF
}; };
}; };
@ -495,7 +500,8 @@ in
User = "hydra"; User = "hydra";
Restart = "always"; Restart = "always";
WorkingDirectory = baseDir; WorkingDirectory = baseDir;
EnvironmentFile = optional (cfg.environmentFile != null) cfg.environmentFile; Slice = "system-hydra.slice";
EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile; # DIFF
}; };
}; };
@ -508,6 +514,7 @@ in
serviceConfig = { serviceConfig = {
ExecStart = "@${hydra-package}/bin/hydra-update-gc-roots hydra-update-gc-roots"; ExecStart = "@${hydra-package}/bin/hydra-update-gc-roots hydra-update-gc-roots";
User = "hydra"; User = "hydra";
Slice = "system-hydra.slice";
}; };
startAt = "2,14:15"; startAt = "2,14:15";
}; };
@ -521,7 +528,8 @@ in
serviceConfig = { serviceConfig = {
ExecStart = "@${hydra-package}/bin/hydra-send-stats hydra-send-stats"; ExecStart = "@${hydra-package}/bin/hydra-send-stats hydra-send-stats";
User = "hydra"; User = "hydra";
EnvironmentFile = optional (cfg.environmentFile != null) cfg.environmentFile; Slice = "system-hydra.slice";
EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile; # DIFF
}; };
}; };
@ -530,6 +538,7 @@ in
requires = [ "hydra-init.service" ]; requires = [ "hydra-init.service" ];
after = [ "hydra-init.service" ]; after = [ "hydra-init.service" ];
restartTriggers = [ hydraConf ]; restartTriggers = [ hydraConf ];
path = [ pkgs.zstd ];
environment = env // { environment = env // {
PGPASSFILE = "${baseDir}/pgpass-queue-runner"; PGPASSFILE = "${baseDir}/pgpass-queue-runner";
HYDRA_DBI = "${env.HYDRA_DBI};application_name=hydra-notify"; HYDRA_DBI = "${env.HYDRA_DBI};application_name=hydra-notify";
@ -540,7 +549,8 @@ in
User = "hydra-queue-runner"; User = "hydra-queue-runner";
Restart = "always"; Restart = "always";
RestartSec = 5; RestartSec = 5;
EnvironmentFile = optional (cfg.environmentFile != null) cfg.environmentFile; Slice = "system-hydra.slice";
EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile; # DIFF
}; };
}; };
@ -558,22 +568,34 @@ in
fi fi
''; '';
startAt = "*:0/5"; startAt = "*:0/5";
serviceConfig.Slice = "system-hydra.slice";
}; };
# Periodically compress build logs. The queue runner compresses # Periodically compress build logs. The queue runner compresses
# logs automatically after a step finishes, but this doesn't work # logs automatically after a step finishes, but this doesn't work
# if the queue runner is stopped prematurely. # if the queue runner is stopped prematurely.
systemd.services.hydra-compress-logs = { systemd.services.hydra-compress-logs = {
path = [ pkgs.bzip2 ]; path = [
pkgs.bzip2
pkgs.zstd
];
script = '' script = ''
find /var/lib/hydra/build-logs -type f -name "*.drv" -mtime +3 -size +0c | xargs -r bzip2 -v -f set -eou pipefail
compression=$(sed -nr 's/compress_build_logs_compression = ()/\1/p' ${baseDir}/hydra.conf)
if [[ $compression == "" || $compression == bzip2 ]]; then
compressionCmd=(bzip2)
elif [[ $compression == zstd ]]; then
compressionCmd=(zstd --rm)
fi
find ${baseDir}/build-logs -ignore_readdir_race -type f -name "*.drv" -mtime +3 -size +0c -print0 | xargs -0 -r "''${compressionCmd[@]}" --force --quiet
''; '';
startAt = "Sun 01:45"; startAt = "Sun 01:45";
serviceConfig.Slice = "system-hydra.slice";
}; };
services.postgresql.enable = mkIf haveLocalDB true; services.postgresql.enable = lib.mkIf haveLocalDB true;
services.postgresql.identMap = optionalString haveLocalDB '' services.postgresql.identMap = lib.optionalString haveLocalDB ''
hydra-users hydra hydra hydra-users hydra hydra
hydra-users hydra-queue-runner hydra hydra-users hydra-queue-runner hydra
hydra-users hydra-www hydra hydra-users hydra-www hydra
@ -582,7 +604,7 @@ in
hydra-users postgres postgres hydra-users postgres postgres
''; '';
services.postgresql.authentication = optionalString haveLocalDB '' services.postgresql.authentication = lib.optionalString haveLocalDB ''
local hydra all ident map=hydra-users local hydra all ident map=hydra-users
''; '';