From 11e7b7414004913913e12512995f225690890516 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Mon, 26 Jan 2026 23:34:52 +0100 Subject: [PATCH 1/3] fix: git runner memory increase --- hosts/fw/modules/gitea-vm.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosts/fw/modules/gitea-vm.nix b/hosts/fw/modules/gitea-vm.nix index 8c47651..c3afdea 100644 --- a/hosts/fw/modules/gitea-vm.nix +++ b/hosts/fw/modules/gitea-vm.nix @@ -7,7 +7,7 @@ in { microvm.vms = lib.mapAttrs (runner: idx: { config = { microvm = { - mem = 4048; + mem = 8096; shares = [ { source = "/nix/store"; From 9d7b8082c086018371c0b4d89eb939c310b93e5b Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Mon, 26 Jan 2026 23:35:07 +0100 Subject: [PATCH 2/3] feat: add initial forgejo --- hosts/fw/configuration.nix | 1 + hosts/fw/modules/forgejo.nix | 145 +++++++++++++++++++++++++++++++++ hosts/fw/modules/staticids.nix | 2 + hosts/fw/secrets.yaml | 99 +++++++++++----------- 4 files changed, 198 insertions(+), 49 deletions(-) create mode 100644 hosts/fw/modules/forgejo.nix diff --git a/hosts/fw/configuration.nix b/hosts/fw/configuration.nix index 493d50f..a2661ff 100644 --- a/hosts/fw/configuration.nix +++ b/hosts/fw/configuration.nix @@ -43,6 +43,7 @@ # git ./modules/gitea.nix + ./modules/forgejo.nix # Migration: autoStart=false, start after migration script # ./modules/fwmetrics.nix # ha customers diff --git a/hosts/fw/modules/forgejo.nix b/hosts/fw/modules/forgejo.nix new file mode 100644 index 0000000..ec6a237 --- /dev/null +++ b/hosts/fw/modules/forgejo.nix @@ -0,0 +1,145 @@ +{ config, pkgs, ... }: +let + cids = import ../modules/staticids.nix; + domain = "git.cloonar.com"; + networkPrefix = config.networkPrefix; + + user = { + isSystemUser = true; + uid = cids.uids.forgejo; + group = "forgejo"; + home = "/var/lib/forgejo"; + createHome = true; + }; + group = { + gid = cids.gids.forgejo; + }; +in +{ + users.users.forgejo = user; + users.groups.forgejo = group; + + # Reuse the existing git.cloonar.com ACME cert from gitea.nix + + containers.forgejo = { + autoStart = false; # Don't start until migration is complete + ephemeral = false; # because of ssh key + privateNetwork = true; + hostBridge = "server"; + hostAddress = "${networkPrefix}.97.1"; + localAddress = "${networkPrefix}.97.51/24"; # Different from gitea's .50 + bindMounts = { + "/var/lib/forgejo" = { + hostPath = "/var/lib/forgejo/"; + isReadOnly = false; + }; + "/var/lib/acme/forgejo/" = { + hostPath = config.security.acme.certs.${domain}.directory; + isReadOnly = true; + }; + "/run/secrets/forgejo-mailer-password" = { + hostPath = config.sops.secrets.forgejo-mailer-password.path; + }; + }; + config = { lib, config, pkgs, ... }: { + imports = [ + ../fleet.nix + ../modules/cloonar-assistant-config-server.nix + ]; + + environment.systemPackages = with pkgs; [ + vim # my preferred editor + ]; + + networking = { + hostName = "forgejo"; + useHostResolvConf = false; + defaultGateway = { + address = "${networkPrefix}.96.1"; + interface = "eth0"; + }; + firewall.enable = false; + nameservers = [ "${networkPrefix}.97.1" ]; + }; + + services.nginx.enable = true; + services.nginx.virtualHosts."${domain}" = { + sslCertificate = "/var/lib/acme/forgejo/fullchain.pem"; + sslCertificateKey = "/var/lib/acme/forgejo/key.pem"; + sslTrustedCertificate = "/var/lib/acme/forgejo/chain.pem"; + forceSSL = true; + extraConfig = '' + client_max_body_size 2048M; + ''; + locations."/" = { + proxyPass = "http://localhost:3001/"; + }; + }; + + services.forgejo = { + enable = true; + stateDir = "/var/lib/forgejo"; + settings = { + DEFAULT = { + APP_NAME = "Cloonar Forgejo server"; + }; + server = { + ROOT_URL = "https://${domain}/"; + HTTP_PORT = 3001; + DOMAIN = domain; + }; + repository = { + DEFAULT_BRANCH = "main"; + }; + openid = { + ENABLE_OPENID_SIGNIN = false; + ENABLE_OPENID_SIGNUP = true; + WHITELISTED_URIS = "auth.cloonar.com"; + }; + service = { + DISABLE_REGISTRATION = false; + ALLOW_ONLY_EXTERNAL_REGISTRATION = true; + SHOW_REGISTRATION_BUTTON = false; + ENABLE_NOTIFY_MAIL = true; + REQUIRE_SIGNIN_VIEW = false; + }; + mailer = { + ENABLED = true; + FROM = "Forgejo Cloonar "; + PROTOCOL = "smtp+starttls"; + SMTP_ADDR = "mail.cloonar.com"; + SMTP_PORT = 587; + USER = "gitea@cloonar.com"; + }; + actions.ENABLED=true; + attachment = { + MAX_SIZE = 2048; # 2GB in MB for general attachments + }; + packages = { + ENABLED = true; + }; + }; + }; + + # Configure mailer password + systemd.services.forgejo.serviceConfig.EnvironmentFile = "/run/secrets/forgejo-mailer-password"; + + services.openssh.enable = true; + users.users.root.openssh.authorizedKeys.keys = [ + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius" + ]; + + users.users.forgejo = user; + users.groups.forgejo = group; + + system.stateVersion = "23.05"; + }; + }; + + sops.secrets.forgejo-mailer-password = { + owner = "forgejo"; + # restartUnits removed - would start the container even with autoStart=false + # Re-add after migration: restartUnits = [ "container@forgejo.service" ]; + }; +} diff --git a/hosts/fw/modules/staticids.nix b/hosts/fw/modules/staticids.nix index d382370..90b3973 100644 --- a/hosts/fw/modules/staticids.nix +++ b/hosts/fw/modules/staticids.nix @@ -8,6 +8,7 @@ pyload = 10006; jellyfin = 10007; filebot = 10008; + forgejo = 10009; }; gids = { unbound = 10001; @@ -18,5 +19,6 @@ pyload = 10006; jellyfin = 10007; filebot = 10008; + forgejo = 10009; }; } diff --git a/hosts/fw/secrets.yaml b/hosts/fw/secrets.yaml index e0da8b6..a15e913 100644 --- a/hosts/fw/secrets.yaml +++ b/hosts/fw/secrets.yaml @@ -1,69 +1,70 @@ -ai-mailer-imap-password: ENC[AES256_GCM,data:q9eJ9Tom+X6KxQJhWQTUB61k5A==,iv:FH+IUWi2yZBBgMiL/kNW470GEVHEG3fImf0bel9og/c=,tag:RSlcpXwmNyLB8Oc/K2Epvw==,type:str] -ai-mailer-openrouter-key: ENC[AES256_GCM,data:EvI0BuCBA1uYOderjAVcB8RSk7un7tiKmgsSe70KQcmfu3CxmQerP/2kQsRTJ0/6pWf4QqNpaes691O3nf+UG1qgG2CUcIaYRQ==,iv:OYEy0xMs+vkGa0qMtY4UP/iol5JPQ0eFVyPpPXLAmUE=,tag:5PeXZcI8TRSUOyuKs0STWg==,type:str] -borg-passphrase: ENC[AES256_GCM,data:GGmf09zX5wQ8Fih1EyP1p3up9ckFjVKsktU6ZFwvuZnG/O2OyOod66qXc/IXx8GQordubZ3TgisOeMLNnSowp2qylh8=,iv:fFgw/x8Ww9cInkNlPIoE3stUfISbfk46PBj7aimuXNA=,tag:hnNYrkLgt1qJc+gN5s9L2Q==,type:str] -borg-ssh-key: ENC[AES256_GCM,data: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,iv:f7rBK8aNqX8dGyzjoeRX6yl20XsnLU8b4gitaw9+O+0=,tag:WvfUw1JgFBAtS3vsVIvM6Q==,type:str] -ddclient: ENC[AES256_GCM,data:dS6TVVNb6R7EE1JVMDfSnRYCZyHHqEPvwaYpkTSj+VA=,iv:9uMo+9X7dFdVW4wuSgrqIAaQelXuA4cek2oif0GRHow=,tag:ncQq4UeUzWtjPNxEUOlqNA==,type:str] -filebot-license: ENC[AES256_GCM,data:jY7E29fFJ/h9NIgIjuX++WBhnLk6Mm4iRfMh4P0pUDdqH231gXDsTZ6pJ1rpFXdEHSuNN4LfznDTKgZ2azKid4WprDUzGkN0uJD6CfSR8gTIx5Rq0M8vkRah51LC36bop4hTMzECYQd1YA47hOBV/gfyg3RIw95coWamV9FebnQjIBgWYxE+wTvO5iRvWpiCHd6VZQfkiiR0KF1DrkYkuxlX0piGEKmIgyYCiKMFZ4nrrIe58x5lEQA9uPVjE7vmq3c3ge6tJzjVVaaNocbJhxhA18GLMqTSHfnBsOLRlA8qSQ3xX/VRzKQmaYQHIM77Ylb9ZQsvFt6EDlzQMl5NqT7OJZUW/0jwNaEXHURjeTOC3Hr1HugiDGm+uLXEraaJ6Na2AbFDn28o+3J22p9xNg6vWL0FElzKuaz5TFDzdZLZsD9HOPQm95/ZM8JymDjN4qxkkd2o9rEKY6to1MVDarj+lDxIHhf4pL23YhZsn3esNlEbFswzHQiH7nMsu9Jg6a0rPu7IYylDnH/soBjxSKmf2dhH1LLsDm8It9K/7NnXwmvncFXaqNBqm/e7JzCDBCCyVUf/BXbBc3xwLwZf5MiirZ/iYiYnRtUssveh7BV7ICigRj5Ewtr+n97+IGI+FyonkvOgM0bn8nHf79ZzJCKMuntcw3FlGd1nIkmcehkC79PlKIS95oV/wypl1OmU0CVel+D8hsMuONmF9NPHgFk/ztp4GF+XXRO4ExNotX1XrUlvLOccoHDsl1TedUOISzgAK71edxfI8y110shIe9OfsCEUAbmWMmjGVWH2fKu/IrYYQTry6pYFOjG2bIEUXMaiIP0lALbq/QNgleqMNPY8wGzFbP+/jaYzTbw9KXH4bwYQCl1hSI8THfV7lLE=,iv:4ik/aQqi/hIqH8ix3ejgUiXGY7ycw0ymdVrV+CEQe1o=,tag:7ymc4QZEezJVPlYTlU4H/g==,type:str] -gitea-mailer-password: ENC[AES256_GCM,data:lEv5euTCHG6pyNqrVtKK7oE8wLvk+q8ABXOzFSizQ2TVFi35lyGPzOTel/dCCC0Je5GAHE1KQQ4Y4/iHghZgb5Ft,iv:gt/mCzLbDrHFNqW+Lkd2dy9nRIBKO+rqsVuXM45zJ8k=,tag:gCxTSzY7GZ+jQP9SCsdUtw==,type:str] -gitea-runner: ENC[AES256_GCM,data:HLjSETmu2C2ROf6kqUuIzQl/t4Fe5EOVkMqdTeLNnb6AJ95l6M/WUk//dnPMrWVvEq7rV07awUiyvyJcYQzMgPNddCrfcn2Xr0dYK4XFenz/sdhknVex9uS/RhK8fOqdYJ6djpynikMKddZMQr9AOVfpF5mea//87+Az9rOrlzLdgNtf5HyBEAFKaOFbkZboAsP+jlxyyYurGHPr8LxxikewDVxnpB+XzMc6RAnesrZPOTDQlkMiPZ2t2o0klhD/4VomgiHEklULxCCmIAHaqDo=,iv:1FwTespqVTnKFbyf9Unbbod08D36MKsVbDhIBNGBkHg=,tag:rgVvyxUCwzYB2CqWm2fwgg==,type:str] -gitea-runner-token: ENC[AES256_GCM,data:pzJp7j1Ktz+27oU+qtESk7D32w7+BSEUkPSX4xuFml0i10z12Gzu0QHXL9s3734=,iv:U77b5515H1URfz5BCdzuY03zVkhSRsL9d+HdHUJFx9U=,tag:QvooaT4TS/X5R5KGdaVpVQ==,type:str] -home-assistant-ldap: ENC[AES256_GCM,data:4kofJzPbiLXILxjuAZWiTb9hu2Gver/IHBCXDnrmrKuCSII6SJ9FrSi67nl7SHdoA6xe22GSMfmPrKzy5sGiow==,iv:F8mIHhWHpaI6kzRV9du6uW/Fj07PbEIU1goSDmeSD5E=,tag:6NIC6sN8OclinribZhrLLw==,type:str] -home-assistant-secrets.yaml: ENC[AES256_GCM,data:rns9heAmVMxB6WWlGMXvF/ianFUnja3FObiLTEKJmodePNsJ8ah3OhuCAX5jON+/7NZ+3JN/hIJjXsORC5WYhr01DvO9meykf0aMpbmAnYI+cmPEPvcunF4NNInl96rpcI519nMiHDSh5J7pD74CxHZcXSV4c9ZR5UBymchrwmHyZMF6dVrD9Jbr9yph1r7iq6S5wlI2ZImWRjaoGDZ1x+ZU8XnsUmYcP4pa1Yt8JBxSnyUw5gxgBkVCh4eSZBsUCt0cd9P0i7qWVg==,iv:YXQsawXZsQb9ZUt1/lkpfTa4tfKIQrLkkyShFtBRaIQ=,tag:/vSnipGiMntdMqHLePSEQw==,type:str] -piped-db-password: ENC[AES256_GCM,data:5atQccdHYDEf638bpiON9VO14jqNDtzZ8nnXVW0/cqtWkZJc8RYn9N7QhAw=,iv:Gwyf1R+mpmX+TFuoYLPHjXwSDwzJhSEpnj5ZsJgmrtk=,tag:zm4zNkzbqbCyTN6o3lQQfg==,type:str] -pushover-api-token: ENC[AES256_GCM,data:cMBDdySEBQ7vS7FUC2DsCcSvEMpapWvMFmnuCsY6,iv:SVDrrDm2pcAfwUVAC5j47YwF4s/FWNARlZdIZ1Wgwgw=,tag:w7ZeNMPXWc9j+zVaSxq1cQ==,type:str] -pushover-user-key: ENC[AES256_GCM,data:fjoA2YQxmeWEbSKWWE5iyi+CUh1vtW9usVCm5EGk,iv:p4YwYIhpgn/bY9t61//CDrDmZrsj9B/naZit62lCpwo=,tag:pqEw3pDlX7i87tE0Nsy0/Q==,type:str] -wrwks_vpn_key: ENC[AES256_GCM,data:VEHqnr/bDtmyLzs0wnmZ0jCWS0BGJWu6Wjq0ZHJuEz8PH3j/E54S9NUe6WRIo+BJCsh1PlRqw/PD9xSqlW5uPg==,iv:OMP0s8Lc2CmFgwRuwB3UWJVuQFqvpy+BiyhnIKbVIb8=,tag:x1LvSf6i8khd8jKgv/284g==,type:str] -wg_cloonar_key: ENC[AES256_GCM,data:1OfHD8yX+pgCXqqxn7cddnnCA9HBjGra4eht7uLxdcbdG9vDvxUoE1x6aWg=,iv:/NBEbmA3wP/zwrqCeBKDzaoSMqz3f4ZeMlWbu81R5Pg=,tag:Apt8x/j0qiJAKR4UEVSkrA==,type:str] -wg_epicenter_works_key: ENC[AES256_GCM,data:CTZkVGEVRlCdt6W0BGPmX0SZbuBBH5IIlUsi44SGXi7gdmrZNwv2zDv6zjA=,iv:4ZDDKqR6pBq8cjX763tBxOvWFaS2IiGaBxJu6L2JYig=,tag:H8p63BvXSx1SKPFw5gnptw==,type:str] -wg_epicenter_works_psk: ENC[AES256_GCM,data:K0SDlDWfUk9vIGP5U1j8p6TJ9GsydJTuKPb4kMgde1CILOia0S9/+4AkMWY=,iv:ITwLoWZXR6NxRFF3eBvOogiWHLmXnf7S1e2FW0ofr/M=,tag:2OVi3OBFYT0nlCx8gf2AdA==,type:str] -wg_ghetto_at_key: ENC[AES256_GCM,data:+bonpVjV1hxwaqtR7ywshmoDxCnFPD11q0OiNLzxUJIaYrDeS1srpyo6rlE=,iv:Djn16kuXTWqJZy/AT77GpH8RcNtUMZ6zcIdKIMHv+PM=,tag:LP2JCaPKpzeOKvBc2bMr4w==,type:str] -matrix-shared-secret: ENC[AES256_GCM,data:nVSHwPa8xYUaDCxL+5neFtzc11DDNzJtoDCSHYXZ+bZXVAAbp6/Pjx6UkTdAA8B2GOM09nFAsBuLnQfJ3w==,iv:WU3hnRlWVwx7Qin3ejw7V4VhAmYLf6oXzVk6xQgZPgA=,tag:O2hJ2q8XDxYF+rHPNgATgA==,type:str] -phpldapadmin: ENC[AES256_GCM,data:94jCcgGJ89Er5ENLqhFZ1qY44Qp709SuUhBUuED6v/a7mPPjrJGDmi0Gm3r1Hb4CDPGkWf+x4NStY7LSQ2bHEzjyMPMS23wvSLTmC5b2TVca1UI8vZRTD1R7OvdWo8d1oNweSpYEnAXGv3USYF0NZo8DrPLM5G8lG5Tk/rKS/mxU5ZRhPyA60rbmIiy3Mk4yNcs1tvTEckxU/zMVl7zUPAsOOlmYGuwJrHmmh9p7YIWHGIgZNiLs3U0BvSKzN7WktmlwqjfWpeLn4dusqgov4SSQ2otAkxLHIH8mGhyotd1wgXJDZc6tilMe+WPHQDz9db7FT0VdeKggQ94FD+8rP0OsIjR4AdjZ,iv:C8X10wtA9jPgS41pxasaZJTO/XFcRymOyTDZCWJlhmg=,tag:xkMJsGubny+Di+GucAqypQ==,type:str] -palworld: ENC[AES256_GCM,data:iR9nceVotLKrFHnPIVskCYVLev9OzGLLlmfCGQq5hqB1HveXjhjkfm/NMmqnSi9o776+Ezy7l3kkS0R+0cFJ2B9kaWGsdJtdYDwQevmf6Nq5eaBYmvu8kTnaatqZ5e/1BQzcF3to6MA061XL54YGqsAV5FpnDVLhyyzIvaR3gMvMqJ748NL7K+hbBqMFuWcSH3hKXwxtDK7SLtcgx93W5ZgXkZMMumtlH9hSSlZL4yxuQDAQUwHrEBL+rdphA0m27dyS87DA3Av5ZL1MZ+Vlm4uAHM68T+rtVYXTakNImDTc0WrhIP8FZD/UKTAhVYAbA9oz6cbeC574vchuEY1z19SY9+2HshZZBOiPDMqdvrqyszMQCo5I9dUzAJCemQQTlYG8ekREQ0wxARnBYi3iy5PbmgDQWdM3+ff4yhMmGiHtiMQLHzrquKy8nvS9lDp9uT7njkaI0QAt3eNWa2DAQRqXQAtmuRVob5+GS2Nt6XMTWRkbeEb1phwbTqZD5mH4p2TiyMKCn6KOXsgQTxqGr35Izbe+bfptCmUeyscTKq01IZg77w/dvg3AX4iHAcMNgJ9LIHDLibGHQzu9fGN6alpeyy788GDwRY4glYyKxPhCasKkBSj/uhcDAtdg+c63vDTBhqRjNr6+v1NeRW6lVBzrgq+f9QvO5RVKrvdsVHnTA3CMGQzUPAaluNZMpzV5KqxqIrpAAPXnN0ktig==,iv:kkcm/alLHwC84IKK//OJpa36ec9ddOARTIM+KJlOHHs=,tag:jV1DjfNzRgNaCGgJTKIy5g==,type:str] -ark: ENC[AES256_GCM,data:TRTwxqkeUGbtgrWuj1YEFr73+nxCXmt/fR5vVnYR+k4FpNBB2FoY/gXl0kqeFKPDcajwn8nYBs8YE9vmYtAX/Qs4g5OyU9qC/pkmSV7/gbGfqLLqcbIlbWrZzeM8gRW0fp6h1TMPsGO8/iYdF4bmInfuZW+fKr0i7ZRgrtOpPiRCOI/ztPGkFaduuwGIy+yVoS64b9r7ZLRnOZT7ghVv80GKorJuuOQIipNAJMzEqtSA2IqaxWeb13v8wdQoKuMNcD6dCYVJnvgwf4R+,iv:+F9+yJUZBzPSSIt4uLHxjjXAjzRojLxKAyrd8grMXkk=,tag:VrIr4FFbIGTq9RBJMz8/Ig==,type:str] -firefox-sync: ENC[AES256_GCM,data:guNgEVi9n8uJuLkkX2Z3tMY/NVqzQ2tdIutZAqleah9qBri0/3dzVHF2xvztLeAgm/59tN7TtAlAH2SMK6gcfAZDasAWOJ/rGEASxLi6VRjqCe25glDMp2YrA0/mcqZVYMCg+QZ5OPA56b55WDqPHPoBJkPDuTm9axwm6AOxdNi5BkDzMw12fVBxlJL/Rm8=,iv:yD+MkZK5vvZ85vYGd9X2Dv6KkSvMUsMGLrwlJ1pRqlk=,tag:YA379QupHh7aJZKcQxB7bA==,type:str] -knot-tsig-key: ENC[AES256_GCM,data:CBFaRKPr+HRVM01fA9/OLWeD1O33axQKEKJuqDRfcGmuDeP3oXf+ccEJhQE=,iv:2O5y24YenpiMc9txPx8kz8x0aO37LpLjIcwlNywPEak=,tag:J4bVZ7RNSR9fiOBQ2HKpnQ==,type:str] -mopidy-spotify: ENC[AES256_GCM,data:irBeIh2FieNkdf6Hls/Oj+qYxj1U7R7/Ffq6dx+JCS0PdOiFWIHXtccY+PXPKP7RhhaQOgZtIcgPyqTiML52P0c8AwN6UHMl7kgUcKnk60AI0IUZNWorCBZluHhEpf2e2OISlFzDGjSHk+zAzh2eDS1lJ9lCRYEC,iv:r6aZmlVHdRsA9DxkelcIVVpwwm32jaOgP429h61NL/U=,tag:FvPIr0HX/V7+G9kal4nO8w==,type:str] -lms-spotify: ENC[AES256_GCM,data:E53aUSNxE30SSrG6Y6SWKVzmsv0lu8aZvjk1RBgSj3q4m65dPLwGM9HcagN3BPoVTc0tKJaccrjoL2k5FOMnwcTXIz3qgiZGbnB6hVCoOhMrrkoFRN2JzSIA5WxKOT8VuMoC4/a6WaWbY8SWAdhgRQb9uq1hUxdkMCoNRLNJnPqR/0w07lCDVHvkj8XuBV4rGl93VVT3rCzjVTL+Vigv38WZ2il2aANkCz3joNeN8Uod3K/HA5uXLw3cLFmD7eI7LBDSTHpMEg==,iv:iRKrij3TRaufB5BXy7Xhiu3asClZ6hpkbMV14aod7jk=,tag:hpUwP/OHygqfgI6j6q2sKQ==,type:str] +ai-mailer-imap-password: ENC[AES256_GCM,data:shEEPVXXUwyKpqkZgvUifdxd4w==,iv:IgGyuaJLHm7ARgpzKQFUa4CoJtgxgYn6drMXaQqfG9A=,tag:PFjvzSk+l9+M16inz+X+uA==,type:str] +ai-mailer-openrouter-key: ENC[AES256_GCM,data:kJ/ujzpPep5zTv3j6ZKG2N2JaDl6Qi2e1Kz3u7Eu9aXYzziFnBcCIbru6usuKXuC1O7QhozaVemeirthUCOqFV4lzRk8cQsN+w==,iv:dngoIRY/Rr73wbWIl6htCO4TWqMdGt2oxHZS9lT+nFc=,tag:oTHu9yuDl2zTYhjNbRqmow==,type:str] +borg-passphrase: ENC[AES256_GCM,data:VfSwMgBHxETZMUXeEXnvQbRnf/C9Coq2IgrJBUrGsCAqWIvBqioBMTS47/fDPElj2sacn6su3v7vFSNVacf/u1X1+jE=,iv:OC4h9/kD0B1fd1EvBrAb09lu/0mLQcCwV3/gJLMtqDQ=,tag:NaDPQE3bg4eP49mSN6SdPg==,type:str] +borg-ssh-key: ENC[AES256_GCM,data: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,iv:12jhIp7FXTaUYGwh9JtO4EEnz7ps0D2wQgVnXWeeWzY=,tag:IeGpZIvOlVtXf7rp8e612A==,type:str] +ddclient: ENC[AES256_GCM,data:5TD+zbq8gbfpg0zL+Q/rZAM6/UWpdoQnWGUzxffEHNY=,iv:yOYlurCmeFpck81MT3tYmcVwSLH4Q+h8KvWAR56NGGU=,tag:XUxGcSOnn4xWHfpmj9FfDA==,type:str] +filebot-license: ENC[AES256_GCM,data:3Z01llfp3EovHTcOeALZvgoF9Sg9+ZTuSxeb+U5D06+uwl9QCM0Gl9FJ6iwt9gWJTkMS19FMHmG6smpZKVb11GZUFT3D8yTavu9AqRumZYBW6tPR8Q/WHGRSP1kUD0BLL3M3qBlbipqg7XSW4qElGKmyPCW/eswEXnhuIhcBf1wJ9q36WqQuxtOzRLhGi3S8ZaMdfQ8EdGTsePRuYA8qAinuO9G+yejQfzhrHZ5fZhcbTPWlKMzQt+q2m03N0A7Yf6DMuWujmdXbMX7FgRVMvaiz7cCgsKVQQOzRwEVbnivA+3b/BGTY98YJZs2ULKH9gMtnjqt9Pxc4iQVnJ5lUdnBXrsYcU2JVeKhMD3/h9Ds7P5P/Xzv63dvFAdQfJtMLeSvFr2+zbLL3n/1yeamtEZq/9vXs6Sj1Gerrfv/3y0oJbxo7mdlMEZ5hdkFz9m8LZ+b+G4StcWOR8I3fxKoSzXOM2SfmFQ2qeJaIaO8NfhZ6bwfsTZtevZksAeF/MY8hAAUJc4FARV7y0n6X3p5PC/iok1TrtIXOlgzMP8UVZCytDOUztqvpguag4/QpvkAZ0aOQ8ohXxAdzF2jW513beWjN1DGCUDsIn3angS3C80ybp8CsG4NNZBFsW6wL2XcmX6nHaXA9YTRLg3neIXFNcHSnRKHGZGdGIKpg5zeHZJeoN89T4YokUxSw8709F+Tzm5zowNAvnw/a3tQHCpLzy2EoS1MYeFnl5ReuRlitYjTwvZY/Zl9gMI5IGNlLGaSbW6G3r3Vkjtz0XlX/NcD6yk6usM2w5HRUaAaM4uUWvIADTcWG+IVVFgXVVRpdl8eC/4jWf85/naQEHNPch5PmRHMWO8br7iamZzkqODL/iz6UQzEUROydcCvj2KaCKrg=,iv:n/o313QaIZjmp0RJvGw/x24AGh+ZaLcMnyrMdFaugJg=,tag:flPEv7y9UEObPPSkYSwARw==,type:str] +forgejo-mailer-password: ENC[AES256_GCM,data:BK1hLTsz1GtOEHWnXeM8WhFtnUUm5PLFYNlEwTHQ2FjWNU+e244eFWj3P0xUkbXPF5poVUiMOD74LXVLbmlEQm8s,iv:VVXV7awaChr4UUKNwVMrK4MYA2NPcg7PsOEIYu33EQg=,tag:eTcguiaujYGoFf2Mim/TOg==,type:str] +gitea-mailer-password: ENC[AES256_GCM,data:jEj/pbIwJbgjlAJT/C27ZxRopzE7btaBeFV5Nnqzgwqyf/EZO1k257D263jUZX2NWmuB/jMAY8IYc8bO1BJtYiyB,iv:gPGILhy4FFDfqeq762GgHKBoBdCFnZqcLRFtxPIafAU=,tag:gzgDv5WcXGCJWlnqOpkhzA==,type:str] +gitea-runner: ENC[AES256_GCM,data:skZDOjcPFNEEYWf8IEK1goF0r7Ei9K7A9f+qdbqnv/zi/6pF7Ol2wEe6knmcbMUG7NPGg5LETqsP2/ifi0Q7TZO3aFq/cIY4hC2FEhN6mgEd2oWj8H9BT5Fop4IwCQE1a2pF36njmooEs7q83ccnQTmo7K+k2a9jrS5aEVN5fw2H75cppjoc5JSur4Z9oET/BJpeM7KhXBUui3Ebb0fD38WJItWOEXNGhyFZqtsyPFRcgJpUYejBnXo5Y08jCBMNwN1lQwl+1y+FpzJFwXGrfdQ=,iv:ossCQKu2Mg52k5UN8wHDJdC4A9+74X02LeblgINf4qw=,tag:eNekPZUC56Lf4BTax3lhgg==,type:str] +gitea-runner-token: ENC[AES256_GCM,data:6+0h+au4hg0s7XCWsHxlPls/oJUJadph0yG7BVgt15p9UFQhvyEUxIv9vqc265M=,iv:evHWbZz5/voZqKudTS1D+t/n9cSUTaYbv0JCoOUQuiE=,tag:4XMMBuIUPpvxq7iYgDnEyA==,type:str] +home-assistant-ldap: ENC[AES256_GCM,data:imgkUpeBrOG19uKCrb7f3hya3HKqVFiGKXodnb7iMom3bWWtqEQDusJtUssQxPZDvpCszejRfG/j7UZ6iob6KA==,iv:reORLwrkQe0e4c01v4//g/xBU38hXZuv7F2L+TGALVE=,tag:U9KDtcraJsWXSmVuK6xxEA==,type:str] +home-assistant-secrets.yaml: ENC[AES256_GCM,data:xR+HJgS7maPQS3/9WO+z7IKJ1aJG44MhQLBZvciyCgNpyxp17YbKaTnrt19URIWFtbgWqacDgn16GB6vZfBTsf6f3NdPsLVAV21sHr2jAXogfiZds1Db2TsBrfTpUn9ygycQaJwU0D/nkzJxxDJfegvzTYZeiTsn7Gjq2I70+2rOVjpNwXfWQbxNvxB2ywoo1Dg52Bu0wXauprZYGYt84bGg9ZFXCsLFCE4p9fNWRiZKaH7E/ugGxyF07QdfEWGS6gfE6c+/EsHM8w==,iv:wpmbJTweNQfm7fuBM3eyA6CnNL7+o/J/4bEGl6my5GY=,tag:i+VJs/LLVCx+NNEUBOmZHg==,type:str] +piped-db-password: ENC[AES256_GCM,data:6kzLGlwXvr5TZUwKZ7v+ypB+VLDshQv/vKpt/uE1E31HMrK57PcS+pHrK/U=,iv:EWSfx3PBTg3JHGWt31dPi620WZRt/HqYI1KijY7/w24=,tag:xBV6u3NByVaq4tnflmb+mg==,type:str] +pushover-api-token: ENC[AES256_GCM,data:CgqZHBAFOrzTmdwmWTYDJQ7ri2Z7PRzeMzcfD0Hc,iv:5xkiTei/6sr978E2QwQVmLswJEmSPWmfs/RSyB+D2Rc=,tag:vy5GRtAyQA7mLyePau84cw==,type:str] +pushover-user-key: ENC[AES256_GCM,data:L9E5vCh7lAWG+WMoJSV6ralPzl5yeBDSk1kvYmlq,iv:gOetcV9E0uTzeEUoDbswKo9zXvAk9GpoFg/A+TeGY60=,tag:q82CO6mfS+n9q8TfuvXX5Q==,type:str] +wrwks_vpn_key: ENC[AES256_GCM,data:o0v0AZYBaClYC1w5rNIjrnkORILZ2o/+Z5nQ3MTJgHPvvnZJ5AIORuQ4QDUmpAA8VhMivn5F3EKiZ00nBDW/Nw==,iv:XhvTRwDSfseRaUgJ8lbcL/QnaA/eBEvTByLoJh2PeXw=,tag:GLS4eGETG9Q2W6tuW9EXZA==,type:str] +wg_cloonar_key: ENC[AES256_GCM,data:iAyabwyNKEy1VrsXgAPMljxXYg6CQv/n6DzJrjLtAPDO4VbFRLSj82ln7V0=,iv:8ng5RD/hfKVeOx5aGDwilkKJQ60qJbLtgWXBiE3dqb0=,tag:zmE3BO5490U7UdawQ6i/5w==,type:str] +wg_epicenter_works_key: ENC[AES256_GCM,data:JehIfUz4Bi9mNf7nZZufcVf8FxH8MLNFKSJJ+E0/I7rbh4thzUbIjYeLmaI=,iv:KyBToeAMX1jwehlmCi9zi40+RS18RAYExpAlGL4lf/0=,tag:K/o47pmCsqjoGWmGeF+DVQ==,type:str] +wg_epicenter_works_psk: ENC[AES256_GCM,data:kq4LLsNBP6fm7lhU2OS+kUZy4xRjq4hBpT8Lgo/dFpWb/FPL/qWobVG3nDs=,iv:Qm41W1zbSnml8l0yu8yjpE/OOHttNan29gngft2v0P0=,tag:t2qjuZnarRK+9BlmAjXclg==,type:str] +wg_ghetto_at_key: ENC[AES256_GCM,data:FaI5/1P7pXUEA2/5kLiGheK7f99/ntCUNdC3W3a23M1uhNYVGA0AXA+OIXk=,iv:V7KHwsIMewJylTPOXOnSQMkI06w6U1vXecBx/NUcyg4=,tag:gsr4vkqOlN5pexhyycWquQ==,type:str] +matrix-shared-secret: ENC[AES256_GCM,data:qfXA1eFLyFM4hIk5IwXc1zfWVmwvTgUNcSJDYGar0L9b+cHD8kM0qEr2DsPkDcy+7DJNldK+tcdgkCV8GA==,iv:XqwxLRs3ADyjmmKVKMMsANGhGzTg7PXQNiYnrp+WYFo=,tag:duSgoaxqm3z71jGfP9lCoA==,type:str] +phpldapadmin: ENC[AES256_GCM,data:oDgmCm38ZEhcpOvyY49jv+7FmikzrN2WCxJ9i5wvOTvUFblz1pVSe3/qm0VZ/IqdbudTpoOHIc97znAE129fPPNGbd3prY3v+dXn5JpUhcSuBT4QOf3aXVUlPzUVgTXt9E/Y/ASX39dPXGWEDt/XelXHVkgQAHwAt4XFSx20Ph2b1x1s1Gs/WETZe1Dyl8y4TyMRn4TuGYvm1l8iob8RLGazCk5XOGL+0AyLe/0H1vTKZN9lu7S/VH6612tDQxVJf2grzmZu1t8P9QBVgFxpmqdGsudMMeGoO8ajOWkgu2DUziZc0ul+PNdDdPThpetV6jjcZ8qJ9bZsbbDadZC8WWmKt+P+WPfE,iv:IUhwcZez5ROJuiKynSmfvI7j5iLF4arJokt6Gs10Xhs=,tag:MnVgO7JyOetB1j2ZZtxLLw==,type:str] +palworld: ENC[AES256_GCM,data:ZEf4Qw6FiHQwgWJAvX/oy90rJWffjwqqu9aqhvtzY8BmffhGDDPc653GEwxBEMRdBBVot+puxYkHA/WvQgPb1FaU+qXmAkclw9ayNcxqm7Ra74thV9JAseNH6xuDycWD0mNNEF6IRjsiB86JpeOivjrZeZy5zHSieMx01u235szmR+j/qX+QOo+E5NyVNPyIIT1LiJ3MpNPLltzNfiAfuDMbT+2VHC3EzU5BxxrXJ6iY9nRvDLoLOznfGG63eNxq4xRH4BL7kp0Qv4a8JlQlV9rfGyKqcwWH4FrRVNP/1xdT2Q+9qBHlnSafY2sj0UXI2FGvbwhuHxFZ/OSVwohe5ag/xtCVDgFE8bKaKPyXl+qgR9gNvwyAYRoOv1WDQxc4MkzJIBchPGLA2L5RFGZddByOUAR4LsyqvC6n8jjU19dSFaEGWSJDF068Szq2/dWdAl19Edw/nTvfRqVnJSM2m2tf5F1kBBM/yrtJrzDzmTd/wDwLEfhbO0PyktWjltjrrEzqxlaLeIYDKwx3+vlDLn+7rxaHZAHiwshGGYgW9Yxf0Epb2+o0dNgJ6X1+v+vbeWfgyobLklA7qKvodoEvAHLGsW9YicURNNjy/ERwA8ojSezfEZ6+pRhrk5s5HUDtq4b5tbD1FocPYXZyhSlIIqc02FGWJnQQamooa1hTnABYRDz0DtFudLHvRDFnUei4v827nr97y9e91GC21Q==,iv:DtA27Ksq1pGMxlq3UJ4HxYiJC4dTjBJD/aRbDIijyNE=,tag:J0kvm+khW63qfmHZwXzyRg==,type:str] +ark: ENC[AES256_GCM,data:mwIyAXNT6rAEoxyh+kbTnTlZMgbYlRUzAKHhbI8CG7AdG5l1gNhnnAfcIPWjx2Y+VAwL6XIRJL18FCxCB52UHMDqLpkdn4/pf4gaJ6zLzLuHBNPGDtLB21+frderjuVbOMxwV6Dx946clgIHFA/FY9ooKOknS5K4mNQm7wyrTUJQ38shyRMwbbpPA9sQOK41XMkaU0Axdpie1adZiN+Vq9ikwf4H1TglymPeXvIypEwWN0iJXaHwr7CKGDyWixYAjUz4VwfkeNVfyR1u,iv:3pHjP5CZLM5xULs5oSk6BlCEHccqdrrVGLZctYSb9eA=,tag:wLMORvSvdcb/If86vZsXXg==,type:str] +firefox-sync: ENC[AES256_GCM,data:osI5nogy+LQQ7KAECZlJ2cO2eZ5lz40yON6kgW9ROmicpuukr7+9R7jURYNZg2ETNMrZpK0bSK6zWoe9hlcwtTwVz815ocLU+o4rdUFpUjE36a0+toT9AWhwF0K9RVwITK6A5nIW8TADH3RaGZI6PGQzrjn9F1nMruo3gn0h1Gv3R4mT1ySpZxc4CV9zyKE=,iv:ou78yVK/d9X4sKSmSp2s/WhRp1TCe+adbK52He5qVE8=,tag:FJgLYpS3dtwku4Vs/SvXUw==,type:str] +knot-tsig-key: ENC[AES256_GCM,data:Mj201Ikwl6VuU8cBmwRTJbApOfCwIYt/g0dZL8kYDBoLuUNkkRygO7HB+vQ=,iv:vDjQ2Of8/u4gJHZD+brUeGcjWSv3psAYTUp8FXGVDaw=,tag:7IBISNf7RnV99zYAKAyTpQ==,type:str] +mopidy-spotify: ENC[AES256_GCM,data:oZag4Uyhk7Lkd2awfInu0bABKB2aB9ZxgQ/oOqJ5QJ/HEQO6ysWVBodu2MF2PPBe13Y6zv3HAnz4DEi1rDNBRwxRloyAwSAnB8R1uTuIh0fnJ2lr64RuT2+EermmhVz/HoypCnKTlFad39d16cRCEIy3n7cYnbG3,iv:xZAroRKaKn0xuMieUWcs4KdtRyVbuxm2HaWLV5zMr/8=,tag:i0kiytOZhVXKmSvF41T/Og==,type:str] +lms-spotify: ENC[AES256_GCM,data:1GPYCleCAkqkctywo6QTTLCBv4nq1I+jGXreYXfYZX3X9qdDjBlGuQyIIzD/krspUg5iYm0m1z6CEkKZQOeCs6IUv7Da+iMV1Kte3/nosWpEqYtYeC0kIw3wV9RtAIsMCFADj9W/w97NTMjebg4CZdmqVjI8coFDEN9dKqfMWT5amsC+emH2oO4k67vA7lgBBhL69Wgi7YAC2lSL1EVvcestWSzVcIHWucH8VI7CIKXJOLNzPD6hsKoQARroo4N3lQZ/dTpd1A==,iv:7Ukq1yPXeoTTmFroXL6sOBG76INPqynKyrbBQIv1Y+A=,tag:KY2rfZrqUhm2xBSsjHRWDQ==,type:str] sops: age: - recipient: age14grjcxaq4h55yfnjxvnqhtswxhj9sfdcvyas4lwvpa8py27pjy2sv3g6v7 enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrTDFvM2l3Tm5lU0paWXpF - cjVBSFhENW5mNG9DSFM1NXh3UHdaKzlKMGlZCnRmNFBFVWY4N0FqLzF1bUMyUDdL - U091VENiVFhYeEJ5K0xodXlHVkhHKzgKLS0tIGxta3A2TjJiMUtiR2RzcU02Rys5 - U1c0SjRKK2UwbTVIQUMrT1pOOVFmOVkKY3UyGNIPZJLE8GG124y0pLgqGub9SMCq - plK5H+kASOB1X6pK+3PBFuDYT1AbsRxXvWgAEMvVI7eBcxQlSrrB4Q== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzZGZiWHg4OHRENFZUR0N1 + L2UwdTc5RU1RRlZrZitxNG1aeSs3eEJweEVjCnp4NjdnTFlPVGE5Z09uQjJyL1da + MjN1UGdwdDM4ODNhdVVtMmhlTDNuNEUKLS0tIHhhTUpjbkVDSEU4d1plM0dCSy9J + OFU0TU5hZW1sdzdudmRkRzRKOVZrb3cKa27jKzE608GlCCT0RW9caAaV9TN5kes6 + xqcj6s2CPa6LeMC0yMfeN2bYDPGXtEGUStzG0hXwg4pvQliBMtht5Q== -----END AGE ENCRYPTED FILE----- - recipient: age1exny8unxynaw03yu8ppahu5z28uermghr8ag34e7kdqnaduq9stsyettzz enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVaXBqMGl1UytNL3BkZEhQ - S3RFL3lZRVZKTGVRTGFMNlFlWFRCNDNvRTM4CnpWZWovSDZaclQvN2Vwa0dWZGgz - Q1ZLM0sveXBxOVpvNHkycWJWWXdmVE0KLS0tIHl2bFk3RE03N01IdDJPWk5HT1Np - Qm82Sit3Q0haaDdnbzFjendMUm04Wk0KYp09dxXjzvC4IlH6Ilip8YjTz0mFeu/0 - 5IDMYjT1BuW5YiKgIJVd+UgOd6ysZLFFwk+Us2AcV7z110xk/askqQ== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2cUdxb0kvNkdDeWVMYmVJ + L2xlY29QaTd6ZFY4dCtsejhHN2s2anMya3d3CjdDN3FqajlzS3llWGhVK1A3eVdE + RjFuZmVBdFROTXhzZWduK0RVRWQyT1kKLS0tIGpzekV1OEJ3cW5Md1BhMitncnBs + OWUzY1p1dndFWjBTdXNOSEZyUjhMNzgKzewnYGETh0NSUad3p9s49JQyt0JHV58Y + eni6QDJ/ruQao2d2xBhwb4koGbzSUPQ2ie0syoOfMrxXYmu76bCb7Q== -----END AGE ENCRYPTED FILE----- - recipient: age1v6p8dan2t3w9h94fz4flldl32082j3s9x6zqq7u5j66keth9aphsd6pvch enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4Wkd0YnBQRnExeVdUTGFu - N3o3MnF2aTY2NlBmdDJYT01zRytWZ2w1dFg0ClAzcnJ0NFYrVWlBM2JQU1B0SEJi - MGE5aVh6KzNmaEoxaHFOTW90K0VmMGsKLS0tIDNkOGZyVmMzME80TlBWMzI5UVR2 - djB3Y2FIRDFKWlEwTnRBUnRIT3M2OXcK+SIt/7DRdQi6H1AZooJN2Pt2g1EwVTZe - Q14cEt0sLyVYzLJugfz2JWRHDZX6wPueYcTSEs7w3wAPVwvJWju8bg== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKNVVrZzV5dDlWeFJINkVj + KzFDUUl6OGRCN1o3eEhGdGdjTHdGVWZld0hJCjI4dWtNZUltb0RqTGxoT3JONC95 + YXgraDJWcklyYUluTWZlU2pSY3NGTUkKLS0tIHFMYUJKN3h0Mm4rU1J2MmRCdjky + Vi93N3JHdi82WDRZRXUwbFQzRlJmUk0Kha2OzeHtJo5sIVqR5S0Wcpkp/nmo0s9k + WNXEtj8qPvtR4Ji/V7P6BdwGmtn+Yr4zbN+CamHkOjR3C62EQbXZSw== -----END AGE ENCRYPTED FILE----- - recipient: age1wq82xjyj80htz33x7agxddjfumr3wkwh3r24tasagepxw7ka893sau68df enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzQXhNSFBnNUtMdkpwR0th - M1NmOVorcUdlZTFDM3dVRHZlYWpJcDZiakVnCit6eTFOeW92SzhPYzJxR0VTem9r - MSs4cWxRbzVBQmlWaHIwMjB5RUlJMXcKLS0tIHNSVTloOEVVVndDWkVrWmQrYXlD - NTd1WGFJWHVLTnFNT3hYbDdtSnMzTTAKBmJOayZLbjmBejwVzVtUSYPki+qPkYwG - xdO3L7n0Z8Cv/kVYZpkuG5GqOUL+nCJuYDjF0g4PaLb6WWd0W8ZGFA== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEODNlS3EwcmxFN3hvTkVv + UTU4OFVIZENTbFc4dmlkSk5Gdk1qK1FySTJnCjJWR2FTYjZ1dDA3QzMyQyt6RW1C + UFlQZkFrS0QzSldvQjdRbkdLRDJNa3cKLS0tIDZTa3NXUks0M0FOdm0xSHBuSzVp + dVdIdXVDMkEvTktYWFFDak5WSzFSWlUKJ3y82O8e/z0Yag8vP6z/SSjExcVQTyvU + OPLHYcg5sByQ75AT5GBA4ZMF7M8EH1cP2QL7X8u4SeHDrVbMTx7d2w== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-12-01T11:01:54Z" - mac: ENC[AES256_GCM,data:taGX5HHZCL7Zo4taS2Jz/5WxhvpBNNKZ13ZCtS3x/P17tC1Nrk2UDcxbOZ1pPVbVvvaAHJtDb3owFvBOM4nr2Eve0M9zT4HbXh3hke7AviQ6U7CT1ru6LjY7W8lBjbQ6uCt+Ldxd1PRPPGiyKdK5GAUPKg6avFjpJbhEikh8Gww=,iv:NNs5usVJ5izYvHKnNm1IgjSt4dg0QFQ7cClJ6zh+3wM=,tag:sYYbEWIUgOWthEItdy5PFg==,type:str] + lastmodified: "2026-01-26T22:13:01Z" + mac: ENC[AES256_GCM,data:FuOSimEEfiyjY0oBzkaDcEwYhQZCyNiQYXPJxKFmybZ2eH+BKRoydtgMpCnOumCHHOH8W1N5HO7Ls7JSieAcx1MrFPXTkIaseUB+mQh69k42yi9FJ3rus2u1uT5KOLCDantE7HhlBCuEOSE9qoCxeda8Zx9210gXPt7QfgzkXds=,iv:39xgMDM0Y7nNF1tDN7N4zwdu0dvfL3Ss6yH5KS+Udk0=,tag:0u8crcNalWIyBc0WKxXzkQ==,type:str] unencrypted_suffix: _unencrypted version: 3.11.0 From eba36f9d56a82dfe7961870310fe1dec7506ed2d Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Tue, 27 Jan 2026 00:52:16 +0100 Subject: [PATCH 3/3] fix: forgejo ip --- hosts/fw/modules/dnsmasq.nix | 1 + hosts/fw/modules/forgejo.nix | 8 +- hosts/fw/modules/web/proxies.nix | 9 + scripts/migrate-gitea-to-forgejo.env.example | 19 + scripts/migrate-gitea-to-forgejo.sh | 497 +++++++++++++++++++ 5 files changed, 532 insertions(+), 2 deletions(-) create mode 100644 scripts/migrate-gitea-to-forgejo.env.example create mode 100755 scripts/migrate-gitea-to-forgejo.sh diff --git a/hosts/fw/modules/dnsmasq.nix b/hosts/fw/modules/dnsmasq.nix index 12ce8e1..7461ae6 100644 --- a/hosts/fw/modules/dnsmasq.nix +++ b/hosts/fw/modules/dnsmasq.nix @@ -102,6 +102,7 @@ "/snapcast.cloonar.com/${config.networkPrefix}.97.21" "/lms.cloonar.com/${config.networkPrefix}.97.21" "/git.cloonar.com/${config.networkPrefix}.97.50" + "/forgejo.cloonar.com/${config.networkPrefix}.97.55" "/feeds.cloonar.com/188.34.191.144" "/nukibridge1a753f72.cloonar.smart/${config.networkPrefix}.100.112" "/allywatch.cloonar.com/${config.networkPrefix}.97.5" diff --git a/hosts/fw/modules/forgejo.nix b/hosts/fw/modules/forgejo.nix index ec6a237..c6fead7 100644 --- a/hosts/fw/modules/forgejo.nix +++ b/hosts/fw/modules/forgejo.nix @@ -20,6 +20,9 @@ in users.groups.forgejo = group; # Reuse the existing git.cloonar.com ACME cert from gitea.nix + security.acme.certs."forgejo.cloonar.com" = { + group = "nginx"; + }; containers.forgejo = { autoStart = false; # Don't start until migration is complete @@ -27,14 +30,15 @@ in privateNetwork = true; hostBridge = "server"; hostAddress = "${networkPrefix}.97.1"; - localAddress = "${networkPrefix}.97.51/24"; # Different from gitea's .50 + localAddress = "${networkPrefix}.97.55/24"; # Different from gitea's .50 bindMounts = { "/var/lib/forgejo" = { hostPath = "/var/lib/forgejo/"; isReadOnly = false; }; "/var/lib/acme/forgejo/" = { - hostPath = config.security.acme.certs.${domain}.directory; + # hostPath = config.security.acme.certs.${domain}.directory; + hostPath = config.security.acme.certs."forgejo.cloonar.com".directory; isReadOnly = true; }; "/run/secrets/forgejo-mailer-password" = { diff --git a/hosts/fw/modules/web/proxies.nix b/hosts/fw/modules/web/proxies.nix index 5b33e43..5cc42d5 100644 --- a/hosts/fw/modules/web/proxies.nix +++ b/hosts/fw/modules/web/proxies.nix @@ -7,6 +7,15 @@ proxyPass = "https://git.cloonar.com/"; }; }; + services.nginx.virtualHosts."forgejo.cloonar.com" = { + forceSSL = true; + enableACME = true; + acmeRoot = null; + locations."/" = { + proxyPass = "http://${config.networkPrefix}.97.55:3001/"; + proxyWebsockets = true; + }; + }; services.nginx.virtualHosts."foundry-vtt.cloonar.com" = { forceSSL = true; enableACME = true; diff --git a/scripts/migrate-gitea-to-forgejo.env.example b/scripts/migrate-gitea-to-forgejo.env.example new file mode 100644 index 0000000..7695ad0 --- /dev/null +++ b/scripts/migrate-gitea-to-forgejo.env.example @@ -0,0 +1,19 @@ +# Gitea to Forgejo Migration - Environment Configuration +# +# Copy this file to migrate-gitea-to-forgejo.env and adjust values. +# Then run: ./scripts/migrate-gitea-to-forgejo.sh +# +# IMPORTANT: Ensure Gitea is stopped before running migration. + +# Source (Gitea) - READ ONLY, never modified +# This is the original Gitea data directory +SOURCE_DATA=/var/lib/gitea + +# Target (Forgejo) - where data will be copied +# Must be on a filesystem with enough space (1.2x source size) +TARGET_DATA=/var/lib/forgejo + +# User/group for target files +# These should match your Forgejo service user +TARGET_USER=forgejo +TARGET_GROUP=forgejo diff --git a/scripts/migrate-gitea-to-forgejo.sh b/scripts/migrate-gitea-to-forgejo.sh new file mode 100755 index 0000000..aef9354 --- /dev/null +++ b/scripts/migrate-gitea-to-forgejo.sh @@ -0,0 +1,497 @@ +#!/usr/bin/env bash +# +# Gitea 1.25.4 to Forgejo Migration Script +# +# This script copies data from Gitea to Forgejo and rolls back the database +# schema from version 322/323 to 304, allowing Forgejo to run its own migrations. +# +# IMPORTANT: This script NEVER modifies source data. All operations work on copies, +# so the original Gitea instance can be restarted as a rollback. +# +# Usage: +# 1. Copy migrate-gitea-to-forgejo.env.example to migrate-gitea-to-forgejo.env +# 2. Edit the .env file with your paths +# 3. Stop Gitea +# 4. Run: ./scripts/migrate-gitea-to-forgejo.sh +# 5. Update NixOS config and deploy +# +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ENV_FILE="${SCRIPT_DIR}/migrate-gitea-to-forgejo.env" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +log_info() { echo -e "${BLUE}[INFO]${NC} $*"; } +log_success() { echo -e "${GREEN}[OK]${NC} $*"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } + +# Load environment file +if [[ ! -f "$ENV_FILE" ]]; then + log_error "Environment file not found: $ENV_FILE" + log_info "Copy migrate-gitea-to-forgejo.env.example to migrate-gitea-to-forgejo.env and configure it." + exit 1 +fi + +# shellcheck source=/dev/null +source "$ENV_FILE" + +# Verify required variables +: "${SOURCE_DATA:?SOURCE_DATA must be set in $ENV_FILE}" +: "${TARGET_DATA:?TARGET_DATA must be set in $ENV_FILE}" +: "${TARGET_USER:?TARGET_USER must be set in $ENV_FILE}" +: "${TARGET_GROUP:?TARGET_GROUP must be set in $ENV_FILE}" + +echo "========================================" +echo "Gitea to Forgejo Migration Script" +echo "========================================" +echo "" +echo "Source: $SOURCE_DATA (read-only)" +echo "Target: $TARGET_DATA" +echo "User: $TARGET_USER:$TARGET_GROUP" +echo "" + +# ============================================ +# PHASE 1: Pre-flight Checks +# ============================================ +log_info "Phase 1: Pre-flight checks..." + +# Check if running as root (needed for chown) +if [[ $EUID -ne 0 ]]; then + log_error "This script must be run as root (for chown operations)" + exit 1 +fi + +# Verify SQLite version >= 3.35 (required for DROP COLUMN) +if ! command -v sqlite3 &> /dev/null; then + log_error "sqlite3 command not found. Please install SQLite." + exit 1 +fi + +sqlite_version=$(sqlite3 --version | cut -d' ' -f1) +sqlite_major=$(echo "$sqlite_version" | cut -d'.' -f1) +sqlite_minor=$(echo "$sqlite_version" | cut -d'.' -f2) +if [[ "$sqlite_major" -lt 3 ]] || { [[ "$sqlite_major" -eq 3 ]] && [[ "$sqlite_minor" -lt 35 ]]; }; then + log_error "SQLite $sqlite_version is too old. Need 3.35+ for DROP COLUMN support." + exit 1 +fi +log_success "SQLite version: $sqlite_version" + +# Verify rsync is available (needed for incremental copying) +if ! command -v rsync &> /dev/null; then + log_error "rsync command not found. Please install rsync." + exit 1 +fi +log_success "rsync available" + +# Verify source exists +if [[ ! -d "$SOURCE_DATA" ]]; then + log_error "Source directory not found: $SOURCE_DATA" + exit 1 +fi +log_success "Source directory exists" + +# Find source database (could be gitea.db or forgejo.db depending on setup) +SOURCE_DB="" +if [[ -f "$SOURCE_DATA/data/gitea.db" ]]; then + SOURCE_DB="$SOURCE_DATA/data/gitea.db" +elif [[ -f "$SOURCE_DATA/gitea.db" ]]; then + SOURCE_DB="$SOURCE_DATA/gitea.db" +else + log_error "Source database not found in $SOURCE_DATA/data/ or $SOURCE_DATA/" + exit 1 +fi +log_success "Source database found: $SOURCE_DB" + +# Verify source app.ini exists +SOURCE_INI="" +if [[ -f "$SOURCE_DATA/custom/conf/app.ini" ]]; then + SOURCE_INI="$SOURCE_DATA/custom/conf/app.ini" +elif [[ -f "$SOURCE_DATA/conf/app.ini" ]]; then + SOURCE_INI="$SOURCE_DATA/conf/app.ini" +else + log_error "Source app.ini not found in $SOURCE_DATA/custom/conf/ or $SOURCE_DATA/conf/" + exit 1 +fi +log_success "Source app.ini found: $SOURCE_INI" + +# Check disk space (need 1.2x source size) +source_size=$(du -sb "$SOURCE_DATA" | cut -f1) +required=$((source_size * 12 / 10)) +target_parent=$(dirname "$TARGET_DATA") +mkdir -p "$target_parent" +available=$(df --output=avail -B1 "$target_parent" | tail -1) +if [[ "$available" -lt "$required" ]]; then + log_error "Not enough disk space. Need $(numfmt --to=iec $required), have $(numfmt --to=iec $available)" + exit 1 +fi +log_success "Disk space OK: need $(numfmt --to=iec $required), have $(numfmt --to=iec $available)" + +# Warn if target exists (rsync will sync incrementally) +if [[ -d "$TARGET_DATA" ]]; then + log_warn "Target directory exists: $TARGET_DATA" + log_info "rsync will perform incremental sync (only copying changed files)" + read -p "Continue with incremental sync? (y/N) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + log_error "Aborted by user" + exit 1 + fi +fi + +# ============================================ +# PHASE 2: Copy All Data +# ============================================ +log_info "Phase 2: Copying data..." + +mkdir -p "$TARGET_DATA/data" +mkdir -p "$TARGET_DATA/custom/conf" + +# Copy database +log_info "Copying database..." +rsync -a --info=progress2 "$SOURCE_DB" "$TARGET_DATA/data/forgejo.db" +log_success "Database copied" + +# Copy all data directories (preserve attributes, sync incrementally) +for dir in repositories avatars attachments packages lfs custom queues indexers; do + if [[ -d "$SOURCE_DATA/$dir" ]]; then + log_info "Syncing $dir..." + mkdir -p "$TARGET_DATA/$dir" + rsync -a --delete --info=progress2 "$SOURCE_DATA/$dir/" "$TARGET_DATA/$dir/" + log_success "Synced $dir" + fi +done + +# Also check data/ subdirectory structure +for dir in repositories avatars attachments packages lfs; do + if [[ -d "$SOURCE_DATA/data/$dir" ]]; then + log_info "Syncing data/$dir..." + mkdir -p "$TARGET_DATA/data/$dir" + rsync -a --delete --info=progress2 "$SOURCE_DATA/data/$dir/" "$TARGET_DATA/data/$dir/" + log_success "Synced data/$dir" + fi +done + +# ============================================ +# PHASE 3: Database Schema Rollback +# ============================================ +log_info "Phase 3: Rolling back database schema..." + +TARGET_DB="$TARGET_DATA/data/forgejo.db" + +# Show current schema version +current_version=$(sqlite3 "$TARGET_DB" "SELECT version FROM version WHERE id=1;") +log_info "Current Gitea schema version: $current_version" +log_info "Target version: 304" + +# Create rollback SQL script +ROLLBACK_SQL=$(mktemp) +cat > "$ROLLBACK_SQL" << 'ROLLBACK_EOF' +-- ================================================================ +-- Gitea 1.25.4 to Forgejo Rollback Script +-- Rolls back migrations 305-322 to allow Forgejo to migrate cleanly +-- ================================================================ + +-- Enable foreign keys check after we're done +PRAGMA foreign_keys = OFF; + +-- ============================================ +-- MIGRATION 305: Drop repo_license table +-- ============================================ +DROP TABLE IF EXISTS repo_license; + +-- ============================================ +-- MIGRATION 308 & 317: Drop action table indices +-- (These are the main conflict source) +-- ============================================ +DROP INDEX IF EXISTS IDX_action_r_u_d; +DROP INDEX IF EXISTS IDX_action_au_r_c_u_d; +DROP INDEX IF EXISTS IDX_action_c_u_d; +DROP INDEX IF EXISTS IDX_action_c_u; +DROP INDEX IF EXISTS IDX_action_au_c_u; +-- Alternative naming conventions +DROP INDEX IF EXISTS UQE_action_r_u_d; +DROP INDEX IF EXISTS UQE_action_au_r_c_u_d; +DROP INDEX IF EXISTS UQE_action_c_u_d; +DROP INDEX IF EXISTS UQE_action_c_u; +DROP INDEX IF EXISTS UQE_action_au_c_u; + +-- ============================================ +-- MIGRATION 309: Drop notification table indices +-- ============================================ +DROP INDEX IF EXISTS IDX_notification_u_s_uu; +DROP INDEX IF EXISTS IDX_notification_user_id; +DROP INDEX IF EXISTS IDX_notification_repo_id; +DROP INDEX IF EXISTS IDX_notification_status; +DROP INDEX IF EXISTS IDX_notification_source; +DROP INDEX IF EXISTS IDX_notification_issue_id; +DROP INDEX IF EXISTS IDX_notification_commit_id; +DROP INDEX IF EXISTS IDX_notification_updated_by; +DROP INDEX IF EXISTS UQE_notification_u_s_uu; + +-- ============================================ +-- MIGRATION 313: Drop issue_pin table +-- (pin_order restoration handled separately) +-- ============================================ +DROP TABLE IF EXISTS issue_pin; + +-- ============================================ +-- MIGRATION 306: Drop protected_branch column +-- ============================================ +ALTER TABLE protected_branch DROP COLUMN IF EXISTS block_admin_merge_override; + +-- ============================================ +-- MIGRATION 310: Drop protected_branch column +-- ============================================ +ALTER TABLE protected_branch DROP COLUMN IF EXISTS priority; + +-- ============================================ +-- MIGRATION 311: Drop issue column +-- ============================================ +ALTER TABLE issue DROP COLUMN IF EXISTS time_estimate; + +-- ============================================ +-- MIGRATION 312: Drop pull_auto_merge column +-- ============================================ +ALTER TABLE pull_auto_merge DROP COLUMN IF EXISTS delete_branch_after_merge; + +-- ============================================ +-- MIGRATION 315: Drop action_runner column +-- ============================================ +ALTER TABLE action_runner DROP COLUMN IF EXISTS ephemeral; + +-- ============================================ +-- MIGRATION 316: Drop description columns +-- ============================================ +ALTER TABLE secret DROP COLUMN IF EXISTS description; +ALTER TABLE action_variable DROP COLUMN IF EXISTS description; + +-- ============================================ +-- MIGRATION 318: Drop repo_unit column +-- ============================================ +ALTER TABLE repo_unit DROP COLUMN IF EXISTS anonymous_access_mode; + +-- ============================================ +-- MIGRATION 319: Drop label column +-- ============================================ +ALTER TABLE label DROP COLUMN IF EXISTS exclusive_order; + +-- ============================================ +-- MIGRATION 320: Drop login_source column +-- ============================================ +ALTER TABLE login_source DROP COLUMN IF EXISTS two_factor_policy; + +-- ============================================ +-- SET VERSION TO 304 +-- ============================================ +UPDATE version SET version = 304 WHERE id = 1; + +PRAGMA foreign_keys = ON; +ROLLBACK_EOF + +log_info "Executing schema rollback..." + +# SQLite doesn't support DROP COLUMN IF EXISTS, so we need to handle errors gracefully +# Execute each ALTER TABLE separately to handle missing columns +sqlite3 "$TARGET_DB" << 'SQL_PART1' +PRAGMA foreign_keys = OFF; + +-- Drop tables +DROP TABLE IF EXISTS repo_license; +DROP TABLE IF EXISTS issue_pin; + +-- Drop indices (these always work, even if index doesn't exist) +DROP INDEX IF EXISTS IDX_action_r_u_d; +DROP INDEX IF EXISTS IDX_action_au_r_c_u_d; +DROP INDEX IF EXISTS IDX_action_c_u_d; +DROP INDEX IF EXISTS IDX_action_c_u; +DROP INDEX IF EXISTS IDX_action_au_c_u; +DROP INDEX IF EXISTS UQE_action_r_u_d; +DROP INDEX IF EXISTS UQE_action_au_r_c_u_d; +DROP INDEX IF EXISTS UQE_action_c_u_d; +DROP INDEX IF EXISTS UQE_action_c_u; +DROP INDEX IF EXISTS UQE_action_au_c_u; +DROP INDEX IF EXISTS IDX_notification_u_s_uu; +DROP INDEX IF EXISTS IDX_notification_user_id; +DROP INDEX IF EXISTS IDX_notification_repo_id; +DROP INDEX IF EXISTS IDX_notification_status; +DROP INDEX IF EXISTS IDX_notification_source; +DROP INDEX IF EXISTS IDX_notification_issue_id; +DROP INDEX IF EXISTS IDX_notification_commit_id; +DROP INDEX IF EXISTS IDX_notification_updated_by; +DROP INDEX IF EXISTS UQE_notification_u_s_uu; +SQL_PART1 + +# Function to drop column if it exists +drop_column_if_exists() { + local table="$1" + local column="$2" + local exists + exists=$(sqlite3 "$TARGET_DB" "SELECT COUNT(*) FROM pragma_table_info('$table') WHERE name='$column';") + if [[ "$exists" -gt 0 ]]; then + log_info "Dropping column $table.$column..." + sqlite3 "$TARGET_DB" "ALTER TABLE $table DROP COLUMN $column;" + log_success "Dropped $table.$column" + else + log_info "Column $table.$column does not exist, skipping" + fi +} + +# Drop columns added in migrations 306-320 +drop_column_if_exists "protected_branch" "block_admin_merge_override" +drop_column_if_exists "protected_branch" "priority" +drop_column_if_exists "issue" "time_estimate" +drop_column_if_exists "pull_auto_merge" "delete_branch_after_merge" +drop_column_if_exists "action_runner" "ephemeral" +drop_column_if_exists "secret" "description" +drop_column_if_exists "action_variable" "description" +drop_column_if_exists "repo_unit" "anonymous_access_mode" +drop_column_if_exists "label" "exclusive_order" +drop_column_if_exists "login_source" "two_factor_policy" + +# Check if pin_order column needs to be added back to issue table (migration 313 removed it) +log_info "Checking if pin_order column needs to be restored to issue table..." +has_pin_order=$(sqlite3 "$TARGET_DB" "SELECT COUNT(*) FROM pragma_table_info('issue') WHERE name='pin_order';") +if [[ "$has_pin_order" -eq 0 ]]; then + log_info "Adding pin_order column back to issue table..." + sqlite3 "$TARGET_DB" "ALTER TABLE issue ADD COLUMN pin_order INTEGER DEFAULT 0;" + log_success "Added pin_order column to issue table" +else + log_info "pin_order column already exists in issue table" +fi + +# Set version to 304 (allows Forgejo to run migration 305 which converts two_factor.secret from TEXT to BLOB) +sqlite3 "$TARGET_DB" "UPDATE version SET version = 304 WHERE id = 1;" +log_success "Database version set to 304" + +rm -f "$ROLLBACK_SQL" + +# ============================================ +# PHASE 4: Clear Regeneratable Data +# ============================================ +log_info "Phase 4: Clearing regeneratable data..." + +# Remove indexers (will be rebuilt on first start) +if [[ -d "$TARGET_DATA/indexers" ]]; then + rm -rf "$TARGET_DATA/indexers" + log_success "Removed indexers (will be rebuilt)" +fi + +# Remove queues (will be recreated) +if [[ -d "$TARGET_DATA/queues" ]]; then + rm -rf "$TARGET_DATA/queues" + log_success "Removed queues (will be recreated)" +fi + +# ============================================ +# PHASE 5: Update Configuration +# ============================================ +log_info "Phase 5: Updating configuration..." + +# Copy app.ini +rsync -a --info=progress2 "$SOURCE_INI" "$TARGET_DATA/custom/conf/app.ini" +log_success "Copied app.ini" + +# Update paths from gitea to forgejo +sed -i 's|/var/lib/gitea|/var/lib/forgejo|g' "$TARGET_DATA/custom/conf/app.ini" +log_success "Updated paths in app.ini" + +# Check if WAL mode is already configured +if ! grep -q "SQLITE_JOURNAL_MODE" "$TARGET_DATA/custom/conf/app.ini"; then + # Add WAL mode after [database] section + sed -i '/^\[database\]/a SQLITE_JOURNAL_MODE = WAL' "$TARGET_DATA/custom/conf/app.ini" + log_success "Enabled SQLite WAL mode" +else + log_info "SQLite journal mode already configured" +fi + +# ============================================ +# PHASE 6: Set Permissions +# ============================================ +log_info "Phase 6: Setting permissions..." + +chown -R "$TARGET_USER:$TARGET_GROUP" "$TARGET_DATA" +chmod 750 "$TARGET_DATA" +chmod 640 "$TARGET_DATA/data/forgejo.db" +log_success "Permissions set for $TARGET_USER:$TARGET_GROUP" + +# ============================================ +# PHASE 7: Verify Database Integrity +# ============================================ +log_info "Phase 7: Verifying database integrity..." + +sqlite3 "$TARGET_DB" << 'VERIFY_SQL' +.headers off +.mode list + +-- Verify version was set correctly +SELECT 'Version: ' || CASE WHEN version = 304 THEN 'PASS (304)' ELSE 'FAIL (version=' || version || ')' END +FROM version WHERE id = 1; + +-- Check critical tables exist +SELECT 'Users: ' || CASE WHEN COUNT(*) > 0 THEN 'PASS (' || COUNT(*) || ' users)' ELSE 'WARN (empty)' END FROM user; +SELECT 'Repositories: ' || CASE WHEN COUNT(*) > 0 THEN 'PASS (' || COUNT(*) || ' repos)' ELSE 'WARN (empty)' END FROM repository; +SELECT 'Secrets: PASS (' || COUNT(*) || ' secrets)' FROM secret; +SELECT 'Runners: PASS (' || COUNT(*) || ' runners)' FROM action_runner; +SELECT 'Variables: PASS (' || COUNT(*) || ' variables)' FROM action_variable; +VERIFY_SQL + +# Verify dropped tables are gone +repo_license_exists=$(sqlite3 "$TARGET_DB" "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='repo_license';") +issue_pin_exists=$(sqlite3 "$TARGET_DB" "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='issue_pin';") + +if [[ "$repo_license_exists" -eq 0 ]]; then + log_success "repo_license table: DROPPED" +else + log_warn "repo_license table: STILL EXISTS" +fi + +if [[ "$issue_pin_exists" -eq 0 ]]; then + log_success "issue_pin table: DROPPED" +else + log_warn "issue_pin table: STILL EXISTS" +fi + +# ============================================ +# PHASE 8: Print Next Steps +# ============================================ +echo "" +echo "========================================" +echo -e "${GREEN}Migration complete!${NC}" +echo "========================================" +echo "" +echo "Data copied to: $TARGET_DATA" +echo "Database schema rolled back to version 304" +echo "" +echo "Next steps:" +echo "" +echo "1. Update NixOS configuration:" +echo " - Create hosts/fw/modules/forgejo.nix based on gitea.nix" +echo " - Change services.gitea to services.forgejo" +echo " - Update bind mount paths in container config" +echo " - Update runner configuration for Forgejo" +echo "" +echo "2. Deploy:" +echo " nixos-rebuild switch" +echo "" +echo "3. Monitor first startup:" +echo " journalctl -u container@git -f" +echo "" +echo "4. Verify functionality:" +echo " [ ] Forgejo starts without errors" +echo " [ ] Login via OpenID (auth.cloonar.com)" +echo " [ ] All repositories visible" +echo " [ ] Can push/pull to repositories" +echo " [ ] CI/CD runners connect" +echo " [ ] Workflow with secrets runs" +echo " [ ] Packages registry accessible" +echo "" +echo -e "${YELLOW}ROLLBACK:${NC} If anything fails, original Gitea data is untouched." +echo "Just revert NixOS config and restart Gitea container." +echo "========================================"