summaryrefslogtreecommitdiff
path: root/modules/services/github-runner/service.nix
diff options
context:
space:
mode:
authorVincent Haupert <vincent@yaxi.tech>2024-02-28 09:40:25 +0100
committerVincent Haupert <vincent@yaxi.tech>2024-02-28 09:40:25 +0100
commit06f5dab0657f0a51c8a220bdb2b6089ce68b2e96 (patch)
treefe8c69c95e23e91037f22e1a91ec9347c84a8f31 /modules/services/github-runner/service.nix
parent0e6857fa1d632637488666c08e7b02c08e3178f8 (diff)
github-runners: adapt to NixOS module
While #859 added basic support for configuring GitHub runners through nix-darwin, it did not yet support all of the options the NixOS module offers. I am aware that this is a rather big overhaul. I think, however, that it's worth it: - Copies the `options.nix` from the [NixOS module] with only minor adaptations. This should help to keep track of any changes to it. - Respect the `workDir` config option. So far, the implementation didn't even read the value of the option. - Allow configuring a custom user and group. If both are `null`, nix-darwin manages the `_github-runner` user shared among all instances. Take care of creating your own users if that's not what you want. - Also creates the necessary directories for state, logs and the working directory (unless `workDir != null`). It uses the following locations: * state: `/var/lib/github-runners/${name}` * logs: `/var/log/github-runners/${name}` * work: The value of `workDir` or `/var/run/github-runners/${name}` if (`workDir == null`). We have to create the logs directory before starting the service since launchd expects that the `Standard{Error,Out}Path` exist. We do this by prepending to [`system.activationScripts.launchd.text`]. All directories belong to the configured `user` and `group`. - Warn if a `tokenFile` points to the Nix store. [NixOS module]: https://github.com/NixOS/nixpkgs/blob/3c30c56/nixos/modules/services/continuous-integration/github-runner/options.nix [`system.activationScripts.launchd.text`]: https://github.com/LnL7/nix-darwin/blob/bbde06b/modules/system/launchd.nix#L99-L123
Diffstat (limited to 'modules/services/github-runner/service.nix')
-rw-r--r--modules/services/github-runner/service.nix181
1 files changed, 181 insertions, 0 deletions
diff --git a/modules/services/github-runner/service.nix b/modules/services/github-runner/service.nix
new file mode 100644
index 0000000..53f2cdd
--- /dev/null
+++ b/modules/services/github-runner/service.nix
@@ -0,0 +1,181 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+ mkSvcName = name: "github-runner-${name}";
+ mkStateDir = cfg: "/var/lib/github-runners/${cfg.name}";
+ mkLogDir = cfg: "/var/log/github-runners/${cfg.name}";
+ mkWorkDir = cfg: if (cfg.workDir != null) then cfg.workDir else "/var/run/github-runners/${cfg.name}";
+in
+{
+ config.assertions = flatten (
+ flip mapAttrsToList config.services.github-runners (name: cfg: map (mkIf cfg.enable) [
+ {
+ assertion = (cfg.user == null && cfg.group == null) || (cfg.user != null);
+ message = "`services.github-runners.${name}`: Either set `user` and `group` to `null` to have nix-darwin manage them or set at least `user` explicitly";
+ }
+ {
+ assertion = !cfg.noDefaultLabels || (cfg.extraLabels != [ ]);
+ message = "`services.github-runners.${name}`: The `extraLabels` option is mandatory if `noDefaultLabels` is set";
+ }
+ ])
+ );
+
+ config.warnings = flatten (
+ flip mapAttrsToList config.services.github-runners (name: cfg: map (mkIf cfg.enable) [
+ (
+ mkIf (hasPrefix builtins.storeDir cfg.tokenFile)
+ "`services.github-runners.${name}`: `tokenFile` contains a secret but points to the world-readable Nix store."
+ )
+ ])
+ );
+
+ # Create the necessary directories and make the service user/group their owner
+ # This has to happen *after* nix-darwin user creation and *before* any launchd service gets started.
+ config.system.activationScripts = mkMerge (flip mapAttrsToList config.services.github-runners (name: cfg:
+ let
+ user = config.launchd.daemons.${mkSvcName name}.serviceConfig.UserName;
+ group =
+ if config.launchd.daemons.${mkSvcName name}.serviceConfig.GroupName != null
+ then config.launchd.daemons.${mkSvcName name}.serviceConfig.GroupName
+ else "";
+ in
+ {
+ launchd = mkIf cfg.enable {
+ text = mkBefore (''
+ echo >&2 "setting up GitHub Runner '${cfg.name}'..."
+
+ ${pkgs.coreutils}/bin/mkdir -p -m 0750 ${escapeShellArg (mkStateDir cfg)}
+ ${pkgs.coreutils}/bin/chown ${user}:${group} ${escapeShellArg (mkStateDir cfg)}
+
+ ${pkgs.coreutils}/bin/mkdir -p -m 0750 ${escapeShellArg (mkLogDir cfg)}
+ ${pkgs.coreutils}/bin/chown ${user}:${group} ${escapeShellArg (mkLogDir cfg)}
+ '' + optionalString (cfg.workDir == null) ''
+ ${pkgs.coreutils}/bin/mkdir -p -m 0750 ${escapeShellArg (mkWorkDir cfg)}
+ ${pkgs.coreutils}/bin/chown ${user}:${group} ${escapeShellArg (mkWorkDir cfg)}
+ '');
+ };
+ }));
+
+ config.launchd.daemons = flip mapAttrs' config.services.github-runners (name: cfg:
+ let
+ package = cfg.package.override (old: optionalAttrs (hasAttr "nodeRuntimes" old) { inherit (cfg) nodeRuntimes; });
+ stateDir = mkStateDir cfg;
+ logDir = mkLogDir cfg;
+ workDir = mkWorkDir cfg;
+ in
+ nameValuePair
+ (mkSvcName name)
+ (mkIf cfg.enable {
+ environment = {
+ HOME = stateDir;
+ RUNNER_ROOT = stateDir;
+ } // cfg.extraEnvironment;
+
+ # Minimal package set for `actions/checkout`
+ path = (with pkgs; [
+ bash
+ coreutils
+ git
+ gnutar
+ gzip
+ ]) ++ [
+ config.nix.package
+ ] ++ cfg.extraPackages;
+
+ script =
+ let
+ configure = pkgs.writeShellApplication {
+ name = "configure-github-runner-${name}";
+ text = ''
+ export RUNNER_ROOT
+
+ args=(
+ --unattended
+ --disableupdate
+ --work ${escapeShellArg workDir}
+ --url ${escapeShellArg cfg.url}
+ --labels ${escapeShellArg (concatStringsSep "," cfg.extraLabels)}
+ ${optionalString (cfg.name != null ) "--name ${escapeShellArg cfg.name}"}
+ ${optionalString cfg.replace "--replace"}
+ ${optionalString (cfg.runnerGroup != null) "--runnergroup ${escapeShellArg cfg.runnerGroup}"}
+ ${optionalString cfg.ephemeral "--ephemeral"}
+ ${optionalString cfg.noDefaultLabels "--no-default-labels"}
+ )
+ # If the token file contains a PAT (i.e., it starts with "ghp_" or "github_pat_"), we have to use the --pat option,
+ # if it is not a PAT, we assume it contains a registration token and use the --token option
+ token=$(<"${cfg.tokenFile}")
+ if [[ "$token" =~ ^ghp_* ]] || [[ "$token" =~ ^github_pat_* ]]; then
+ args+=(--pat "$token")
+ else
+ args+=(--token "$token")
+ fi
+ ${package}/bin/config.sh "''${args[@]}"
+ '';
+ };
+ in
+ ''
+ echo "Configuring GitHub Actions Runner"
+
+ # Always clean the working directory
+ ${pkgs.findutils}/bin/find ${escapeShellArg workDir} -mindepth 1 -delete
+
+ # Clean the $RUNNER_ROOT if we are in ephemeral mode
+ if ${boolToString cfg.ephemeral}; then
+ echo "Cleaning $RUNNER_ROOT"
+ ${pkgs.findutils}/bin/find "$RUNNER_ROOT" -mindepth 1 -delete
+ fi
+
+ # If the `.runner` file does not exist, we assume the runner is not configured
+ if [[ ! -f "$RUNNER_ROOT/.runner" ]]; then
+ ${getExe configure}
+ fi
+
+ # Start the service
+ ${package}/bin/Runner.Listener run --startuptype service
+ '';
+
+ serviceConfig = mkMerge [
+ {
+ GroupName = cfg.group;
+ KeepAlive = {
+ Crashed = false;
+ } // mkIf cfg.ephemeral {
+ SuccessfulExit = true;
+ };
+ ProcessType = "Interactive";
+ RunAtLoad = true;
+ StandardErrorPath = "${logDir}/launchd-stderr.log";
+ StandardOutPath = "${logDir}/launchd-stdout.log";
+ ThrottleInterval = 30;
+ UserName = if (cfg.user != null) then cfg.user else "_github-runner";
+ WatchPaths = [
+ "/etc/resolv.conf"
+ "/Library/Preferences/SystemConfiguration/NetworkInterfaces.plist"
+ cfg.tokenFile
+ ];
+ WorkingDirectory = stateDir;
+ }
+ cfg.serviceOverrides
+ ];
+ }));
+
+ # If any GitHub runner configuration has set both `user` and `group` set to `null`,
+ # manage the user and group `_github-runner` through nix-darwin.
+ config.users = mkIf (any (cfg: cfg.enable && cfg.user == null && cfg.group == null) (attrValues config.services.github-runners)) {
+ users."_github-runner" = {
+ createHome = false;
+ description = "GitHub Runner service user";
+ gid = config.users.groups."_github-runner".gid;
+ home = "/var/lib/github-runners";
+ shell = "/bin/bash";
+ uid = mkDefault 533;
+ };
+ knownUsers = [ "_github-runner" ];
+
+ groups."_github-runner" = {
+ gid = mkDefault 533;
+ description = "GitHub Runner service user group";
+ };
+ knownGroups = [ "_github-runner" ];
+ };
+}