diff --git a/home/modules/git.nix b/home/modules/git.nix index e1df58d..8bb357e 100644 --- a/home/modules/git.nix +++ b/home/modules/git.nix @@ -6,7 +6,7 @@ }: { programs.git = { enable = true; - userName = "xqtc161"; + userName = "xqtc"; userEmail = "xqtc@tutanota.com"; extraConfig = { init = { diff --git a/home/modules/nixvim.nix b/home/modules/nixvim.nix index cf6ff72..33ab7a5 100644 --- a/home/modules/nixvim.nix +++ b/home/modules/nixvim.nix @@ -47,13 +47,13 @@ inlayHints = { closureStyle = "rust_analyzer"; }; - typing.autoClosingAngleBrackets.enable = true; + typing.autoClosingAngleBrackets.enable = true; }; }; }; lsp = { enable = true; - inlayHints = true; + inlayHints = true; servers = { typst-lsp.enable = true; typos-lsp.enable = true; diff --git a/hosts/x86_64-linux/beleth/git.nix b/hosts/x86_64-linux/beleth/git.nix index 4da35ac..7e9a5b8 100644 --- a/hosts/x86_64-linux/beleth/git.nix +++ b/hosts/x86_64-linux/beleth/git.nix @@ -1,8 +1,19 @@ { lib, + config, inputs, + pkgs, ... -}: { +}: let + gitea-actions-runner-custom = import ./services/gitea-runner.nix; +in { + imports = [ gitea-actions-runner-custom ]; + # config = { services.gitea-actions-runner = gitea-actions-runner-custom.config.services.gitea-actions-runner; }; + sops.secrets.forgejo_runner_token = { + owner = "forgejo"; + group = "forgejo"; + mode = "0444"; + }; services.forgejo = { enable = true; settings.server = { @@ -12,11 +23,75 @@ settings = { service = { DISABLE_REGISTRATION = true; - REQUIRE_SIGNIN_VIEW = true; + # REQUIRE_SIGNIN_VIEW = true; }; DEFAULT = { APP_NAME = "Git so good it could be heroin"; + APP_DESC = "For an account, contact me via Mastodon"; }; }; }; + services.gitea-actions-runner-custom = { + package = pkgs.forgejo-actions-runner; + instances.default = { + enable = true; + name = "monolith"; + url = "https://git.heroin.trade"; + token = ""; + labels = [ + "ubuntu-latest:docker://node:16-bullseye" + "ubuntu-22.04:docker://node:16-bullseye" + "ubuntu-20.04:docker://node:16-bullseye" + "ubuntu-18.04:docker://node:16-buster" + "native:host" + ]; + hostPackages = with pkgs; [ + bash + coreutils + curl + gawk + gitMinimal + gnused + nodejs + wget + ]; + }; + }; + # systemd.services.gitea-runner-default = { + # preStart = lib.mkForce '' + # export INSTANCE_DIR="$STATE_DIRECTORY/default" + # export TOKEN="$(cat /run/secrets/forgejo_runner_token)" + # mkdir -vp "$INSTANCE_DIR" + # cd "$INSTANCE_DIR" + # + # # force reregistration on changed labels + # export LABELS_FILE="$INSTANCE_DIR/.labels" + # # export LABELS_WANTED="$(echo 'ubuntu-latest:docker://node:16-bullseye + # # ubuntu-22.04:docker://node:16-bullseye + # # ubuntu-20.04:docker://node:16-bullseye + # # ubuntu-18.04:docker://node:16-buster' | sort)" + # export LABELS_WANTED="$(echo ${lib.strings.concatStrings config.services.gitea-actions-runner.instances.default.labels} | sort)" + # export LABELS_CURRENT="$(cat $LABELS_FILE 2>/dev/null || echo 0)" + # + # if [ ! -e "$INSTANCE_DIR/.runner" ] || [ "$LABELS_WANTED" != "$LABELS_CURRENT" ]; then + # # remove existing registration file, so that changing the labels forces a re-registration + # rm -v "$INSTANCE_DIR/.runner" || true + # + # # perform the registration + # /nix/store/z3v4091wwdhdb60m72k0pxkim8q7chrk-forgejo-runner-3.5.0/bin/act_runner register --no-interactive \ + # --instance 'https://git.heroin.trade' \ + # --token "$TOKEN" \ + # --name 'monolith' \ + # --labels 'ubuntu-latest:docker://node:16-bullseye,ubuntu-22.04:docker://node:16-bullseye,ubuntu-20.04:docker://node:16-bullseye,ubuntu-18.04:docker://node:16-buster' \ + # --config /nix/store/99q15qaajdi7rqz4bxbmhpkch2v8wg4f-config.yaml + # + # # and write back the configured labels + # echo "$LABELS_WANTED" > "$LABELS_FILE" + # fi + # ''; + # serviceConfig.DynamicUser = lib.mkForce false; + # serviceConfig.User = lib.mkForce "forgejo"; + # serviceConfig.Group = lib.mkForce "forgejo"; + # }; + environment.systemPackages = with pkgs; [forgejo-runner]; } diff --git a/hosts/x86_64-linux/beleth/services/gitea-runner.nix b/hosts/x86_64-linux/beleth/services/gitea-runner.nix new file mode 100644 index 0000000..e433d09 --- /dev/null +++ b/hosts/x86_64-linux/beleth/services/gitea-runner.nix @@ -0,0 +1,262 @@ +{ config +, lib +, pkgs +, utils +, ... +}: + +let + inherit (lib) + any + attrValues + concatStringsSep + escapeShellArg + hasInfix + hasSuffix + optionalAttrs + optionals + literalExpression + mapAttrs' + mkEnableOption + mkOption + mkPackageOption + mkIf + nameValuePair + types + ; + + inherit (utils) + escapeSystemdPath + ; + + cfg = config.services.gitea-actions-runner-custom; + + settingsFormat = pkgs.formats.yaml { }; + + # Check whether any runner instance label requires a container runtime + # Empty label strings result in the upstream defined defaultLabels, which require docker + # https://gitea.com/gitea/act_runner/src/tag/v0.1.5/internal/app/cmd/register.go#L93-L98 + hasDockerScheme = instance: + instance.labels == [] || any (label: hasInfix ":docker:" label) instance.labels; + wantsContainerRuntime = any hasDockerScheme (attrValues cfg.instances); + + hasHostScheme = instance: any (label: hasSuffix ":host" label) instance.labels; + + # provide shorthands for whether container runtimes are enabled + hasDocker = config.virtualisation.docker.enable; + hasPodman = config.virtualisation.podman.enable; + + tokenXorTokenFile = instance: + (instance.token == null && instance.tokenFile != null) || + (instance.token != null && instance.tokenFile == null); +in +{ + meta.maintainers = with lib.maintainers; [ + hexa + ]; + + options.services.gitea-actions-runner-custom = with types; { + package = mkPackageOption pkgs "gitea-actions-runner-custom" { }; + + instances = mkOption { + default = {}; + description = '' + Gitea Actions Runner instances. + ''; + type = attrsOf (submodule { + options = { + enable = mkEnableOption "Gitea Actions Runner instance"; + + name = mkOption { + type = str; + example = literalExpression "config.networking.hostName"; + description = '' + The name identifying the runner instance towards the Gitea/Forgejo instance. + ''; + }; + + url = mkOption { + type = str; + example = "https://forge.example.com"; + description = '' + Base URL of your Gitea/Forgejo instance. + ''; + }; + + token = mkOption { + type = nullOr str; + default = null; + description = '' + Plain token to register at the configured Gitea/Forgejo instance. + ''; + }; + + tokenFile = mkOption { + type = nullOr (either str path); + default = null; + description = '' + Path to an environment file, containing the `TOKEN` environment + variable, that holds a token to register at the configured + Gitea/Forgejo instance. + ''; + }; + + labels = mkOption { + type = listOf str; + example = literalExpression '' + [ + # provide a debian base with nodejs for actions + "debian-latest:docker://node:18-bullseye" + # fake the ubuntu name, because node provides no ubuntu builds + "ubuntu-latest:docker://node:18-bullseye" + # provide native execution on the host + #"native:host" + ] + ''; + description = '' + Labels used to map jobs to their runtime environment. Changing these + labels currently requires a new registration token. + + Many common actions require bash, git and nodejs, as well as a filesystem + that follows the filesystem hierarchy standard. + ''; + }; + settings = mkOption { + description = '' + Configuration for `act_runner daemon`. + See https://gitea.com/gitea/act_runner/src/branch/main/internal/pkg/config/config.example.yaml for an example configuration + ''; + + type = types.submodule { + freeformType = settingsFormat.type; + }; + + default = { }; + }; + + hostPackages = mkOption { + type = listOf package; + default = with pkgs; [ + bash + coreutils + curl + gawk + gitMinimal + gnused + nodejs + wget + ]; + defaultText = literalExpression '' + with pkgs; [ + bash + coreutils + curl + gawk + gitMinimal + gnused + nodejs + wget + ] + ''; + description = '' + List of packages, that are available to actions, when the runner is configured + with a host execution label. + ''; + }; + }; + }); + }; + }; + + config = mkIf (cfg.instances != {}) { + assertions = [ { + assertion = any tokenXorTokenFile (attrValues cfg.instances); + message = "Instances of gitea-actions-runner-custom can have `token` or `tokenFile`, not both."; + } { + assertion = wantsContainerRuntime -> hasDocker || hasPodman; + message = "Label configuration on gitea-actions-runner-custom instance requires either docker or podman."; + } ]; + + systemd.services = let + mkRunnerService = name: instance: let + wantsContainerRuntime = hasDockerScheme instance; + wantsHost = hasHostScheme instance; + wantsDocker = wantsContainerRuntime && config.virtualisation.docker.enable; + wantsPodman = wantsContainerRuntime && config.virtualisation.podman.enable; + configFile = settingsFormat.generate "config.yaml" instance.settings; + in + nameValuePair "gitea-runner-${escapeSystemdPath name}" { + inherit (instance) enable; + description = "Gitea Actions Runner"; + wants = [ "network-online.target" ]; + after = [ + "network-online.target" + ] ++ optionals (wantsDocker) [ + "docker.service" + ] ++ optionals (wantsPodman) [ + "podman.service" + ]; + wantedBy = [ + "multi-user.target" + ]; + environment = optionalAttrs (instance.token != null) { + TOKEN = "${instance.token}"; + } // optionalAttrs (wantsPodman) { + DOCKER_HOST = "unix:///run/podman/podman.sock"; + } // { + HOME = "/var/lib/gitea-runner/${name}"; + }; + path = with pkgs; [ + coreutils + ] ++ lib.optionals wantsHost instance.hostPackages; + serviceConfig = { + DynamicUser = true; + User = "gitea-runner"; + StateDirectory = "gitea-runner"; + WorkingDirectory = "-/var/lib/gitea-runner/${name}"; + + # gitea-runner might fail when gitea is restarted during upgrade. + Restart = "on-failure"; + RestartSec = 2; + + ExecStartPre = [(pkgs.writeShellScript "gitea-register-runner-${name}" '' + export INSTANCE_DIR="$STATE_DIRECTORY/${name}" + export TOKEN=$(cat /run/secrets/forgejo_runner_token) + mkdir -vp "$INSTANCE_DIR" + cd "$INSTANCE_DIR" + + # force reregistration on changed labels + export LABELS_FILE="$INSTANCE_DIR/.labels" + export LABELS_WANTED="$(echo ${escapeShellArg (concatStringsSep "\n" instance.labels)} | sort)" + export LABELS_CURRENT="$(cat $LABELS_FILE 2>/dev/null || echo 0)" + + if [ ! -e "$INSTANCE_DIR/.runner" ] || [ "$LABELS_WANTED" != "$LABELS_CURRENT" ]; then + # remove existing registration file, so that changing the labels forces a re-registration + rm -v "$INSTANCE_DIR/.runner" || true + + # perform the registration + ${cfg.package}/bin/act_runner register --no-interactive \ + --instance ${escapeShellArg instance.url} \ + --token "$TOKEN" \ + --name ${escapeShellArg instance.name} \ + --labels ${escapeShellArg (concatStringsSep "," instance.labels)} \ + --config ${configFile} + + # and write back the configured labels + echo "$LABELS_WANTED" > "$LABELS_FILE" + fi + + '')]; + ExecStart = "${cfg.package}/bin/act_runner daemon --config ${configFile}"; + SupplementaryGroups = optionals (wantsDocker) [ + "docker" + ] ++ optionals (wantsPodman) [ + "podman" + ]; + } // optionalAttrs (instance.tokenFile != null) { + EnvironmentFile = instance.tokenFile; + }; + }; + in mapAttrs' mkRunnerService cfg.instances; + }; +} diff --git a/secrets.yaml b/secrets.yaml index 27cac60..d0dddb8 100644 --- a/secrets.yaml +++ b/secrets.yaml @@ -5,6 +5,7 @@ public_keys: nextcloud_password: ENC[AES256_GCM,data:lwqQio1I1xTv07bLRyrvig1HRyCxcueSPgDpPRhXBqCi8d42OJt7rA==,iv:R0JxpCJz9zycph9p7Ewwt4QTEXQxaxJ691aWCXfEsFE=,tag:Qz3dD2cOkmneEWP7tI54Dg==,type:str] nextcloud_user_password: ENC[AES256_GCM,data:fkX/1pOgRLvhHTtoK9i5F0kO+mRKj40BH2s7VD7ifPEnyJhWqy5mvg==,iv:iEnW4Z8vCY9oapOpVZNuLMa50SXT01clYaScUN+q/k8=,tag:0G0Y8XCSj+dBAy6Cw8YOHg==,type:str] paperless_password: ENC[AES256_GCM,data:OCrc00vUb+lgel8TmFm+9Ee4QJZZV7W6+Jl9+R7AfjfDh6v590ibvw==,iv:emM7g0JRcEH4xuYdvZN64drOhduXyQy6HwF1xByaLvE=,tag:D2O1qAeKtYWGf+Zd3RuBTQ==,type:str] +forgejo_runner_token: ENC[AES256_GCM,data:9JgYl6xQX6Cn5hLTDBw5R87k9+DRwPkpiQn8zQGlz/Zji+KIFxBGkw==,iv:TGohedGU5lLxuMk7MYxrfwiwQl0p6Ifj84d17j8DO2Q=,tag:dsrVA6+9raCcGTn9ty16hw==,type:str] #ENC[AES256_GCM,data:UmGDAz/qalmP6Z2r4VSH802m9ddAoCQ8IaAtAmyQV+Psg0rNpLF7du5ykDepTyHAb6YAG1k2k9ziqfV8P9SUNMvLC7D3TmFk9oZtW8HLrN84tcbp9i4HDFfQ+Q==,iv:PzHlSFj0H4/eSab9j3y9lYAFUPMEb+G74M2/2cmv70A=,tag:GFstk6NgtJCNm207zO1fjQ==,type:comment] sops: kms: [] @@ -57,8 +58,8 @@ sops: a2ZHZmQ0dEVMZFlJRENmU2lGejFuMzAKOO3kTP/VWRYn5CrwPyjUIGS7kjxPvNYZ HMt+cCG9FYeJdqPpHWiM0TeHYUG0h7XlltIMg7KhE4Qj/GARegmuZQ== -----END AGE ENCRYPTED FILE----- - lastmodified: "2024-07-06T14:20:40Z" - mac: ENC[AES256_GCM,data:tiYyfsKlYF4j5YqIezO34L8nkGmHTWQeR7y6e3M2PFFujCqw87Q/WdAdKiyErvPmPwMbkyfXQZgIs2fAKx/C6t5lh5TFFQYZyvCV9A17y4vn8f5SK1HFDePQTfaCqQ4IlSXAXZYpBMMFJn+WBVJQUCb3xM5meuVYTTZpqE8dmso=,iv:NwLsBzVa/Kf9YS6lfS+4VpWkYxpqxPEbRHquuNJ0klY=,tag:LgsV5KBk0Dwij4jEbB99xA==,type:str] + lastmodified: "2024-07-11T11:34:32Z" + mac: ENC[AES256_GCM,data:R5jjjx7iG4wpbn+zUVUBNzUmSS8BESat33ypEWyIi3jQeu7qCJ+OOM+0Soa4AiK3AwS7k1JPfWN8qvcCvDvxeOcHRciqKvORjulfFCceq4RIpEtkcPQU9PQGpMoDg5fYpS8WK8l3qRRPrUYZvKSo1/9O+r0emxF3CYSaR5wOSOs=,iv:673UNciuctMnkEUfrOnGuX2nSQQP/OO1x8QGKaC3noc=,tag:lBnMJwZk4oO8ArAxXXf9Zg==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.8.1