{ config, lib, pkgs, ... }: with lib; let cfg = config.my.services.forgejo; inherit (config.my.server) domain proxyIP firewallInterface; forgejoDomain = "git.${domain}"; forgejoUrl = "https://${forgejoDomain}"; # for nix actions runner storeDeps = pkgs.runCommand "store-deps" {} '' mkdir -p $out/bin for dir in ${ toString [ pkgs.coreutils pkgs.findutils pkgs.gnugrep pkgs.gawk pkgs.git pkgs.nix pkgs.bash pkgs.jq pkgs.nodejs pkgs.devenv ] }; do for bin in "$dir"/bin/*; do ln -s "$bin" "$out/bin/$(basename "$bin")" done done # Add SSL CA certs mkdir -p $out/etc/ssl/certs cp -a "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" $out/etc/ssl/certs/ca-bundle.crt ''; in { options.my.services.forgejo = { enable = mkEnableOption "Forgejo"; proxy = mkEnableOption "Forgejo reverse proxy entry"; actions = mkOption { type = types.submodule (_: { options.enable = mkEnableOption "Forgejo Actions"; }); }; subdomain = mkOption { type = types.str; default = "git"; example = "git"; description = "Subdomain to use for the service."; }; port = mkOption { type = types.port; default = 3000; example = 8080; description = "HTTP port for the Forgejo service."; }; }; config = mkMerge [ (mkIf cfg.enable { age.secrets.forgejoSendgridKey = { file = ../../../secrets/sendgrid-key.age; owner = "forgejo"; group = "forgejo"; }; services.forgejo = { enable = true; package = pkgs.unstable.forgejo; settings.server = { DOMAIN = forgejoDomain; ROOT_URL = forgejoUrl; DISABLE_SSH = true; HTTP_PORT = cfg.port; }; settings.session = { COOKIE_SECURE = true; }; settings.service = { DISABLE_REGISTRATION = true; }; settings.openid = { ENABLE_OPENID_SIGNIN = true; ENABLE_OPENID_SIGNUP = true; }; settings.oauth2_client = { ENABLE_AUTO_REGISTRATION = true; }; settings.mailer = { ENABLED = true; FROM = "forgejo@michaelt.xyz"; PROTOCOL = "starttls"; SMTP_ADDR = "smtp.sendgrid.net"; SMTP_PORT = 587; USER = "apikey"; }; mailerPasswordFile = config.age.secrets.forgejoSendgridKey.path; }; networking.firewall.interfaces."${firewallInterface}".allowedTCPPorts = [cfg.port]; }) (mkIf cfg.actions.enable { # build image // taken from https://git.clan.lol/clan/clan-infra/src/branch/main/modules/web01/gitea/actions-runner.nix # everything here has no dependencies on the store systemd.services.forgejo-runner-nix-image = { wantedBy = ["multi-user.target"]; after = ["podman.service"]; requires = ["podman.service"]; path = [ config.virtualisation.podman.package pkgs.gnutar pkgs.shadow pkgs.getent ]; # we also include etc here because the cleanup job also wants the nixuser to be present script = '' set -eux -o pipefail mkdir -p etc/nix # Create an unpriveleged user that we can use also without the run-as-user.sh script touch etc/passwd etc/group groupid=$(cut -d: -f3 < <(getent group nixuser)) userid=$(cut -d: -f3 < <(getent passwd nixuser)) groupadd --prefix $(pwd) --gid "$groupid" nixuser emptypassword='$6$1ero.LwbisiU.h3D$GGmnmECbPotJoPQ5eoSTD6tTjKnSWZcjHoVTkxFLZP17W9hRi/XkmCiAMOfWruUwy8gMjINrBMNODc7cYEo4K.' useradd --prefix $(pwd) -p "$emptypassword" -m -d /tmp -u "$userid" -g "$groupid" -G nixuser nixuser cat < etc/nix/nix.conf accept-flake-config = true experimental-features = nix-command flakes NIX_CONFIG cat < etc/nsswitch.conf passwd: files mymachines systemd group: files mymachines systemd shadow: files hosts: files mymachines dns myhostname networks: files ethers: files services: files protocols: files rpc: files NSSWITCH # list the content as it will be imported into the container tar -cv . | tar -tvf - tar -cv . | podman import - forgejo-runner-nix ''; serviceConfig = { RuntimeDirectory = "forgejo-runner-nix-image"; WorkingDirectory = "/run/forgejo-runner-nix-image"; Type = "oneshot"; RemainAfterExit = true; }; }; users.users.nixuser = { group = "nixuser"; description = "Used for running nix ci jobs"; home = "/var/empty"; isSystemUser = true; }; users.groups.nixuser = {}; # configure the actions runner itself age.secrets.forgejoActions.file = ../../../secrets/forgejo-actions.age; services.gitea-actions-runner = mkIf cfg.actions.enable { package = pkgs.unstable.forgejo-runner; instances.venus = { enable = true; name = "venus-nix-runner"; url = forgejoUrl; settings = { # options = "-v /var/run/podman/podman.sock:/var/run/podman/podman.sock"; runner = { capacity = 5; timeout = "45m"; }; container = { options = "-e NIX_BUILD_SHELL=/bin/bash -e PAGER=cat -e PATH=/bin -e SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt --device /dev/kvm -v /nix:/nix -v ${storeDeps}/bin:/bin -v ${storeDeps}/etc/ssl:/etc/ssl --user nixuser --device=/dev/kvm"; # privileged = true; valid_volumes = [ "/nix" "${storeDeps}/bin" "${storeDeps}/etc/ssl" ]; # force_pull = false; network = "bridge"; }; }; labels = [ "nix:docker://forgejo-runner-nix" ]; tokenFile = config.age.secrets.forgejoActions.path; }; }; }) (mkIf cfg.proxy { services.caddy.virtualHosts."${forgejoDomain}".extraConfig = '' handle_errors { respond "This server is currently unavailable." 502 } redir /user/login /user/oauth2/Keycloak?{query} reverse_proxy http://${proxyIP}:${toString cfg.port} ''; webapps.dashboardCategories = [ { name = "Git"; tag = "git"; } ]; webapps.apps.forgejo.dashboard = { name = "Forgejo"; category = "git"; icon = "git-alt"; url = forgejoUrl; description = "Beyond coding. We forge."; }; }) ]; }