1/* 2 3 This file is for options that NixOS and nix-darwin have in common. 4 5 Platform-specific code is in the respective default.nix files. 6 7*/ 8 9{ config, lib, options, pkgs, ... }: 10let 11 inherit (lib) 12 filterAttrs 13 literalDocBook 14 literalExpression 15 mkIf 16 mkOption 17 mkRemovedOptionModule 18 mkRenamedOptionModule 19 types 20 ; 21 22 cfg = 23 config.services.hercules-ci-agent; 24 25 format = pkgs.formats.toml { }; 26 27 settingsModule = { config, ... }: { 28 freeformType = format.type; 29 options = { 30 apiBaseUrl = mkOption { 31 description = '' 32 API base URL that the agent will connect to. 33 34 When using Hercules CI Enterprise, set this to the URL where your 35 Hercules CI server is reachable. 36 ''; 37 type = types.str; 38 default = "https://hercules-ci.com"; 39 }; 40 baseDirectory = mkOption { 41 type = types.path; 42 default = "/var/lib/hercules-ci-agent"; 43 description = '' 44 State directory (secrets, work directory, etc) for agent 45 ''; 46 }; 47 concurrentTasks = mkOption { 48 description = '' 49 Number of tasks to perform simultaneously. 50 51 A task is a single derivation build, an evaluation or an effect run. 52 At minimum, you need 2 concurrent tasks for <literal>x86_64-linux</literal> 53 in your cluster, to allow for import from derivation. 54 55 <literal>concurrentTasks</literal> can be around the CPU core count or lower if memory is 56 the bottleneck. 57 58 The optimal value depends on the resource consumption characteristics of your workload, 59 including memory usage and in-task parallelism. This is typically determined empirically. 60 61 When scaling, it is generally better to have a double-size machine than two machines, 62 because each split of resources causes inefficiencies; particularly with regards 63 to build latency because of extra downloads. 64 ''; 65 type = types.either types.ints.positive (types.enum [ "auto" ]); 66 default = "auto"; 67 }; 68 labels = mkOption { 69 description = '' 70 A key-value map of user data. 71 72 This data will be available to organization members in the dashboard and API. 73 74 The values can be of any TOML type that corresponds to a JSON type, but arrays 75 can not contain tables/objects due to limitations of the TOML library. Values 76 involving arrays of non-primitive types may not be representable currently. 77 ''; 78 type = format.type; 79 defaultText = literalExpression '' 80 { 81 agent.source = "..."; # One of "nixpkgs", "flake", "override" 82 lib.version = "..."; 83 pkgs.version = "..."; 84 } 85 ''; 86 }; 87 workDirectory = mkOption { 88 description = '' 89 The directory in which temporary subdirectories are created for task state. This includes sources for Nix evaluation. 90 ''; 91 type = types.path; 92 default = config.baseDirectory + "/work"; 93 defaultText = literalExpression ''baseDirectory + "/work"''; 94 }; 95 staticSecretsDirectory = mkOption { 96 description = '' 97 This is the default directory to look for statically configured secrets like <literal>cluster-join-token.key</literal>. 98 99 See also <literal>clusterJoinTokenPath</literal> and <literal>binaryCachesPath</literal> for fine-grained configuration. 100 ''; 101 type = types.path; 102 default = config.baseDirectory + "/secrets"; 103 defaultText = literalExpression ''baseDirectory + "/secrets"''; 104 }; 105 clusterJoinTokenPath = mkOption { 106 description = '' 107 Location of the cluster-join-token.key file. 108 109 You can retrieve the contents of the file when creating a new agent via 110 <link xlink:href="https://hercules-ci.com/dashboard">https://hercules-ci.com/dashboard</link>. 111 112 As this value is confidential, it should not be in the store, but 113 installed using other means, such as agenix, NixOps 114 <literal>deployment.keys</literal>, or manual installation. 115 116 The contents of the file are used for authentication between the agent and the API. 117 ''; 118 type = types.path; 119 default = config.staticSecretsDirectory + "/cluster-join-token.key"; 120 defaultText = literalExpression ''staticSecretsDirectory + "/cluster-join-token.key"''; 121 }; 122 binaryCachesPath = mkOption { 123 description = '' 124 Path to a JSON file containing binary cache secret keys. 125 126 As these values are confidential, they should not be in the store, but 127 copied over using other means, such as agenix, NixOps 128 <literal>deployment.keys</literal>, or manual installation. 129 130 The format is described on <link xlink:href="https://docs.hercules-ci.com/hercules-ci-agent/binary-caches-json/">https://docs.hercules-ci.com/hercules-ci-agent/binary-caches-json/</link>. 131 ''; 132 type = types.path; 133 default = config.staticSecretsDirectory + "/binary-caches.json"; 134 defaultText = literalExpression ''staticSecretsDirectory + "/binary-caches.json"''; 135 }; 136 secretsJsonPath = mkOption { 137 description = '' 138 Path to a JSON file containing secrets for effects. 139 140 As these values are confidential, they should not be in the store, but 141 copied over using other means, such as agenix, NixOps 142 <literal>deployment.keys</literal>, or manual installation. 143 144 The format is described on <link xlink:href="https://docs.hercules-ci.com/hercules-ci-agent/secrets-json/">https://docs.hercules-ci.com/hercules-ci-agent/secrets-json/</link>. 145 146 ''; 147 type = types.path; 148 default = config.staticSecretsDirectory + "/secrets.json"; 149 defaultText = literalExpression ''staticSecretsDirectory + "/secrets.json"''; 150 }; 151 }; 152 }; 153 154 # TODO (roberth, >=2022) remove 155 checkNix = 156 if !cfg.checkNix 157 then "" 158 else if lib.versionAtLeast config.nix.package.version "2.3.10" 159 then "" 160 else 161 pkgs.stdenv.mkDerivation { 162 name = "hercules-ci-check-system-nix-src"; 163 inherit (config.nix.package) src patches; 164 dontConfigure = true; 165 buildPhase = '' 166 echo "Checking in-memory pathInfoCache expiry" 167 if ! grep 'PathInfoCacheValue' src/libstore/store-api.hh >/dev/null; then 168 cat 1>&2 <<EOF 169 170 You are deploying Hercules CI Agent on a system with an incompatible 171 nix-daemon. Please make sure nix.package is set to a Nix version of at 172 least 2.3.10 or a master version more recent than Mar 12, 2020. 173 EOF 174 exit 1 175 fi 176 ''; 177 installPhase = "touch $out"; 178 }; 179 180in 181{ 182 imports = [ 183 (mkRenamedOptionModule [ "services" "hercules-ci-agent" "extraOptions" ] [ "services" "hercules-ci-agent" "settings" ]) 184 (mkRenamedOptionModule [ "services" "hercules-ci-agent" "baseDirectory" ] [ "services" "hercules-ci-agent" "settings" "baseDirectory" ]) 185 (mkRenamedOptionModule [ "services" "hercules-ci-agent" "concurrentTasks" ] [ "services" "hercules-ci-agent" "settings" "concurrentTasks" ]) 186 (mkRemovedOptionModule [ "services" "hercules-ci-agent" "patchNix" ] "Nix versions packaged in this version of Nixpkgs don't need a patched nix-daemon to work correctly in Hercules CI Agent clusters.") 187 ]; 188 189 options.services.hercules-ci-agent = { 190 enable = mkOption { 191 type = types.bool; 192 default = false; 193 description = '' 194 Enable to run Hercules CI Agent as a system service. 195 196 <link xlink:href="https://hercules-ci.com">Hercules CI</link> is a 197 continuous integation service that is centered around Nix. 198 199 Support is available at <link xlink:href="mailto:help@hercules-ci.com">help@hercules-ci.com</link>. 200 ''; 201 }; 202 checkNix = mkOption { 203 type = types.bool; 204 default = true; 205 description = '' 206 Whether to make sure that the system's Nix (nix-daemon) is compatible. 207 208 If you set this to false, please keep up with the change log. 209 ''; 210 }; 211 package = mkOption { 212 description = '' 213 Package containing the bin/hercules-ci-agent executable. 214 ''; 215 type = types.package; 216 default = pkgs.hercules-ci-agent; 217 defaultText = literalExpression "pkgs.hercules-ci-agent"; 218 }; 219 settings = mkOption { 220 description = '' 221 These settings are written to the <literal>agent.toml</literal> file. 222 223 Not all settings are listed as options, can be set nonetheless. 224 225 For the exhaustive list of settings, see <link xlink:href="https://docs.hercules-ci.com/hercules-ci/reference/agent-config/"/>. 226 ''; 227 type = types.submoduleWith { modules = [ settingsModule ]; }; 228 }; 229 230 /* 231 Internal and/or computed values. 232 233 These are written as options instead of let binding to allow sharing with 234 default.nix on both NixOS and nix-darwin. 235 */ 236 tomlFile = mkOption { 237 type = types.path; 238 internal = true; 239 defaultText = literalDocBook "generated <literal>hercules-ci-agent.toml</literal>"; 240 description = '' 241 The fully assembled config file. 242 ''; 243 }; 244 }; 245 246 config = mkIf cfg.enable { 247 nix.extraOptions = lib.addContextFrom checkNix '' 248 # A store path that was missing at first may well have finished building, 249 # even shortly after the previous lookup. This *also* applies to the daemon. 250 narinfo-cache-negative-ttl = 0 251 ''; 252 services.hercules-ci-agent = { 253 tomlFile = 254 format.generate "hercules-ci-agent.toml" cfg.settings; 255 256 settings.labels = { 257 agent.source = 258 if options.services.hercules-ci-agent.package.highestPrio == (lib.modules.mkOptionDefault { }).priority 259 then "nixpkgs" 260 else lib.mkOptionDefault "override"; 261 pkgs.version = pkgs.lib.version; 262 lib.version = lib.version; 263 }; 264 }; 265 }; 266}