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