Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

github-runners: init module #859

Merged
merged 1 commit into from
Feb 18, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
github-runners: init module
Adds a new module which allows to configure multiple GitHub self-hosted
runners on Darwin. The module is heavily inspired by the nixpkgs NixOS
module. Its implementation differs in some ways:

- There's currently no way to configure the user/group which runs the
  runner. All configured runners share the same user and group.
- No automatic cleanup.
- No advanced sandboxing apart from user/group isolation
veehaitch committed Jan 22, 2024
commit 21b92addaf58b3b8f9f3c21b482f97f96d58895a
1 change: 1 addition & 0 deletions modules/module-list.nix
Original file line number Diff line number Diff line change
@@ -54,6 +54,7 @@
./services/dnsmasq.nix
./services/emacs.nix
./services/eternal-terminal.nix
./services/github-runner
./services/gitlab-runner.nix
./services/hercules-ci-agent
./services/ipfs.nix
79 changes: 79 additions & 0 deletions modules/services/github-runner/config.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{ config, lib, pkgs, ... }:
let
mkSvcName = name: "github-runner-${name}";
mkRootDir = name: "${config.users.users.github-runner.home}/.github-runner/${name}";
mkWorkDir = name: "${mkRootDir name}/_work";
in
with lib;
{
launchd.daemons = flip mapAttrs' config.services.github-runners (name: cfg:
nameValuePair
(mkSvcName name)
(mkIf cfg.enable {
environment = {
RUNNER_ROOT = mkRootDir name;
} // cfg.extraEnvironment;

# Minimal package set for `actions/checkout`
path = (with pkgs; [
bash
coreutils
git
gnutar
gzip
]) ++ [
config.nix.package
] ++ cfg.extraPackages;

script = ''
echo "Configuring GitHub Actions Runner"
mkdir -p ${escapeShellArg (mkRootDir name)}
cd ${escapeShellArg (mkRootDir name)}
args=(
--unattended
--disableupdate
--work ${escapeShellArg (mkWorkDir name)}
--url ${escapeShellArg cfg.url}
--labels ${escapeShellArg (concatStringsSep "," cfg.extraLabels)}
--name ${escapeShellArg cfg.name}
${optionalString cfg.replace "--replace"}
${optionalString (cfg.runnerGroup != null) "--runnergroup ${escapeShellArg cfg.runnerGroup}"}
${optionalString cfg.ephemeral "--ephemeral"}
)
# 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
${cfg.package}/bin/config.sh "''${args[@]}"
# Start the service
${cfg.package}/bin/Runner.Listener run --startuptype service
'';

serviceConfig = mkMerge [
{
KeepAlive = {
Crashed = false;
} // mkIf cfg.ephemeral {
SuccessfulExit = true;
};
GroupName = "github-runner";
ProcessType = "Interactive";
RunAtLoad = true;
ThrottleInterval = 30;
UserName = "github-runner";
WatchPaths = [
"/etc/resolv.conf"
"/Library/Preferences/SystemConfiguration/NetworkInterfaces.plist"
];
WorkingDirectory = config.users.users.github-runner.home;
}
cfg.serviceOverrides
];
}));
}
37 changes: 37 additions & 0 deletions modules/services/github-runner/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{ config, lib, ... }:
let
anyEnabled = lib.any (cfg: cfg.enable) (lib.attrValues config.services.github-runners);
in
{
imports = [
./options.nix
./config.nix
];

config.assertions = lib.mkIf anyEnabled [
{
assertion = lib.elem "github-runner" config.users.knownGroups;
message = "set `users.knownGroups` to enable `github-runner` group";
}
{
assertion = lib.elem "github-runner" config.users.knownUsers;
message = "set `users.knownUsers` to enable `github-runner` user";
}
];

config.users = lib.mkIf anyEnabled {
users."github-runner" = {
createHome = true;
uid = lib.mkDefault 533;
gid = lib.mkDefault config.users.groups.github-runner.gid;
home = lib.mkDefault "/var/lib/github-runners";
shell = "/bin/bash";
description = "GitHub Runner service user";
};

groups."github-runner" = {
gid = lib.mkDefault 533;
description = "GitHub Runner service user group";
};
};
}
181 changes: 181 additions & 0 deletions modules/services/github-runner/options.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
{ lib
, pkgs
, ...
}:

