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 literalMD 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 = lib.mdDoc '' 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 = lib.mdDoc '' 44 State directory (secrets, work directory, etc) for agent 45 ''; 46 }; 47 concurrentTasks = mkOption { 48 description = lib.mdDoc '' 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 `x86_64-linux` 53 in your cluster, to allow for import from derivation. 54 55 `concurrentTasks` 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 = lib.mdDoc '' 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 = lib.mdDoc '' 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 = lib.mdDoc '' 97 This is the default directory to look for statically configured secrets like `cluster-join-token.key`. 98 99 See also `clusterJoinTokenPath` and `binaryCachesPath` 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 = lib.mdDoc '' 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 <https://hercules-ci.com/dashboard>. 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 `deployment.keys`, 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 = lib.mdDoc '' 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 `deployment.keys`, or manual installation. 129 130 The format is described on <https://docs.hercules-ci.com/hercules-ci-agent/binary-caches-json/>. 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 = lib.mdDoc '' 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 `deployment.keys`, or manual installation. 143 144 The format is described on <https://docs.hercules-ci.com/hercules-ci-agent/secrets-json/>. 145 ''; 146 type = types.path; 147 default = config.staticSecretsDirectory + "/secrets.json"; 148 defaultText = literalExpression ''staticSecretsDirectory + "/secrets.json"''; 149 }; 150 }; 151 }; 152 153 # TODO (roberth, >=2022) remove 154 checkNix = 155 if !cfg.checkNix 156 then "" 157 else if lib.versionAtLeast config.nix.package.version "2.3.10" 158 then "" 159 else 160 pkgs.stdenv.mkDerivation { 161 name = "hercules-ci-check-system-nix-src"; 162 inherit (config.nix.package) src patches; 163 dontConfigure = true; 164 buildPhase = '' 165 echo "Checking in-memory pathInfoCache expiry" 166 if ! grep 'PathInfoCacheValue' src/libstore/store-api.hh >/dev/null; then 167 cat 1>&2 <<EOF 168 169 You are deploying Hercules CI Agent on a system with an incompatible 170 nix-daemon. Please make sure nix.package is set to a Nix version of at 171 least 2.3.10 or a master version more recent than Mar 12, 2020. 172 EOF 173 exit 1 174 fi 175 ''; 176 installPhase = "touch $out"; 177 }; 178 179in 180{ 181 imports = [ 182 (mkRenamedOptionModule [ "services" "hercules-ci-agent" "extraOptions" ] [ "services" "hercules-ci-agent" "settings" ]) 183 (mkRenamedOptionModule [ "services" "hercules-ci-agent" "baseDirectory" ] [ "services" "hercules-ci-agent" "settings" "baseDirectory" ]) 184 (mkRenamedOptionModule [ "services" "hercules-ci-agent" "concurrentTasks" ] [ "services" "hercules-ci-agent" "settings" "concurrentTasks" ]) 185 (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.") 186 ]; 187 188 options.services.hercules-ci-agent = { 189 enable = mkOption { 190 type = types.bool; 191 default = false; 192 description = lib.mdDoc '' 193 Enable to run Hercules CI Agent as a system service. 194 195 [Hercules CI](https://hercules-ci.com) is a 196 continuous integation service that is centered around Nix. 197 198 Support is available at [help@hercules-ci.com](mailto:help@hercules-ci.com). 199 ''; 200 }; 201 checkNix = mkOption { 202 type = types.bool; 203 default = true; 204 description = lib.mdDoc '' 205 Whether to make sure that the system's Nix (nix-daemon) is compatible. 206 207 If you set this to false, please keep up with the change log. 208 ''; 209 }; 210 package = mkOption { 211 description = lib.mdDoc '' 212 Package containing the bin/hercules-ci-agent executable. 213 ''; 214 type = types.package; 215 default = pkgs.hercules-ci-agent; 216 defaultText = literalExpression "pkgs.hercules-ci-agent"; 217 }; 218 settings = mkOption { 219 description = lib.mdDoc '' 220 These settings are written to the `agent.toml` file. 221 222 Not all settings are listed as options, can be set nonetheless. 223 224 For the exhaustive list of settings, see <https://docs.hercules-ci.com/hercules-ci/reference/agent-config/>. 225 ''; 226 type = types.submoduleWith { modules = [ settingsModule ]; }; 227 }; 228 229 /* 230 Internal and/or computed values. 231 232 These are written as options instead of let binding to allow sharing with 233 default.nix on both NixOS and nix-darwin. 234 */ 235 tomlFile = mkOption { 236 type = types.path; 237 internal = true; 238 defaultText = literalMD "generated `hercules-ci-agent.toml`"; 239 description = lib.mdDoc '' 240 The fully assembled config file. 241 ''; 242 }; 243 }; 244 245 config = mkIf cfg.enable { 246 nix.extraOptions = lib.addContextFrom checkNix '' 247 # A store path that was missing at first may well have finished building, 248 # even shortly after the previous lookup. This *also* applies to the daemon. 249 narinfo-cache-negative-ttl = 0 250 ''; 251 services.hercules-ci-agent = { 252 tomlFile = 253 format.generate "hercules-ci-agent.toml" cfg.settings; 254 255 settings.labels = { 256 agent.source = 257 if options.services.hercules-ci-agent.package.highestPrio == (lib.modules.mkOptionDefault { }).priority 258 then "nixpkgs" 259 else lib.mkOptionDefault "override"; 260 pkgs.version = pkgs.lib.version; 261 lib.version = lib.version; 262 }; 263 }; 264 }; 265}