nixos-config/nixos/easy-github-runners.nix
2024-02-20 17:20:51 -05:00

151 lines
4.8 KiB
Nix

/* Module for setting up personal or org-wide github runners
Limitations
- A runner can run only one job at a time: https://github.com/orgs/community/discussions/26769
- This makes sharing an org-wide runner less useful, unless we create multiple runners.
TODOs
- [x] Run runners in containers
- [ ] macOS runners: https://github.com/LnL7/nix-darwin/issues/582
- [x] Support github orgs
- [ ] Unbreak cachix? https://github.com/cachix/cachix-action/issues/169
- [x] Or switch to nix-serve or attic
*/
top@{ pkgs, lib, config, ... }:
let
inherit (lib) types;
in
{
options = {
services.easy-github-runners = lib.mkOption {
description = ''
Attrset of runners.
The key will be used to identify the runner name. It could be either org
name or the repo path, unless you want to run multiple runners per
org/repo, in which case choose a suitably unique key while setting org
name or repo path in `githubPath` option.
'';
type = types.lazyAttrsOf (types.submodule ({ config, name, ... }: {
options = {
owner = lib.mkOption {
type = types.str;
description = ''
The owner of this repo/org.
The PAC token of this owner must be able to setup runners for this
repo/org.
'';
default = lib.head (lib.splitString "/" config.githubPath);
};
url = lib.mkOption {
type = types.str;
description = ''Github URL for this runner'';
default = "https://github.com/${config.githubPath}";
};
githubPath = lib.mkOption {
type = types.str;
default = name;
description = ''
The path after https://github.com in the URL for this runner
By default, it uses the attr key. If you are running multiple
runners per org or per repo, you may want to explicitly specify
the githubPath to disambiguate.
'';
};
tokenSecretPath = lib.mkOption {
type = types.str;
# By default, we expect personal access token (not runner registeration token)
# Thus, it is bucket by the owner.
default = "gh-selfhosted-tokens/${config.owner}";
readOnly = true;
description = ''
sops-nix key path containing the token for this runner.
'';
};
nixosConfig = lib.mkOption {
type = types.deferredModule;
description = ''
NixOS configuration for the GitHub Runner container
'';
default = { pkgs, ... }: {
nix.settings = {
experimental-features = "nix-command flakes repl-flake";
max-jobs = "auto";
};
};
};
runnerConfig = lib.mkOption {
type = types.lazyAttrsOf types.raw;
description = ''Configuration for the GitHub Runner'';
default = {
extraPackages = with pkgs; [
cachix
nixci
which
coreutils
];
extraLabels = [ "nixos" ];
};
};
};
}));
};
};
config =
let
cfg = config.services.easy-github-runners;
user = "github-runner";
userModule = {
users.users.${user} = {
uid = 1099;
isSystemUser = true;
group = user;
};
users.groups.${user} = { };
};
in
userModule // {
sops.secrets =
lib.flip lib.mapAttrs' cfg (name: cfg:
lib.nameValuePair cfg.tokenSecretPath {
mode = "0440";
});
nix.settings = {
trusted-users = [ user ];
allowed-users = [ user ];
};
containers =
lib.flip lib.mapAttrs' cfg
(name: cfg:
let
tokenFile = top.config.sops.secrets."${cfg.tokenSecretPath}".path;
nameLegal = lib.replaceStrings [ "/" ] [ "-" ] name;
in
lib.nameValuePair ''github-runner-${nameLegal}'' {
autoStart = true;
bindMounts."${tokenFile}" = {
hostPath = tokenFile;
isReadOnly = true;
};
config = { config, pkgs, ... }: {
system.stateVersion = "23.11";
imports = [
userModule
cfg.nixosConfig
];
nix.settings.trusted-users = [ user ]; # for cachix
services.github-runners."${nameLegal}" = cfg.runnerConfig // {
enable = true;
inherit user tokenFile;
inherit (cfg) url;
};
};
});
};
}