···
32
+
cfg = config.services.gitea-actions-runner;
34
+
# Check whether any runner instance label requires a container runtime
35
+
# Empty label strings result in the upstream defined defaultLabels, which require docker
36
+
# https://gitea.com/gitea/act_runner/src/tag/v0.1.5/internal/app/cmd/register.go#L93-L98
37
+
hasDockerScheme = instance:
38
+
instance.labels == [] || any (label: hasInfix ":docker:" label) instance.labels;
39
+
wantsContainerRuntime = any hasDockerScheme (attrValues cfg.instances);
41
+
hasHostScheme = instance: any (label: hasSuffix ":host" label) instance.labels;
43
+
# provide shorthands for whether container runtimes are enabled
44
+
hasDocker = config.virtualisation.docker.enable;
45
+
hasPodman = config.virtualisation.podman.enable;
47
+
tokenXorTokenFile = instance:
48
+
(instance.token == null && instance.tokenFile != null) ||
49
+
(instance.token != null && instance.tokenFile == null);
52
+
meta.maintainers = with lib.maintainers; [
56
+
options.services.gitea-actions-runner = with types; {
57
+
package = mkPackageOptionMD pkgs "gitea-actions-runner" { };
59
+
instances = mkOption {
61
+
description = lib.mdDoc ''
62
+
Gitea Actions Runner instances.
64
+
type = attrsOf (submodule {
66
+
enable = mkEnableOption (lib.mdDoc "Gitea Actions Runner instance");
70
+
example = literalExpression "config.networking.hostName";
71
+
description = lib.mdDoc ''
72
+
The name identifying the runner instance towards the Gitea/Forgejo instance.
78
+
example = "https://forge.example.com";
79
+
description = lib.mdDoc ''
80
+
Base URL of your Gitea/Forgejo instance.
87
+
description = lib.mdDoc ''
88
+
Plain token to register at the configured Gitea/Forgejo instance.
92
+
tokenFile = mkOption {
93
+
type = nullOr (either str path);
95
+
description = lib.mdDoc ''
96
+
Path to an environment file, containing the `TOKEN` environment
97
+
variable, that holds a token to register at the configured
98
+
Gitea/Forgejo instance.
102
+
labels = mkOption {
104
+
example = literalExpression ''
106
+
# provide a debian base with nodejs for actions
107
+
"debian-latest:docker://node:18-bullseye"
108
+
# fake the ubuntu name, because node provides no ubuntu builds
109
+
"ubuntu-latest:docker://node:18-bullseye"
110
+
# provide native execution on the host
114
+
description = lib.mdDoc ''
115
+
Labels used to map jobs to their runtime environment. Changing these
116
+
labels currently requires a new registration token.
118
+
Many common actions require bash, git and nodejs, as well as a filesystem
119
+
that follows the filesystem hierarchy standard.
123
+
hostPackages = mkOption {
124
+
type = listOf package;
125
+
default = with pkgs; [
135
+
defaultText = literalExpression ''
147
+
description = lib.mdDoc ''
148
+
List of packages, that are available to actions, when the runner is configured
149
+
with a host execution label.
157
+
config = mkIf (cfg.instances != {}) {
159
+
assertion = any tokenXorTokenFile (attrValues cfg.instances);
160
+
message = "Instances of gitea-actions-runner can have `token` or `tokenFile`, not both.";
162
+
assertion = wantsContainerRuntime -> hasDocker || hasPodman;
163
+
message = "Label configuration on gitea-actions-runner instance requires either docker or podman.";
166
+
systemd.services = let
167
+
mkRunnerService = name: instance: let
168
+
wantsContainerRuntime = hasDockerScheme instance;
169
+
wantsHost = hasHostScheme instance;
170
+
wantsDocker = wantsContainerRuntime && config.virtualisation.docker.enable;
171
+
wantsPodman = wantsContainerRuntime && config.virtualisation.podman.enable;
173
+
nameValuePair "gitea-runner-${escapeSystemdPath name}" {
174
+
inherit (instance) enable;
175
+
description = "Gitea Actions Runner";
177
+
"network-online.target"
178
+
] ++ optionals (wantsDocker) [
180
+
] ++ optionals (wantsPodman) [
184
+
"multi-user.target"
186
+
environment = optionalAttrs (instance.token != null) {
187
+
TOKEN = "${instance.token}";
188
+
} // optionalAttrs (wantsPodman) {
189
+
DOCKER_HOST = "unix:///run/podman/podman.sock";
191
+
path = with pkgs; [
193
+
] ++ lib.optionals wantsHost instance.hostPackages;
195
+
DynamicUser = true;
196
+
User = "gitea-runner";
197
+
StateDirectory = "gitea-runner";
198
+
WorkingDirectory = "-/var/lib/gitea-runner/${name}";
199
+
ExecStartPre = pkgs.writeShellScript "gitea-register-runner-${name}" ''
200
+
export INSTANCE_DIR="$STATE_DIRECTORY/${name}"
201
+
mkdir -vp "$INSTANCE_DIR"
204
+
# force reregistration on changed labels
205
+
export LABELS_FILE="$INSTANCE_DIR/.labels"
206
+
export LABELS_WANTED="$(echo ${escapeShellArg (concatStringsSep "\n" instance.labels)} | sort)"
207
+
export LABELS_CURRENT="$(cat $LABELS_FILE 2>/dev/null || echo 0)"
209
+
if [ ! -e "$INSTANCE_DIR/.runner" ] || [ "$LABELS_WANTED" != "$LABELS_CURRENT" ]; then
210
+
# remove existing registration file, so that changing the labels forces a re-registation
211
+
rm -v "$INSTANCE_DIR/.runner" || true
213
+
# perform the registration
214
+
${cfg.package}/bin/act_runner register --no-interactive \
215
+
--instance ${escapeShellArg instance.url} \
217
+
--name ${escapeShellArg instance.name} \
218
+
--labels ${escapeShellArg (concatStringsSep "," instance.labels)}
220
+
# and write back the configured labels
221
+
echo "$LABELS_WANTED" > "$LABELS_FILE"
225
+
ExecStart = "${cfg.package}/bin/act_runner daemon";
226
+
SupplementaryGroups = optionals (wantsDocker) [
228
+
] ++ optionals (wantsPodman) [
231
+
} // optionalAttrs (instance.tokenFile != null) {
232
+
EnvironmentFile = instance.tokenFile;
235
+
in mapAttrs' mkRunnerService cfg.instances;