···1010let
1111 inherit (lib)
1212 filterAttrs
1313- literalMD
1413 literalExpression
1514 mkIf
1615 mkOption
1716 mkRemovedOptionModule
1817 mkRenamedOptionModule
1918 types
2020- ;
2121-2222- cfg =
2323- config.services.hercules-ci-agent;
2424-2525- format = pkgs.formats.toml { };
26192727- settingsModule = { config, ... }: {
2828- freeformType = format.type;
2929- options = {
3030- apiBaseUrl = mkOption {
3131- description = lib.mdDoc ''
3232- API base URL that the agent will connect to.
3333-3434- When using Hercules CI Enterprise, set this to the URL where your
3535- Hercules CI server is reachable.
3636- '';
3737- type = types.str;
3838- default = "https://hercules-ci.com";
3939- };
4040- baseDirectory = mkOption {
4141- type = types.path;
4242- default = "/var/lib/hercules-ci-agent";
4343- description = lib.mdDoc ''
4444- State directory (secrets, work directory, etc) for agent
4545- '';
4646- };
4747- concurrentTasks = mkOption {
4848- description = lib.mdDoc ''
4949- Number of tasks to perform simultaneously.
5050-5151- A task is a single derivation build, an evaluation or an effect run.
5252- At minimum, you need 2 concurrent tasks for `x86_64-linux`
5353- in your cluster, to allow for import from derivation.
5454-5555- `concurrentTasks` can be around the CPU core count or lower if memory is
5656- the bottleneck.
5757-5858- The optimal value depends on the resource consumption characteristics of your workload,
5959- including memory usage and in-task parallelism. This is typically determined empirically.
6060-6161- When scaling, it is generally better to have a double-size machine than two machines,
6262- because each split of resources causes inefficiencies; particularly with regards
6363- to build latency because of extra downloads.
6464- '';
6565- type = types.either types.ints.positive (types.enum [ "auto" ]);
6666- default = "auto";
6767- };
6868- labels = mkOption {
6969- description = lib.mdDoc ''
7070- A key-value map of user data.
2020+ ;
71217272- This data will be available to organization members in the dashboard and API.
2222+ cfg = config.services.hercules-ci-agent;
73237474- The values can be of any TOML type that corresponds to a JSON type, but arrays
7575- can not contain tables/objects due to limitations of the TOML library. Values
7676- involving arrays of non-primitive types may not be representable currently.
7777- '';
7878- type = format.type;
7979- defaultText = literalExpression ''
8080- {
8181- agent.source = "..."; # One of "nixpkgs", "flake", "override"
8282- lib.version = "...";
8383- pkgs.version = "...";
8484- }
8585- '';
8686- };
8787- workDirectory = mkOption {
8888- description = lib.mdDoc ''
8989- The directory in which temporary subdirectories are created for task state. This includes sources for Nix evaluation.
9090- '';
9191- type = types.path;
9292- default = config.baseDirectory + "/work";
9393- defaultText = literalExpression ''baseDirectory + "/work"'';
9494- };
9595- staticSecretsDirectory = mkOption {
9696- description = lib.mdDoc ''
9797- This is the default directory to look for statically configured secrets like `cluster-join-token.key`.
9898-9999- See also `clusterJoinTokenPath` and `binaryCachesPath` for fine-grained configuration.
100100- '';
101101- type = types.path;
102102- default = config.baseDirectory + "/secrets";
103103- defaultText = literalExpression ''baseDirectory + "/secrets"'';
104104- };
105105- clusterJoinTokenPath = mkOption {
106106- description = lib.mdDoc ''
107107- Location of the cluster-join-token.key file.
108108-109109- You can retrieve the contents of the file when creating a new agent via
110110- <https://hercules-ci.com/dashboard>.
111111-112112- As this value is confidential, it should not be in the store, but
113113- installed using other means, such as agenix, NixOps
114114- `deployment.keys`, or manual installation.
115115-116116- The contents of the file are used for authentication between the agent and the API.
117117- '';
118118- type = types.path;
119119- default = config.staticSecretsDirectory + "/cluster-join-token.key";
120120- defaultText = literalExpression ''staticSecretsDirectory + "/cluster-join-token.key"'';
121121- };
122122- binaryCachesPath = mkOption {
123123- description = lib.mdDoc ''
124124- Path to a JSON file containing binary cache secret keys.
125125-126126- As these values are confidential, they should not be in the store, but
127127- copied over using other means, such as agenix, NixOps
128128- `deployment.keys`, or manual installation.
129129-130130- The format is described on <https://docs.hercules-ci.com/hercules-ci-agent/binary-caches-json/>.
131131- '';
132132- type = types.path;
133133- default = config.staticSecretsDirectory + "/binary-caches.json";
134134- defaultText = literalExpression ''staticSecretsDirectory + "/binary-caches.json"'';
135135- };
136136- secretsJsonPath = mkOption {
137137- description = lib.mdDoc ''
138138- Path to a JSON file containing secrets for effects.
139139-140140- As these values are confidential, they should not be in the store, but
141141- copied over using other means, such as agenix, NixOps
142142- `deployment.keys`, or manual installation.
143143-144144- The format is described on <https://docs.hercules-ci.com/hercules-ci-agent/secrets-json/>.
145145- '';
146146- type = types.path;
147147- default = config.staticSecretsDirectory + "/secrets.json";
148148- defaultText = literalExpression ''staticSecretsDirectory + "/secrets.json"'';
149149- };
150150- };
151151- };
152152-153153- # TODO (roberth, >=2022) remove
154154- checkNix =
155155- if !cfg.checkNix
156156- then ""
157157- else if lib.versionAtLeast config.nix.package.version "2.3.10"
158158- then ""
159159- else
160160- pkgs.stdenv.mkDerivation {
161161- name = "hercules-ci-check-system-nix-src";
162162- inherit (config.nix.package) src patches;
163163- dontConfigure = true;
164164- buildPhase = ''
165165- echo "Checking in-memory pathInfoCache expiry"
166166- if ! grep 'PathInfoCacheValue' src/libstore/store-api.hh >/dev/null; then
167167- cat 1>&2 <<EOF
168168-169169- You are deploying Hercules CI Agent on a system with an incompatible
170170- nix-daemon. Please make sure nix.package is set to a Nix version of at
171171- least 2.3.10 or a master version more recent than Mar 12, 2020.
172172- EOF
173173- exit 1
174174- fi
175175- '';
176176- installPhase = "touch $out";
177177- };
2424+ inherit (import ./settings.nix { inherit pkgs lib; }) format settingsModule;
1782517926in
18027{
···19845 Support is available at [help@hercules-ci.com](mailto:help@hercules-ci.com).
19946 '';
20047 };
201201- checkNix = mkOption {
202202- type = types.bool;
203203- default = true;
204204- description = lib.mdDoc ''
205205- Whether to make sure that the system's Nix (nix-daemon) is compatible.
206206-207207- If you set this to false, please keep up with the change log.
208208- '';
209209- };
21048 package = mkOption {
21149 description = lib.mdDoc ''
21250 Package containing the bin/hercules-ci-agent executable.
···23573 tomlFile = mkOption {
23674 type = types.path;
23775 internal = true;
238238- defaultText = literalMD "generated `hercules-ci-agent.toml`";
7676+ defaultText = lib.literalMD "generated `hercules-ci-agent.toml`";
23977 description = lib.mdDoc ''
24078 The fully assembled config file.
24179 '';
···24381 };
2448224583 config = mkIf cfg.enable {
246246- nix.extraOptions = lib.addContextFrom checkNix ''
8484+ # Make sure that nix.extraOptions does not override trusted-users
8585+ assertions = [
8686+ {
8787+ assertion =
8888+ (cfg.settings.nixUserIsTrusted or false) ->
8989+ builtins.match ".*(^|\n)[ \t]*trusted-users[ \t]*=.*" config.nix.extraOptions == null;
9090+ message = ''
9191+ hercules-ci-agent: Please do not set `trusted-users` in `nix.extraOptions`.
9292+9393+ The hercules-ci-agent module by default relies on `nix.settings.trusted-users`
9494+ to be effectful, but a line like `trusted-users = ...` in `nix.extraOptions`
9595+ will override the value set in `nix.settings.trusted-users`.
9696+9797+ Instead of setting `trusted-users` in the `nix.extraOptions` string, you should
9898+ set an option with additive semantics, such as
9999+ - the NixOS option `nix.settings.trusted-users`, or
100100+ - the Nix option in the `extraOptions` string, `extra-trusted-users`
101101+ '';
102102+ }
103103+ ];
104104+ nix.extraOptions = ''
247105 # A store path that was missing at first may well have finished building,
248106 # even shortly after the previous lookup. This *also* applies to the daemon.
249107 narinfo-cache-negative-ttl = 0
···251109 services.hercules-ci-agent = {
252110 tomlFile =
253111 format.generate "hercules-ci-agent.toml" cfg.settings;
254254-255255- settings.labels = {
256256- agent.source =
257257- if options.services.hercules-ci-agent.package.highestPrio == (lib.modules.mkOptionDefault { }).priority
258258- then "nixpkgs"
259259- else lib.mkOptionDefault "override";
260260- pkgs.version = pkgs.lib.version;
261261- lib.version = lib.version;
112112+ settings.config._module.args = {
113113+ packageOption = options.services.hercules-ci-agent.package;
114114+ inherit pkgs;
262115 };
263116 };
264117 };
···3636 Restart = "on-failure";
3737 RestartSec = 120;
38383939- LimitSTACK = 256 * 1024 * 1024;
3939+ # If a worker goes OOM, don't kill the main process. It needs to
4040+ # report the failure and it's unlikely to be part of the problem.
4041 OOMPolicy = "continue";
4242+4343+ # Work around excessive stack use by libstdc++ regex
4444+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86164
4545+ # A 256 MiB stack allows between 400 KiB and 1.5 MiB file to be matched by ".*".
4646+ LimitSTACK = 256 * 1024 * 1024;
4147 };
4248 };
4349
···11+# Not a module
22+{ pkgs, lib }:
33+let
44+ inherit (lib)
55+ types
66+ literalExpression
77+ mkOption
88+ ;
99+1010+ format = pkgs.formats.toml { };
1111+1212+ settingsModule = { config, packageOption, pkgs, ... }: {
1313+ freeformType = format.type;
1414+ options = {
1515+ apiBaseUrl = mkOption {
1616+ description = lib.mdDoc ''
1717+ API base URL that the agent will connect to.
1818+1919+ When using Hercules CI Enterprise, set this to the URL where your
2020+ Hercules CI server is reachable.
2121+ '';
2222+ type = types.str;
2323+ default = "https://hercules-ci.com";
2424+ };
2525+ baseDirectory = mkOption {
2626+ type = types.path;
2727+ default = "/var/lib/hercules-ci-agent";
2828+ description = lib.mdDoc ''
2929+ State directory (secrets, work directory, etc) for agent
3030+ '';
3131+ };
3232+ concurrentTasks = mkOption {
3333+ description = lib.mdDoc ''
3434+ Number of tasks to perform simultaneously.
3535+3636+ A task is a single derivation build, an evaluation or an effect run.
3737+ At minimum, you need 2 concurrent tasks for `x86_64-linux`
3838+ in your cluster, to allow for import from derivation.
3939+4040+ `concurrentTasks` can be around the CPU core count or lower if memory is
4141+ the bottleneck.
4242+4343+ The optimal value depends on the resource consumption characteristics of your workload,
4444+ including memory usage and in-task parallelism. This is typically determined empirically.
4545+4646+ When scaling, it is generally better to have a double-size machine than two machines,
4747+ because each split of resources causes inefficiencies; particularly with regards
4848+ to build latency because of extra downloads.
4949+ '';
5050+ type = types.either types.ints.positive (types.enum [ "auto" ]);
5151+ default = "auto";
5252+ defaultText = lib.literalMD ''
5353+ `"auto"`, meaning equal to the number of CPU cores.
5454+ '';
5555+ };
5656+ labels = mkOption {
5757+ description = lib.mdDoc ''
5858+ A key-value map of user data.
5959+6060+ This data will be available to organization members in the dashboard and API.
6161+6262+ The values can be of any TOML type that corresponds to a JSON type, but arrays
6363+ can not contain tables/objects due to limitations of the TOML library. Values
6464+ involving arrays of non-primitive types may not be representable currently.
6565+ '';
6666+ type = format.type;
6767+ defaultText = literalExpression ''
6868+ {
6969+ agent.source = "..."; # One of "nixpkgs", "flake", "override"
7070+ lib.version = "...";
7171+ pkgs.version = "...";
7272+ }
7373+ '';
7474+ };
7575+ workDirectory = mkOption {
7676+ description = lib.mdDoc ''
7777+ The directory in which temporary subdirectories are created for task state. This includes sources for Nix evaluation.
7878+ '';
7979+ type = types.path;
8080+ default = config.baseDirectory + "/work";
8181+ defaultText = literalExpression ''baseDirectory + "/work"'';
8282+ };
8383+ staticSecretsDirectory = mkOption {
8484+ description = lib.mdDoc ''
8585+ This is the default directory to look for statically configured secrets like `cluster-join-token.key`.
8686+8787+ See also `clusterJoinTokenPath` and `binaryCachesPath` for fine-grained configuration.
8888+ '';
8989+ type = types.path;
9090+ default = config.baseDirectory + "/secrets";
9191+ defaultText = literalExpression ''baseDirectory + "/secrets"'';
9292+ };
9393+ clusterJoinTokenPath = mkOption {
9494+ description = lib.mdDoc ''
9595+ Location of the cluster-join-token.key file.
9696+9797+ You can retrieve the contents of the file when creating a new agent via
9898+ <https://hercules-ci.com/dashboard>.
9999+100100+ As this value is confidential, it should not be in the store, but
101101+ installed using other means, such as agenix, NixOps
102102+ `deployment.keys`, or manual installation.
103103+104104+ The contents of the file are used for authentication between the agent and the API.
105105+ '';
106106+ type = types.path;
107107+ default = config.staticSecretsDirectory + "/cluster-join-token.key";
108108+ defaultText = literalExpression ''staticSecretsDirectory + "/cluster-join-token.key"'';
109109+ };
110110+ binaryCachesPath = mkOption {
111111+ description = lib.mdDoc ''
112112+ Path to a JSON file containing binary cache secret keys.
113113+114114+ As these values are confidential, they should not be in the store, but
115115+ copied over using other means, such as agenix, NixOps
116116+ `deployment.keys`, or manual installation.
117117+118118+ The format is described on <https://docs.hercules-ci.com/hercules-ci-agent/binary-caches-json/>.
119119+ '';
120120+ type = types.path;
121121+ default = config.staticSecretsDirectory + "/binary-caches.json";
122122+ defaultText = literalExpression ''staticSecretsDirectory + "/binary-caches.json"'';
123123+ };
124124+ secretsJsonPath = mkOption {
125125+ description = lib.mdDoc ''
126126+ Path to a JSON file containing secrets for effects.
127127+128128+ As these values are confidential, they should not be in the store, but
129129+ copied over using other means, such as agenix, NixOps
130130+ `deployment.keys`, or manual installation.
131131+132132+ The format is described on <https://docs.hercules-ci.com/hercules-ci-agent/secrets-json/>.
133133+ '';
134134+ type = types.path;
135135+ default = config.staticSecretsDirectory + "/secrets.json";
136136+ defaultText = literalExpression ''staticSecretsDirectory + "/secrets.json"'';
137137+ };
138138+ };
139139+ config = {
140140+ labels = {
141141+ agent.source =
142142+ if packageOption.highestPrio == (lib.modules.mkOptionDefault { }).priority
143143+ then "nixpkgs"
144144+ else lib.mkOptionDefault "override";
145145+ pkgs.version = pkgs.lib.version;
146146+ lib.version = lib.version;
147147+ };
148148+ };
149149+ };
150150+in
151151+{
152152+ inherit format settingsModule;
153153+}