1
0

gogs.nix 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. { config
  2. , lib
  3. , pkgs
  4. , ... }:
  5. with lib;
  6. let
  7. cfg = config.services.gogs;
  8. iniFormat = pkgs.formats.ini {};
  9. defaultConfig = lib.recursiveUpdate {
  10. server.RUN_USER = cfg.user;
  11. security.INSTALL_LOCK = true;
  12. database.TYPE = "sqlite3";
  13. database.PATH = "${cfg.stateDir}/data/gogs.db";
  14. } cfg.settings;
  15. configFile = iniFormat.generate "gogs.ini" defaultConfig;
  16. themeVariants = {
  17. dark = "kristuff.gogs.dark.min.css";
  18. dark-blue = "kristuff.gogs.dark-accent-blue.min.css";
  19. dark-green = "kristuff.gogs.dark-accent-green.min.css";
  20. dark-magenta = "kristuff.gogs.dark-accent-magenta.min.css";
  21. dark-orange = "kristuff.gogs.dark-accent-orange.min.css";
  22. dark-red = "kristuff.gogs.dark-accent-red.min.css";
  23. dark-yellow = "kristuff.gogs.dark-accent-yellow.min.css";
  24. };
  25. themeFile = if cfg.theme == null then null
  26. else if builtins.hasAttr cfg.theme themeVariants
  27. then "${pkgs.gogs-themes}/dist/${themeVariants.${cfg.theme}}"
  28. else cfg.theme;
  29. themeSetupScript = pkgs.writeShellScript "gogs-theme-setup" ''
  30. mkdir -p ${cfg.stateDir}/custom/public/css ${cfg.stateDir}/custom/templates/inject
  31. cp -f ${themeFile} ${cfg.stateDir}/custom/public/css/theme.css
  32. cat > ${cfg.stateDir}/custom/templates/inject/head.tmpl << 'EOF'
  33. <link rel="stylesheet" href="{{AppSubURL}}/css/theme.css">
  34. EOF
  35. '';
  36. in {
  37. options.services.gogs = {
  38. enable = lib.mkEnableOption "Gogs Git service";
  39. package = lib.mkPackageOption pkgs "gogs" { };
  40. user = lib.mkOption {
  41. type = lib.types.str;
  42. default = "git";
  43. description = "User account under which Gogs runs.";
  44. };
  45. group = lib.mkOption {
  46. type = lib.types.str;
  47. default = "git";
  48. description = "Group under which Gogs runs.";
  49. };
  50. stateDir = lib.mkOption {
  51. type = lib.types.str;
  52. default = "/var/lib/gogs";
  53. description = "Persistent data directory.";
  54. };
  55. environmentFile = lib.mkOption {
  56. type = lib.types.nullOr lib.types.path;
  57. default = null;
  58. description = ''
  59. File containing environment variables to pass to the Gogs service,
  60. formatted as VARIABLE=VALUE per line. Values set here are merged into
  61. the service's environment and can be used to pass secrets (e.g.
  62. database passwords) without putting them in the Nix store.
  63. '';
  64. };
  65. theme = lib.mkOption {
  66. type = lib.types.nullOr (
  67. lib.types.either (lib.types.enum [
  68. "dark"
  69. "dark-blue"
  70. "dark-green"
  71. "dark-magenta"
  72. "dark-orange"
  73. "dark-red"
  74. "dark-yellow"
  75. ]) lib.types.path
  76. );
  77. default = null;
  78. example = "dark-blue";
  79. description = ''
  80. Dark theme for Gogs. Can be:
  81. - A string selecting a built-in variant from pkgs.gogs-themes:
  82. dark, dark-blue, dark-green, dark-magenta, dark-orange, dark-red, dark-yellow.
  83. - A path or package pointing to a custom CSS file.
  84. Set to null to disable theming.
  85. '';
  86. };
  87. adminUser = lib.mkOption {
  88. type = lib.types.nullOr (lib.types.submodule {
  89. options = {
  90. name = lib.mkOption {
  91. type = lib.types.str;
  92. description = "Admin username.";
  93. };
  94. email = lib.mkOption {
  95. type = lib.types.str;
  96. description = "Admin email address.";
  97. };
  98. passwordFile = lib.mkOption {
  99. type = lib.types.path;
  100. description = ''
  101. File containing the admin password. Must be readable by root.
  102. Recommended to use an age secret managed by agenix.
  103. '';
  104. };
  105. };
  106. });
  107. default = null;
  108. example = {
  109. email = "[email protected]";
  110. passwordFile = "/run/secrets/gogs-admin-password";
  111. };
  112. description = ''
  113. Admin user to create on first startup. Uses gogs admin create-user
  114. via ExecStartPost. Idempotent — silently skips if the user already
  115. exists. Set to null to skip.
  116. '';
  117. };
  118. settings = lib.mkOption {
  119. type = iniFormat.type;
  120. default = { };
  121. example = lib.literalExpression ''
  122. {
  123. server = {
  124. DOMAIN = "git.example.com";
  125. ROOT_URL = "https://git.example.com/";
  126. HTTP_PORT = 3000;
  127. };
  128. database = {
  129. TYPE = "sqlite3";
  130. PATH = "/var/lib/gogs/data/gogs.db";
  131. };
  132. }
  133. '';
  134. description = ''
  135. Settings written to app.ini.
  136. See:
  137. https://gogs.io/docs/advanced/configuration_cheat_sheet
  138. '';
  139. };
  140. };
  141. config = mkIf cfg.enable {
  142. users.users.${cfg.user} = {
  143. isSystemUser = true;
  144. group = cfg.group;
  145. home = cfg.stateDir;
  146. createHome = true;
  147. };
  148. users.groups.${cfg.group} = { };
  149. systemd.tmpfiles.rules = [
  150. "d ${cfg.stateDir} 0750 ${cfg.user} ${cfg.group} -"
  151. "d ${cfg.stateDir}/repositories 0750 ${cfg.user} ${cfg.group} -"
  152. "d ${cfg.stateDir}/data 0750 ${cfg.user} ${cfg.group} -"
  153. "d ${cfg.stateDir}/log 0750 ${cfg.user} ${cfg.group} -"
  154. "d ${cfg.stateDir}/custom/public/css 0755 ${cfg.user} ${cfg.group} -"
  155. "d ${cfg.stateDir}/custom/templates/inject 0755 ${cfg.user} ${cfg.group} -"
  156. ];
  157. systemd.services.gogs = {
  158. description = "Gogs Git Service";
  159. after = [ "network.target" ];
  160. wantedBy = [ "multi-user.target" ];
  161. environment = {
  162. GOGS_WORK_DIR = cfg.stateDir;
  163. GOGS_CUSTOM = "${cfg.stateDir}/custom";
  164. };
  165. serviceConfig = {
  166. Type = "simple";
  167. User = cfg.user;
  168. Group = cfg.group;
  169. EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
  170. StateDirectory = "gogs";
  171. StateDirectoryMode = "0750";
  172. WorkingDirectory = cfg.stateDir;
  173. ExecStart =
  174. "${getExe cfg.package} web --config ${configFile}";
  175. ExecStartPre = lib.mkIf (themeFile != null) [
  176. "+${themeSetupScript}"
  177. ];
  178. Restart = "on-failure";
  179. NoNewPrivileges = true;
  180. PrivateTmp = true;
  181. ProtectSystem = "strict";
  182. ProtectHome = true;
  183. };
  184. postStart = lib.mkIf (cfg.adminUser != null) ''
  185. ${lib.getExe cfg.package} admin create-user \
  186. --name ${lib.escapeShellArg cfg.adminUser.name} \
  187. --password "$(cat ${cfg.adminUser.passwordFile})" \
  188. --email ${lib.escapeShellArg cfg.adminUser.email} \
  189. --admin \
  190. --config ${configFile} \
  191. >/dev/null 2>&1 || true
  192. '';
  193. };
  194. };
  195. }