1{ lib 2, pkgs 3, ... 4}: 5 6with lib; 7{ 8 options.services.github-runners = mkOption { 9 description = '' 10 Multiple GitHub Runners. 11 ''; 12 example = { 13 runner1 = { 14 enable = true; 15 url = "https://github.com/owner/repo"; 16 name = "runner1"; 17 tokenFile = "/secrets/token1"; 18 }; 19 20 runner2 = { 21 enable = true; 22 url = "https://github.com/owner/repo"; 23 name = "runner2"; 24 tokenFile = "/secrets/token2"; 25 }; 26 }; 27 default = { }; 28 type = types.attrsOf (types.submodule ({ name, ... }: { 29 options = { 30 enable = mkOption { 31 default = false; 32 example = true; 33 description = '' 34 Whether to enable GitHub Actions runner. 35 36 Note: GitHub recommends using self-hosted runners with private repositories only. Learn more here: 37 [About self-hosted runners](https://docs.github.com/en/actions/hosting-your-own-runners/about-self-hosted-runners). 38 ''; 39 type = types.bool; 40 }; 41 42 url = mkOption { 43 type = types.str; 44 description = '' 45 Repository to add the runner to. 46 47 Changing this option triggers a new runner registration. 48 49 IMPORTANT: If your token is org-wide (not per repository), you need to 50 provide a github org link, not a single repository, so do it like this 51 `https://github.com/nixos`, not like this 52 `https://github.com/nixos/nixpkgs`. 53 Otherwise, you are going to get a `404 NotFound` 54 from `POST https://api.github.com/actions/runner-registration` 55 in the configure script. 56 ''; 57 example = "https://github.com/nixos/nixpkgs"; 58 }; 59 60 tokenFile = mkOption { 61 type = types.path; 62 description = '' 63 The full path to a file which contains either 64 65 * a fine-grained personal access token (PAT), 66 * a classic PAT 67 * or a runner registration token 68 69 Changing this option or the `tokenFile`s content triggers a new runner registration. 70 71 We suggest using the fine-grained PATs. A runner registration token is valid 72 only for 1 hour after creation, so the next time the runner configuration changes 73 this will give you hard-to-debug HTTP 404 errors in the configure step. 74 75 The file should contain exactly one line with the token without any newline. 76 (Use `echo -n 'token' > token file` to make sure no newlines sneak in.) 77 78 If the file contains a PAT, the service creates a new registration token 79 on startup as needed. 80 If a registration token is given, it can be used to re-register a runner of the same 81 name but is time-limited as noted above. 82 83 For fine-grained PATs: 84 85 Give it "Read and Write access to organization/repository self hosted runners", 86 depending on whether it is organization wide or per-repository. You might have to 87 experiment a little, fine-grained PATs are a `beta` Github feature and still subject 88 to change; nonetheless they are the best option at the moment. 89 90 For classic PATs: 91 92 Make sure the PAT has a scope of `admin:org` for organization-wide registrations 93 or a scope of `repo` for a single repository. 94 95 For runner registration tokens: 96 97 Nothing special needs to be done, but updating will break after one hour, 98 so these are not recommended. 99 ''; 100 example = "/run/secrets/github-runner/nixos.token"; 101 }; 102 103 name = mkOption { 104 type = types.nullOr types.str; 105 description = '' 106 Name of the runner to configure. If null, defaults to the hostname. 107 108 Changing this option triggers a new runner registration. 109 ''; 110 example = "nixos"; 111 default = name; 112 }; 113 114 runnerGroup = mkOption { 115 type = types.nullOr types.str; 116 description = '' 117 Name of the runner group to add this runner to (defaults to the default runner group). 118 119 Changing this option triggers a new runner registration. 120 ''; 121 default = null; 122 }; 123 124 extraLabels = mkOption { 125 type = types.listOf types.str; 126 description = '' 127 Extra labels in addition to the default (unless disabled through the `noDefaultLabels` option). 128 129 Changing this option triggers a new runner registration. 130 ''; 131 example = literalExpression ''[ "nixos" ]''; 132 default = [ ]; 133 }; 134 135 noDefaultLabels = mkOption { 136 type = types.bool; 137 description = '' 138 Disables adding the default labels. Also see the `extraLabels` option. 139 140 Changing this option triggers a new runner registration. 141 ''; 142 default = false; 143 }; 144 145 replace = mkOption { 146 type = types.bool; 147 description = '' 148 Replace any existing runner with the same name. 149 150 Without this flag, registering a new runner with the same name fails. 151 ''; 152 default = false; 153 }; 154 155 extraPackages = mkOption { 156 type = types.listOf types.package; 157 description = '' 158 Extra packages to add to `PATH` of the service to make them available to workflows. 159 ''; 160 default = [ ]; 161 }; 162 163 extraEnvironment = mkOption { 164 type = types.attrs; 165 description = '' 166 Extra environment variables to set for the runner, as an attrset. 167 ''; 168 example = { 169 GIT_CONFIG = "/path/to/git/config"; 170 }; 171 default = { }; 172 }; 173 174 serviceOverrides = mkOption { 175 type = types.attrs; 176 description = '' 177 Modify the systemd service. Can be used to, e.g., adjust the sandboxing options. 178 See {manpage}`systemd.exec(5)` for more options. 179 ''; 180 example = { 181 ProtectHome = false; 182 RestrictAddressFamilies = [ "AF_PACKET" ]; 183 }; 184 default = { }; 185 }; 186 187 package = mkPackageOption pkgs "github-runner" { }; 188 189 ephemeral = mkOption { 190 type = types.bool; 191 description = '' 192 If enabled, causes the following behavior: 193 194 - Passes the `--ephemeral` flag to the runner configuration script 195 - De-registers and stops the runner with GitHub after it has processed one job 196 - On stop, systemd wipes the runtime directory (this always happens, even without using the ephemeral option) 197 - Restarts the service after its successful exit 198 - On start, wipes the state directory and configures a new runner 199 200 You should only enable this option if `tokenFile` points to a file which contains a 201 personal access token (PAT). If you're using the option with a registration token, restarting the 202 service will fail as soon as the registration token expired. 203 204 Changing this option triggers a new runner registration. 205 ''; 206 default = false; 207 }; 208 209 user = mkOption { 210 type = types.nullOr types.str; 211 description = '' 212 User under which to run the service. 213 214 If this option and the `group` option is set to `null`, 215 the service runs as a dynamically allocated user. 216 217 Also see the `group` option for an overview on the effects of the `user` and `group` settings. 218 ''; 219 default = null; 220 defaultText = literalExpression "username"; 221 }; 222 223 group = mkOption { 224 type = types.nullOr types.str; 225 description = '' 226 Group under which to run the service. 227 228 The effect of this option depends on the value of the `user` option: 229 230 - `group == null` and `user == null`: 231 The service runs with a dynamically allocated user and group. 232 - `group == null` and `user != null`: 233 The service runs as the given user and its default group. 234 - `group != null` and `user == null`: 235 This configuration is invalid. In this case, the service would use the given group 236 but run as root implicitly. If this is really what you want, set `user = "root"` explicitly. 237 ''; 238 default = null; 239 defaultText = literalExpression "groupname"; 240 }; 241 242 workDir = mkOption { 243 type = with types; nullOr str; 244 description = '' 245 Working directory, available as `$GITHUB_WORKSPACE` during workflow runs 246 and used as a default for [repository checkouts](https://github.com/actions/checkout). 247 The service cleans this directory on every service start. 248 249 A value of `null` will default to the systemd `RuntimeDirectory`. 250 251 Changing this option triggers a new runner registration. 252 ''; 253 default = null; 254 }; 255 256 nodeRuntimes = mkOption { 257 type = with types; nonEmptyListOf (enum [ "node20" ]); 258 default = [ "node20" ]; 259 description = '' 260 List of Node.js runtimes the runner should support. 261 ''; 262 }; 263 }; 264 })); 265 }; 266}