gogup: 0.27.5 -> 0.27.6 (#373906)
[NixPkgs.git] / nixos / modules / services / continuous-integration / gitlab-runner.nix
blob895ba66e7ea0c4383c43f871bb8029137c32c05b
2   config,
3   lib,
4   pkgs,
5   ...
6 }:
8 let
9   inherit (builtins)
10     hashString
11     map
12     substring
13     toJSON
14     toString
15     unsafeDiscardStringContext
16     ;
18   inherit (lib)
19     any
20     assertMsg
21     attrValues
22     concatStringsSep
23     escapeShellArg
24     filterAttrs
25     hasPrefix
26     isStorePath
27     literalExpression
28     mapAttrs'
29     mapAttrsToList
30     mkDefault
31     mkEnableOption
32     mkIf
33     mkOption
34     mkPackageOption
35     mkRemovedOptionModule
36     mkRenamedOptionModule
37     nameValuePair
38     optional
39     optionalAttrs
40     optionals
41     teams
42     toShellVar
43     types
44     ;
46   cfg = config.services.gitlab-runner;
47   hasDocker = config.virtualisation.docker.enable;
48   hasPodman = config.virtualisation.podman.enable && config.virtualisation.podman.dockerSocket.enable;
50   /*
51     The whole logic of this module is to diff the hashes of the desired vs existing runners
52     The hash is recorded in the runner's name because we can't do better yet
53     See https://gitlab.com/gitlab-org/gitlab-runner/-/issues/29350 for more details
54   */
55   genRunnerName =
56     name: service:
57     let
58       hash = substring 0 12 (hashString "md5" (unsafeDiscardStringContext (toJSON service)));
59     in
60     if service ? description && service.description != null then
61       "${hash} ${service.description}"
62     else
63       "${name}_${config.networking.hostName}_${hash}";
65   hashedServices = mapAttrs' (
66     name: service: nameValuePair (genRunnerName name service) service
67   ) cfg.services;
68   configPath = ''"$HOME"/.gitlab-runner/config.toml'';
69   configureScript = pkgs.writeShellApplication {
70     name = "gitlab-runner-configure";
71     runtimeInputs =
72       [ cfg.package ]
73       ++ (with pkgs; [
74         bash
75         gawk
76         jq
77         moreutils
78         remarshal
79         util-linux
80         perl
81         python3
82       ]);
83     text =
84       if (cfg.configFile != null) then
85         ''
86           cp ${cfg.configFile} ${configPath}
87           # make config file readable by service
88           chown -R --reference="$HOME" "$(dirname ${configPath})"
89         ''
90       else
91         ''
92           export CONFIG_FILE=${configPath}
94           mkdir -p "$(dirname ${configPath})"
95           touch ${configPath}
97           # update global options
98           remarshal --if toml --of json --stringify ${configPath} \
99             | jq -cM 'with_entries(select([.key] | inside(["runners"])))' \
100             | jq -scM '.[0] + .[1]' - <(echo ${escapeShellArg (toJSON cfg.settings)}) \
101             | remarshal --if json --of toml \
102             | sponge ${configPath}
104           # remove no longer existing services
105           gitlab-runner verify --delete
107           ${toShellVar "NEEDED_SERVICES" (lib.mapAttrs (name: value: 1) hashedServices)}
109           declare -A REGISTERED_SERVICES
111           while IFS="," read -r name token;
112           do
113             REGISTERED_SERVICES["$name"]="$token"
114           done < <(gitlab-runner --log-format json list 2>&1 | grep Token  | jq -r '.msg +"," + .Token')
116           echo "NEEDED_SERVICES: " "''${!NEEDED_SERVICES[@]}"
117           echo "REGISTERED_SERVICES:" "''${!REGISTERED_SERVICES[@]}"
119           # difference between current and desired state
120           declare -A NEW_SERVICES
121           for name in "''${!NEEDED_SERVICES[@]}"; do
122             if [ ! -v 'REGISTERED_SERVICES[$name]' ]; then
123               NEW_SERVICES[$name]=1
124             fi
125           done
127           declare -A OLD_SERVICES
128           # shellcheck disable=SC2034
129           for name in "''${!REGISTERED_SERVICES[@]}"; do
130             if [ ! -v 'NEEDED_SERVICES[$name]' ]; then
131               OLD_SERVICES[$name]=1
132             fi
133           done
135           # register new services
136           ${concatStringsSep "\n" (
137             mapAttrsToList (name: service: ''
138               # TODO so here we should mention NEW_SERVICES
139               if [ -v 'NEW_SERVICES["${name}"]' ] ; then
140                 bash -c ${
141                   escapeShellArg (
142                     concatStringsSep " \\\n " (
143                       [
144                         "set -a && source ${
145                           if service.registrationConfigFile != null then
146                             service.registrationConfigFile
147                           else
148                             service.authenticationTokenConfigFile
149                         } &&"
150                         "gitlab-runner register"
151                         "--non-interactive"
152                         "--name '${name}'"
153                         "--executor ${service.executor}"
154                         "--limit ${toString service.limit}"
155                         "--request-concurrency ${toString service.requestConcurrency}"
156                       ]
157                       ++ optional (
158                         service.authenticationTokenConfigFile == null
159                       ) "--maximum-timeout ${toString service.maximumTimeout}"
160                       ++ service.registrationFlags
161                       ++ optional (service.buildsDir != null) "--builds-dir ${service.buildsDir}"
162                       ++ optional (service.cloneUrl != null) "--clone-url ${service.cloneUrl}"
163                       ++ optional (
164                         service.preGetSourcesScript != null
165                       ) "--pre-get-sources-script ${service.preGetSourcesScript}"
166                       ++ optional (
167                         service.postGetSourcesScript != null
168                       ) "--post-get-sources-script ${service.postGetSourcesScript}"
169                       ++ optional (service.preBuildScript != null) "--pre-build-script ${service.preBuildScript}"
170                       ++ optional (service.postBuildScript != null) "--post-build-script ${service.postBuildScript}"
171                       ++ optional (
172                         service.authenticationTokenConfigFile == null && service.tagList != [ ]
173                       ) "--tag-list ${concatStringsSep "," service.tagList}"
174                       ++ optional (service.authenticationTokenConfigFile == null && service.runUntagged) "--run-untagged"
175                       ++ optional (
176                         service.authenticationTokenConfigFile == null && service.protected
177                       ) "--access-level ref_protected"
178                       ++ optional service.debugTraceDisabled "--debug-trace-disabled"
179                       ++ map (e: "--env ${escapeShellArg e}") (
180                         mapAttrsToList (name: value: "${name}=${value}") service.environmentVariables
181                       )
182                       ++ optionals (hasPrefix "docker" service.executor) (
183                         assert (
184                           assertMsg (
185                             service.dockerImage != null
186                           ) "dockerImage option is required for ${service.executor} executor (${name})"
187                         );
188                         [ "--docker-image ${service.dockerImage}" ]
189                         ++ optional service.dockerDisableCache "--docker-disable-cache"
190                         ++ optional service.dockerPrivileged "--docker-privileged"
191                         ++ map (v: "--docker-volumes ${escapeShellArg v}") service.dockerVolumes
192                         ++ map (v: "--docker-extra-hosts ${escapeShellArg v}") service.dockerExtraHosts
193                         ++ map (v: "--docker-allowed-images ${escapeShellArg v}") service.dockerAllowedImages
194                         ++ map (v: "--docker-allowed-services ${escapeShellArg v}") service.dockerAllowedServices
195                       )
196                     )
197                   )
198                 } && sleep 1 || exit 1
199               fi
200             '') hashedServices
201           )}
203           # check key is in array https://stackoverflow.com/questions/30353951/how-to-check-if-dictionary-contains-a-key-in-bash
205           echo "NEW_SERVICES: ''${NEW_SERVICES[*]}"
206           echo "OLD_SERVICES: ''${OLD_SERVICES[*]}"
207           # unregister old services
208           for NAME in "''${!OLD_SERVICES[@]}"
209           do
210             [ -n "$NAME" ] && gitlab-runner unregister \
211               --name "$NAME" && sleep 1
212           done
214           # make config file readable by service
215           chown -R --reference="$HOME" "$(dirname ${configPath})"
216         '';
217   };
218   startScript = pkgs.writeShellScriptBin "gitlab-runner-start" ''
219     export CONFIG_FILE=${configPath}
220     exec gitlab-runner run --working-directory $HOME
221   '';
224   options.services.gitlab-runner = {
225     enable = mkEnableOption "Gitlab Runner";
226     configFile = mkOption {
227       type = types.nullOr types.path;
228       default = null;
229       description = ''
230         Configuration file for gitlab-runner.
232         {option}`configFile` takes precedence over {option}`services`.
233         {option}`checkInterval` and {option}`concurrent` will be ignored too.
235         This option is deprecated, please use {option}`services` instead.
236         You can use {option}`registrationConfigFile` and
237         {option}`registrationFlags`
238         for settings not covered by this module.
239       '';
240     };
241     settings = mkOption {
242       type = types.submodule {
243         freeformType = (pkgs.formats.json { }).type;
244       };
245       default = { };
246       description = ''
247         Global gitlab-runner configuration. See
248         <https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section>
249         for supported values.
250       '';
251     };
252     gracefulTermination = mkOption {
253       type = types.bool;
254       default = false;
255       description = ''
256         Finish all remaining jobs before stopping.
257         If not set gitlab-runner will stop immediately without waiting
258         for jobs to finish, which will lead to failed builds.
259       '';
260     };
261     gracefulTimeout = mkOption {
262       type = types.str;
263       default = "infinity";
264       example = "5min 20s";
265       description = ''
266         Time to wait until a graceful shutdown is turned into a forceful one.
267       '';
268     };
269     package = mkPackageOption pkgs "gitlab-runner" {
270       example = "gitlab-runner_1_11";
271     };
272     extraPackages = mkOption {
273       type = types.listOf types.package;
274       default = [ ];
275       description = ''
276         Extra packages to add to PATH for the gitlab-runner process.
277       '';
278     };
279     services = mkOption {
280       description = "GitLab Runner services.";
281       default = { };
282       example = literalExpression ''
283         {
284           # runner for building in docker via host's nix-daemon
285           # nix store will be readable in runner, might be insecure
286           nix = {
287             # File should contain at least these two variables:
288             # - `CI_SERVER_URL`
289             # - `REGISTRATION_TOKEN`
290             #
291             # NOTE: Support for runner registration tokens will be removed in GitLab 18.0.
292             # Please migrate to runner authentication tokens soon. For reference, the example
293             # runners below this one are configured with authentication tokens instead.
294             registrationConfigFile = "/run/secrets/gitlab-runner-registration";
296             dockerImage = "alpine";
297             dockerVolumes = [
298               "/nix/store:/nix/store:ro"
299               "/nix/var/nix/db:/nix/var/nix/db:ro"
300               "/nix/var/nix/daemon-socket:/nix/var/nix/daemon-socket:ro"
301             ];
302             dockerDisableCache = true;
303             preBuildScript = pkgs.writeScript "setup-container" '''
304               mkdir -p -m 0755 /nix/var/log/nix/drvs
305               mkdir -p -m 0755 /nix/var/nix/gcroots
306               mkdir -p -m 0755 /nix/var/nix/profiles
307               mkdir -p -m 0755 /nix/var/nix/temproots
308               mkdir -p -m 0755 /nix/var/nix/userpool
309               mkdir -p -m 1777 /nix/var/nix/gcroots/per-user
310               mkdir -p -m 1777 /nix/var/nix/profiles/per-user
311               mkdir -p -m 0755 /nix/var/nix/profiles/per-user/root
312               mkdir -p -m 0700 "$HOME/.nix-defexpr"
314               . ''${pkgs.nix}/etc/profile.d/nix.sh
316               ''${pkgs.nix}/bin/nix-env -i ''${concatStringsSep " " (with pkgs; [ nix cacert git openssh ])}
318               ''${pkgs.nix}/bin/nix-channel --add https://nixos.org/channels/nixpkgs-unstable
319               ''${pkgs.nix}/bin/nix-channel --update nixpkgs
320             ''';
321             environmentVariables = {
322               ENV = "/etc/profile";
323               USER = "root";
324               NIX_REMOTE = "daemon";
325               PATH = "/nix/var/nix/profiles/default/bin:/nix/var/nix/profiles/default/sbin:/bin:/sbin:/usr/bin:/usr/sbin";
326               NIX_SSL_CERT_FILE = "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt";
327             };
328             tagList = [ "nix" ];
329           };
330           # runner for building docker images
331           docker-images = {
332             # File should contain at least these two variables:
333             # `CI_SERVER_URL`
334             # `CI_SERVER_TOKEN`
335             authenticationTokenConfigFile = "/run/secrets/gitlab-runner-docker-images-token-env";
337             dockerImage = "docker:stable";
338             dockerVolumes = [
339               "/var/run/docker.sock:/var/run/docker.sock"
340             ];
341             tagList = [ "docker-images" ];
342           };
343           # runner for executing stuff on host system (very insecure!)
344           # make sure to add required packages (including git!)
345           # to `environment.systemPackages`
346           shell = {
347             # File should contain at least these two variables:
348             # `CI_SERVER_URL`
349             # `CI_SERVER_TOKEN`
350             authenticationTokenConfigFile = "/run/secrets/gitlab-runner-shell-token-env";
352             executor = "shell";
353             tagList = [ "shell" ];
354           };
355           # runner for everything else
356           default = {
357             # File should contain at least these two variables:
358             # `CI_SERVER_URL`
359             # `CI_SERVER_TOKEN`
360             authenticationTokenConfigFile = "/run/secrets/gitlab-runner-default-token-env";
361             dockerImage = "debian:stable";
362           };
363         }
364       '';
365       type = types.attrsOf (
366         types.submodule {
367           options = {
368             authenticationTokenConfigFile = mkOption {
369               type = with types; nullOr path;
370               default = null;
371               description = ''
372                 Absolute path to a file containing environment variables used for
373                 gitlab-runner registrations with *runner authentication tokens*.
374                 They replace the deprecated *runner registration tokens*, as
375                 outlined in the [GitLab documentation].
377                 A list of all supported environment variables can be found with
378                 `gitlab-runner register --help`.
380                 The ones you probably want to set are:
381                 - `CI_SERVER_URL=<CI server URL>`
382                 - `CI_SERVER_TOKEN=<runner authentication token secret>`
384                 ::: {.warning}
385                 Make sure to use a quoted absolute path,
386                 or it is going to be copied to Nix Store.
387                 :::
389                 [GitLab documentation]: https://docs.gitlab.com/17.0/ee/ci/runners/new_creation_workflow.html#estimated-time-frame-for-planned-changes
390               '';
391             };
392             registrationConfigFile = mkOption {
393               type = with types; nullOr path;
394               default = null;
395               description = ''
396                 Absolute path to a file with environment variables
397                 used for gitlab-runner registration with *runner registration
398                 tokens*.
400                 A list of all supported environment variables can be found in
401                 `gitlab-runner register --help`.
403                 The ones you probably want to set are:
404                 - `CI_SERVER_URL=<CI server URL>`
405                 - `REGISTRATION_TOKEN=<registration secret>`
407                 Support for *runner registration tokens* is deprecated since
408                 GitLab 16.0, has been disabled by default in GitLab 17.0 and
409                 will be removed in GitLab 18.0, as outlined in the
410                 [GitLab documentation]. Please consider migrating to
411                 [runner authentication tokens] and check the documentation on
412                 {option}`services.gitlab-runner.services.<name>.authenticationTokenConfigFile`.
414                 ::: {.warning}
415                 Make sure to use a quoted absolute path,
416                 or it is going to be copied to Nix Store.
417                 :::
419                 [GitLab documentation]: https://docs.gitlab.com/17.0/ee/ci/runners/new_creation_workflow.html#estimated-time-frame-for-planned-changes
420                 [runner authentication tokens]: https://docs.gitlab.com/17.0/ee/ci/runners/new_creation_workflow.html#the-new-runner-registration-workflow
421               '';
422             };
423             registrationFlags = mkOption {
424               type = types.listOf types.str;
425               default = [ ];
426               example = [ "--docker-helper-image my/gitlab-runner-helper" ];
427               description = ''
428                 Extra command-line flags passed to
429                 `gitlab-runner register`.
430                 Execute `gitlab-runner register --help`
431                 for a list of supported flags.
432               '';
433             };
434             environmentVariables = mkOption {
435               type = types.attrsOf types.str;
436               default = { };
437               example = {
438                 NAME = "value";
439               };
440               description = ''
441                 Custom environment variables injected to build environment.
442                 For secrets you can use {option}`registrationConfigFile`
443                 with `RUNNER_ENV` variable set.
444               '';
445             };
446             description = mkOption {
447               type = types.nullOr types.str;
448               default = null;
449               description = ''
450                 Name/description of the runner.
451               '';
452             };
453             executor = mkOption {
454               type = types.str;
455               default = "docker";
456               description = ''
457                 Select executor, eg. shell, docker, etc.
458                 See [runner documentation](https://docs.gitlab.com/runner/executors/README.html) for more information.
459               '';
460             };
461             buildsDir = mkOption {
462               type = types.nullOr types.path;
463               default = null;
464               example = "/var/lib/gitlab-runner/builds";
465               description = ''
466                 Absolute path to a directory where builds will be stored
467                 in context of selected executor (Locally, Docker, SSH).
468               '';
469             };
470             cloneUrl = mkOption {
471               type = types.nullOr types.str;
472               default = null;
473               example = "http://gitlab.example.local";
474               description = ''
475                 Overwrite the URL for the GitLab instance. Used if the Runner can’t connect to GitLab on the URL GitLab exposes itself.
476               '';
477             };
478             dockerImage = mkOption {
479               type = types.nullOr types.str;
480               default = null;
481               description = ''
482                 Docker image to be used.
483               '';
484             };
485             dockerVolumes = mkOption {
486               type = types.listOf types.str;
487               default = [ ];
488               example = [ "/var/run/docker.sock:/var/run/docker.sock" ];
489               description = ''
490                 Bind-mount a volume and create it
491                 if it doesn't exist prior to mounting.
492               '';
493             };
494             dockerDisableCache = mkOption {
495               type = types.bool;
496               default = false;
497               description = ''
498                 Disable all container caching.
499               '';
500             };
501             dockerPrivileged = mkOption {
502               type = types.bool;
503               default = false;
504               description = ''
505                 Give extended privileges to container.
506               '';
507             };
508             dockerExtraHosts = mkOption {
509               type = types.listOf types.str;
510               default = [ ];
511               example = [ "other-host:127.0.0.1" ];
512               description = ''
513                 Add a custom host-to-IP mapping.
514               '';
515             };
516             dockerAllowedImages = mkOption {
517               type = types.listOf types.str;
518               default = [ ];
519               example = [
520                 "ruby:*"
521                 "python:*"
522                 "php:*"
523                 "my.registry.tld:5000/*:*"
524               ];
525               description = ''
526                 Whitelist allowed images.
527               '';
528             };
529             dockerAllowedServices = mkOption {
530               type = types.listOf types.str;
531               default = [ ];
532               example = [
533                 "postgres:9"
534                 "redis:*"
535                 "mysql:*"
536               ];
537               description = ''
538                 Whitelist allowed services.
539               '';
540             };
541             preGetSourcesScript = mkOption {
542               type = types.nullOr (types.either types.str types.path);
543               default = null;
544               description = ''
545                 Runner-specific command script executed before code is pulled.
546               '';
547             };
548             postGetSourcesScript = mkOption {
549               type = types.nullOr (types.either types.str types.path);
550               default = null;
551               description = ''
552                 Runner-specific command script executed after code is pulled.
553               '';
554             };
555             preBuildScript = mkOption {
556               type = types.nullOr (types.either types.str types.path);
557               default = null;
558               description = ''
559                 Runner-specific command script executed after code is pulled,
560                 just before build executes.
561               '';
562             };
563             postBuildScript = mkOption {
564               type = types.nullOr (types.either types.str types.path);
565               default = null;
566               description = ''
567                 Runner-specific command script executed after code is pulled
568                 and just after build executes.
569               '';
570             };
571             tagList = mkOption {
572               type = types.listOf types.str;
573               default = [ ];
574               description = ''
575                 Tag list.
577                 This option has no effect for runners registered with an runner
578                 authentication tokens and will be ignored.
579               '';
580             };
581             runUntagged = mkOption {
582               type = types.bool;
583               default = false;
584               description = ''
585                 Register to run untagged builds; defaults to
586                 `true` when {option}`tagList` is empty.
588                 This option has no effect for runners registered with an runner
589                 authentication tokens and will be ignored.
590               '';
591             };
592             limit = mkOption {
593               type = types.int;
594               default = 0;
595               description = ''
596                 Limit how many jobs can be handled concurrently by this service.
597                 0 (default) simply means don't limit.
598               '';
599             };
600             requestConcurrency = mkOption {
601               type = types.int;
602               default = 0;
603               description = ''
604                 Limit number of concurrent requests for new jobs from GitLab.
605               '';
606             };
607             maximumTimeout = mkOption {
608               type = types.int;
609               default = 0;
610               description = ''
611                 What is the maximum timeout (in seconds) that will be set for
612                 job when using this Runner. 0 (default) simply means don't limit.
614                 This option has no effect for runners registered with an runner
615                 authentication tokens and will be ignored.
616               '';
617             };
618             protected = mkOption {
619               type = types.bool;
620               default = false;
621               description = ''
622                 When set to true Runner will only run on pipelines
623                 triggered on protected branches.
625                 This option has no effect for runners registered with an runner
626                 authentication tokens and will be ignored.
627               '';
628             };
629             debugTraceDisabled = mkOption {
630               type = types.bool;
631               default = false;
632               description = ''
633                 When set to true Runner will disable the possibility of
634                 using the `CI_DEBUG_TRACE` feature.
635               '';
636             };
637           };
638         }
639       );
640     };
641     clear-docker-cache = {
642       enable = mkOption {
643         type = types.bool;
644         default = false;
645         description = ''
646           Whether to periodically prune gitlab runner's Docker resources. If
647           enabled, a systemd timer will run {command}`clear-docker-cache` as
648           specified by the `dates` option.
649         '';
650       };
652       flags = mkOption {
653         type = types.listOf types.str;
654         default = [ ];
655         example = [ "prune" ];
656         description = ''
657           Any additional flags passed to {command}`clear-docker-cache`.
658         '';
659       };
661       dates = mkOption {
662         default = "weekly";
663         type = types.str;
664         description = ''
665           Specification (in the format described by
666           {manpage}`systemd.time(7)`) of the time at
667           which the prune will occur.
668         '';
669       };
671       package = mkOption {
672         default = config.virtualisation.docker.package;
673         defaultText = literalExpression "config.virtualisation.docker.package";
674         example = literalExpression "pkgs.docker";
675         description = "Docker package to use for clearing up docker cache.";
676       };
677     };
678   };
679   config = mkIf cfg.enable {
680     assertions = mapAttrsToList (name: serviceConfig: {
681       assertion =
682         serviceConfig.registrationConfigFile == null || serviceConfig.authenticationTokenConfigFile == null;
683       message = "`services.gitlab-runner.${name}.registrationConfigFile` and `services.gitlab-runner.services.${name}.authenticationTokenConfigFile` are mutually exclusive.";
684     }) cfg.services;
686     warnings =
687       mapAttrsToList (
688         name: serviceConfig:
689         "services.gitlab-runner.services.${name}.`registrationConfigFile` points to a file in Nix Store. You should use quoted absolute path to prevent this."
690       ) (filterAttrs (name: serviceConfig: isStorePath serviceConfig.registrationConfigFile) cfg.services)
691       ++
692         mapAttrsToList
693           (
694             name: serviceConfig:
695             "services.gitlab-runner.services.${name}.`authenticationTokenConfigFile` points to a file in Nix Store. You should use quoted absolute path to prevent this."
696           )
697           (
698             filterAttrs (
699               name: serviceConfig: isStorePath serviceConfig.authenticationTokenConfigFile
700             ) cfg.services
701           )
702       ++
703         mapAttrsToList
704           (name: serviceConfig: ''
705             Runner registration tokens have been deprecated and disabled by default in GitLab >= 17.0.
706             Consider migrating to runner authentication tokens by setting `services.gitlab-runner.services.${name}.authenticationTokenConfigFile`.
707             https://docs.gitlab.com/17.0/ee/ci/runners/new_creation_workflow.html'')
708           (
709             filterAttrs (name: serviceConfig: serviceConfig.authenticationTokenConfigFile == null) cfg.services
710           )
711       ++
712         mapAttrsToList
713           (
714             name: serviceConfig:
715             ''`services.gitlab-runner.services.${name}.protected` with runner authentication tokens has no effect and will be ignored. Please remove it from your configuration.''
716           )
717           (
718             filterAttrs (
719               name: serviceConfig:
720               serviceConfig.authenticationTokenConfigFile != null && serviceConfig.protected == true
721             ) cfg.services
722           )
723       ++
724         mapAttrsToList
725           (
726             name: serviceConfig:
727             ''`services.gitlab-runner.services.${name}.runUntagged` with runner authentication tokens has no effect and will be ignored. Please remove it from your configuration.''
728           )
729           (
730             filterAttrs (
731               name: serviceConfig:
732               serviceConfig.authenticationTokenConfigFile != null && serviceConfig.runUntagged == true
733             ) cfg.services
734           )
735       ++
736         mapAttrsToList
737           (
738             name: v:
739             ''`services.gitlab-runner.services.${name}.maximumTimeout` with runner authentication tokens has no effect and will be ignored. Please remove it from your configuration.''
740           )
741           (
742             filterAttrs (
743               name: serviceConfig:
744               serviceConfig.authenticationTokenConfigFile != null && serviceConfig.maximumTimeout != 0
745             ) cfg.services
746           )
747       ++
748         mapAttrsToList
749           (
750             name: v:
751             ''`services.gitlab-runner.services.${name}.tagList` with runner authentication tokens has no effect and will be ignored. Please remove it from your configuration.''
752           )
753           (
754             filterAttrs (
755               serviceName: serviceConfig:
756               serviceConfig.authenticationTokenConfigFile != null && serviceConfig.tagList != [ ]
757             ) cfg.services
758           );
760     environment.systemPackages = [ cfg.package ];
761     systemd.services.gitlab-runner = {
762       description = "Gitlab Runner";
763       documentation = [ "https://docs.gitlab.com/runner/" ];
764       after =
765         [ "network.target" ] ++ optional hasDocker "docker.service" ++ optional hasPodman "podman.service";
767       requires = optional hasDocker "docker.service" ++ optional hasPodman "podman.service";
768       wantedBy = [ "multi-user.target" ];
769       environment = config.networking.proxy.envVars // {
770         HOME = "/var/lib/gitlab-runner";
771       };
773       path =
774         (with pkgs; [
775           bash
776           gawk
777           jq
778           moreutils
779           remarshal
780           util-linux
781         ])
782         ++ [ cfg.package ]
783         ++ cfg.extraPackages;
785       reloadIfChanged = true;
786       serviceConfig =
787         {
788           # Set `DynamicUser` under `systemd.services.gitlab-runner.serviceConfig`
789           # to `lib.mkForce false` in your configuration to run this service as root.
790           # You can also set `User` and `Group` options to run this service as desired user.
791           # Make sure to restart service or changes won't apply.
792           DynamicUser = true;
793           StateDirectory = "gitlab-runner";
794           SupplementaryGroups = optional hasDocker "docker" ++ optional hasPodman "podman";
795           ExecStartPre = "!${configureScript}/bin/gitlab-runner-configure";
796           ExecStart = "${startScript}/bin/gitlab-runner-start";
797           ExecReload = "!${configureScript}/bin/gitlab-runner-configure";
798         }
799         // optionalAttrs cfg.gracefulTermination {
800           TimeoutStopSec = "${cfg.gracefulTimeout}";
801           KillSignal = "SIGQUIT";
802           KillMode = "process";
803         };
804     };
805     # Enable periodic clear-docker-cache script
806     systemd.services.gitlab-runner-clear-docker-cache =
807       mkIf (cfg.clear-docker-cache.enable && (any (s: s.executor == "docker") (attrValues cfg.services)))
808         {
809           description = "Prune gitlab-runner docker resources";
810           restartIfChanged = false;
811           unitConfig.X-StopOnRemoval = false;
813           serviceConfig.Type = "oneshot";
815           path = [
816             cfg.clear-docker-cache.package
817             pkgs.gawk
818           ];
820           script = ''
821             ${pkgs.gitlab-runner}/bin/clear-docker-cache ${toString cfg.clear-docker-cache.flags}
822           '';
824           startAt = cfg.clear-docker-cache.dates;
825         };
826     # Enable docker if `docker` executor is used in any service
827     virtualisation.docker.enable = mkIf (any (s: s.executor == "docker") (attrValues cfg.services)) (
828       mkDefault true
829     );
830   };
831   imports = [
832     (mkRenamedOptionModule
833       [ "services" "gitlab-runner" "packages" ]
834       [ "services" "gitlab-runner" "extraPackages" ]
835     )
836     (mkRemovedOptionModule [
837       "services"
838       "gitlab-runner"
839       "configOptions"
840     ] "Use services.gitlab-runner.services option instead")
841     (mkRemovedOptionModule [
842       "services"
843       "gitlab-runner"
844       "workDir"
845     ] "You should move contents of workDir (if any) to /var/lib/gitlab-runner")
847     (mkRenamedOptionModule
848       [ "services" "gitlab-runner" "checkInterval" ]
849       [ "services" "gitlab-runner" "settings" "check_interval" ]
850     )
851     (mkRenamedOptionModule
852       [ "services" "gitlab-runner" "concurrent" ]
853       [ "services" "gitlab-runner" "settings" "concurrent" ]
854     )
855     (mkRenamedOptionModule
856       [ "services" "gitlab-runner" "sentryDSN" ]
857       [ "services" "gitlab-runner" "settings" "sentry_dsn" ]
858     )
859     (mkRenamedOptionModule
860       [ "services" "gitlab-runner" "prometheusListenAddress" ]
861       [ "services" "gitlab-runner" "settings" "listen_address" ]
862     )
864     (mkRenamedOptionModule
865       [ "services" "gitlab-runner" "sessionServer" "listenAddress" ]
866       [ "services" "gitlab-runner" "settings" "session_server" "listen_address" ]
867     )
868     (mkRenamedOptionModule
869       [ "services" "gitlab-runner" "sessionServer" "advertiseAddress" ]
870       [ "services" "gitlab-runner" "settings" "session_server" "advertise_address" ]
871     )
872     (mkRenamedOptionModule
873       [ "services" "gitlab-runner" "sessionServer" "sessionTimeout" ]
874       [ "services" "gitlab-runner" "settings" "session_server" "session_timeout" ]
875     )
876   ];
878   meta.maintainers = teams.gitlab.members;