with lib;
{
options.services.github-runners = mkOption {
default = { };
description = mdDoc ''
Configure multiple GitHub Runners.
'';
example = literalExpression ''
{
m1-runner = {
enable = true;
ephemeral = true;
replace = true;
tokenFile = "/secrets/github-org-pat.token";
url = "https://github.com/nixos";
};
m2-runner = {
enable = true;
extraLabels = [ "nixpkgs" ];
replace = true;
tokenFile = "/secrets/github-repo-pat.token";
url = "https://github.com/nixos/nixpkgs";
};
}
'';
type = with types; attrsOf (submodule ({ name, ... }: {
options = {
enable = mkOption {
default = false;
example = true;
description = mdDoc ''
Whether to enable GitHub Actions runner.
Note: GitHub recommends using self-hosted runners with private repositories only. Learn more here:
[About self-hosted runners](https://docs.github.com/en/actions/hosting-your-own-runners/about-self-hosted-runners).
'';
type = lib.types.bool;
};

url = mkOption {
type = types.str;
description = mdDoc ''
Repository to add the runner to.
Changing this option triggers a new runner registration.
IMPORTANT: If your token is org-wide (not per repository), you need to
provide a github org link, not a single repository, so do it like this
`https://github.com/nixos`, not like this
`https://github.com/nixos/nixpkgs`.
Otherwise, you are going to get a `404 NotFound`
from `POST https://api.github.com/actions/runner-registration`
in the configure script.
'';
example = "https://github.com/nixos/nixpkgs";
};

tokenFile = mkOption {
type = types.path;
description = mdDoc ''
The full path to a file which contains either a runner registration token or a
(fine-grained) personal access token (PAT).
The file should contain exactly one line with the token without any newline.
If a registration token is given, it can be used to re-register a runner of the same
name but is time-limited. If the file contains a PAT, the service creates a new
registration token on startup as needed. Make sure the PAT has a scope of
`admin:org` for organization-wide registrations or a scope of
`repo` for a single repository. Fine-grained PATs need read and write permission
to the "Adminstration" resources.
Changing this option or the file's content triggers a new runner registration.
'';
example = "/run/secrets/github-runner/nixos.token";
};

name = mkOption {
type = types.str;
description = mdDoc ''
Name of the runner to configure. Defaults to the attribute name.
Changing this option triggers a new runner registration.
'';
example = "nixos";
default = name;
};

runnerGroup = mkOption {
type = types.nullOr types.str;
description = mdDoc ''
Name of the runner group to add this runner to (defaults to the default runner group).
Changing this option triggers a new runner registration.
'';
default = null;
};

extraLabels = mkOption {
type = types.listOf types.str;
description = mdDoc ''
Extra labels in addition to the default (`["self-hosted", "Linux", "X64"]`).
Changing this option triggers a new runner registration.
'';
example = literalExpression ''[ "nixos" ]'';
default = [ ];
};

replace = mkOption {
type = types.bool;
description = mdDoc ''
Replace any existing runner with the same name.
Without this flag, registering a new runner with the same name fails.
'';
default = false;
};

extraPackages = mkOption {
type = types.listOf types.package;
description = mdDoc ''
Extra packages to add to `PATH` of the service to make them available to workflows.
'';
default = [ ];
};

extraEnvironment = mkOption {
type = types.attrs;
description = mdDoc ''
Extra environment variables to set for the runner, as an attrset.
'';
example = {
GIT_CONFIG = "/path/to/git/config";
};
default = { };
};

serviceOverrides = mkOption {
type = types.attrs;
description = mdDoc ''
Overrides for the systemd service. Can be used to adjust the sandboxing options.
'';
example = {
ProtectHome = false;
};
default = { };
};

package = mkOption {
type = types.package;
description = mdDoc ''
Which github-runner derivation to use.
'';
default = pkgs.github-runner;
defaultText = literalExpression "pkgs.github-runner";
};

ephemeral = mkOption {
type = types.bool;
description = mdDoc ''
If enabled, causes the following behavior:
- Passes the `--ephemeral` flag to the runner configuration script
- De-registers and stops the runner with GitHub after it has processed one job
- The runner wipes some state before it exists
You should only enable this option if `tokenFile` points to a file which contains a
personal access token (PAT). If you're using the option with a registration token, restarting the
service will fail as soon as the registration token expired.
'';
default = false;
};
};
}));
};
}
1 change: 1 addition & 0 deletions release.nix
Original file line number Diff line number Diff line change
@@ -119,6 +119,7 @@ let
tests.services-activate-system = makeTest ./tests/services-activate-system.nix;
tests.services-activate-system-changed-label-prefix = makeTest ./tests/services-activate-system-changed-label-prefix.nix;
tests.services-buildkite-agent = makeTest ./tests/services-buildkite-agent.nix;
tests.services-github-runners = makeTest ./tests/services-github-runners.nix;
tests.services-lorri = makeTest ./tests/services-lorri.nix;
tests.services-nix-daemon = makeTest ./tests/services-nix-daemon.nix;
tests.sockets-nix-daemon = makeTest ./tests/sockets-nix-daemon.nix;
23 changes: 23 additions & 0 deletions tests/services-github-runners.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{ config, pkgs, ... }:
{
users = {
knownUsers = [ "github-runner" ];
knownGroups = [ "github-runner" ];
};

services.github-runners."a-runner" = {
enable = true;
url = "https://github.com/nixos/nixpkgs";
tokenFile = pkgs.writeText "fake-token" "not-a-token";
package = pkgs.runCommand "github-runner-0.0.0" { } "touch $out";
};

test = ''
echo >&2 "checking github-runner service in /Library/LaunchDaemons"
grep "org.nixos.github-runner-a-runner" ${config.out}/Library/LaunchDaemons/org.nixos.github-runner-a-runner.plist
grep "<string>github-runner</string>" ${config.out}/Library/LaunchDaemons/org.nixos.github-runner-a-runner.plist
echo >&2 "checking for user in /activate"
grep "GitHub Runner service user" ${config.out}/activate
'';
}