]> Git — Sourcephile - sourcephile-nix.git/blob - nixpkgs/patches/sourcehut.diff
logrotate: rotate /var/log/{b,w}tmp and nginx
[sourcephile-nix.git] / nixpkgs / patches / sourcehut.diff
1 diff --git a/nixos/lib/make-options-doc/options-to-docbook.xsl b/nixos/lib/make-options-doc/options-to-docbook.xsl
2 index b9ac2645051..b286f7b5e2c 100644
3 --- a/nixos/lib/make-options-doc/options-to-docbook.xsl
4 +++ b/nixos/lib/make-options-doc/options-to-docbook.xsl
5 @@ -20,7 +20,7 @@
6 <title>Configuration Options</title>
7 <variablelist xml:id="configuration-variable-list">
8 <xsl:for-each select="attrs">
9 - <xsl:variable name="id" select="concat('opt-', str:replace(str:replace(str:replace(attr[@name = 'name']/string/@value, '*', '_'), '&lt;', '_'), '>', '_'))" />
10 + <xsl:variable name="id" select="concat('opt-', str:replace(str:replace(str:replace(str:replace(attr[@name = 'name']/string/@value, '*', '_'), '&lt;', '_'), '>', '_'), ':', '_'))" />
11 <varlistentry>
12 <term xlink:href="#{$id}">
13 <xsl:attribute name="xml:id"><xsl:value-of select="$id"/></xsl:attribute>
14 diff --git a/nixos/modules/services/misc/sourcehut/builds.nix b/nixos/modules/services/misc/sourcehut/builds.nix
15 deleted file mode 100644
16 index f806e8c51b9..00000000000
17 --- a/nixos/modules/services/misc/sourcehut/builds.nix
18 +++ /dev/null
19 @@ -1,234 +0,0 @@
20 -{ config, lib, pkgs, ... }:
21 -
22 -with lib;
23 -let
24 - cfg = config.services.sourcehut;
25 - scfg = cfg.builds;
26 - rcfg = config.services.redis;
27 - iniKey = "builds.sr.ht";
28 -
29 - drv = pkgs.sourcehut.buildsrht;
30 -in
31 -{
32 - options.services.sourcehut.builds = {
33 - user = mkOption {
34 - type = types.str;
35 - default = "buildsrht";
36 - description = ''
37 - User for builds.sr.ht.
38 - '';
39 - };
40 -
41 - port = mkOption {
42 - type = types.port;
43 - default = 5002;
44 - description = ''
45 - Port on which the "builds" module should listen.
46 - '';
47 - };
48 -
49 - database = mkOption {
50 - type = types.str;
51 - default = "builds.sr.ht";
52 - description = ''
53 - PostgreSQL database name for builds.sr.ht.
54 - '';
55 - };
56 -
57 - statePath = mkOption {
58 - type = types.path;
59 - default = "${cfg.statePath}/buildsrht";
60 - description = ''
61 - State path for builds.sr.ht.
62 - '';
63 - };
64 -
65 - enableWorker = mkOption {
66 - type = types.bool;
67 - default = false;
68 - description = ''
69 - Run workers for builds.sr.ht.
70 - '';
71 - };
72 -
73 - images = mkOption {
74 - type = types.attrsOf (types.attrsOf (types.attrsOf types.package));
75 - default = { };
76 - example = lib.literalExpression ''(let
77 - # Pinning unstable to allow usage with flakes and limit rebuilds.
78 - pkgs_unstable = builtins.fetchGit {
79 - url = "https://github.com/NixOS/nixpkgs";
80 - rev = "ff96a0fa5635770390b184ae74debea75c3fd534";
81 - ref = "nixos-unstable";
82 - };
83 - image_from_nixpkgs = pkgs_unstable: (import ("${pkgs.sourcehut.buildsrht}/lib/images/nixos/image.nix") {
84 - pkgs = (import pkgs_unstable {});
85 - });
86 - in
87 - {
88 - nixos.unstable.x86_64 = image_from_nixpkgs pkgs_unstable;
89 - }
90 - )'';
91 - description = ''
92 - Images for builds.sr.ht. Each package should be distro.release.arch and point to a /nix/store/package/root.img.qcow2.
93 - '';
94 - };
95 -
96 - };
97 -
98 - config = with scfg; let
99 - image_dirs = lib.lists.flatten (
100 - lib.attrsets.mapAttrsToList
101 - (distro: revs:
102 - lib.attrsets.mapAttrsToList
103 - (rev: archs:
104 - lib.attrsets.mapAttrsToList
105 - (arch: image:
106 - pkgs.runCommand "buildsrht-images" { } ''
107 - mkdir -p $out/${distro}/${rev}/${arch}
108 - ln -s ${image}/*.qcow2 $out/${distro}/${rev}/${arch}/root.img.qcow2
109 - '')
110 - archs)
111 - revs)
112 - scfg.images);
113 - image_dir_pre = pkgs.symlinkJoin {
114 - name = "builds.sr.ht-worker-images-pre";
115 - paths = image_dirs ++ [
116 - "${pkgs.sourcehut.buildsrht}/lib/images"
117 - ];
118 - };
119 - image_dir = pkgs.runCommand "builds.sr.ht-worker-images" { } ''
120 - mkdir -p $out/images
121 - cp -Lr ${image_dir_pre}/* $out/images
122 - '';
123 - in
124 - lib.mkIf (cfg.enable && elem "builds" cfg.services) {
125 - users = {
126 - users = {
127 - "${user}" = {
128 - isSystemUser = true;
129 - group = user;
130 - extraGroups = lib.optionals cfg.builds.enableWorker [ "docker" ];
131 - description = "builds.sr.ht user";
132 - };
133 - };
134 -
135 - groups = {
136 - "${user}" = { };
137 - };
138 - };
139 -
140 - services.postgresql = {
141 - authentication = ''
142 - local ${database} ${user} trust
143 - '';
144 - ensureDatabases = [ database ];
145 - ensureUsers = [
146 - {
147 - name = user;
148 - ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
149 - }
150 - ];
151 - };
152 -
153 - systemd = {
154 - tmpfiles.rules = [
155 - "d ${statePath} 0755 ${user} ${user} -"
156 - ] ++ (lib.optionals cfg.builds.enableWorker
157 - [ "d ${statePath}/logs 0775 ${user} ${user} - -" ]
158 - );
159 -
160 - services = {
161 - buildsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey
162 - {
163 - after = [ "postgresql.service" "network.target" ];
164 - requires = [ "postgresql.service" ];
165 - wantedBy = [ "multi-user.target" ];
166 -
167 - description = "builds.sr.ht website service";
168 -
169 - serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
170 -
171 - # Hack to bypass this hack: https://git.sr.ht/~sircmpwn/core.sr.ht/tree/master/item/srht-update-profiles#L6
172 - } // { preStart = " "; };
173 -
174 - buildsrht-worker = {
175 - enable = scfg.enableWorker;
176 - after = [ "postgresql.service" "network.target" ];
177 - requires = [ "postgresql.service" ];
178 - wantedBy = [ "multi-user.target" ];
179 - partOf = [ "buildsrht.service" ];
180 - description = "builds.sr.ht worker service";
181 - path = [ pkgs.openssh pkgs.docker ];
182 - preStart = let qemuPackage = pkgs.qemu_kvm;
183 - in ''
184 - if [[ "$(docker images -q qemu:latest 2> /dev/null)" == "" || "$(cat ${statePath}/docker-image-qemu 2> /dev/null || true)" != "${qemuPackage.version}" ]]; then
185 - # Create and import qemu:latest image for docker
186 - ${
187 - pkgs.dockerTools.streamLayeredImage {
188 - name = "qemu";
189 - tag = "latest";
190 - contents = [ qemuPackage ];
191 - }
192 - } | docker load
193 - # Mark down current package version
194 - printf "%s" "${qemuPackage.version}" > ${statePath}/docker-image-qemu
195 - fi
196 - '';
197 - serviceConfig = {
198 - Type = "simple";
199 - User = user;
200 - Group = "nginx";
201 - Restart = "always";
202 - };
203 - serviceConfig.ExecStart = "${pkgs.sourcehut.buildsrht}/bin/builds.sr.ht-worker";
204 - };
205 - };
206 - };
207 -
208 - services.sourcehut.settings = {
209 - # URL builds.sr.ht is being served at (protocol://domain)
210 - "builds.sr.ht".origin = mkDefault "http://builds.${cfg.originBase}";
211 - # Address and port to bind the debug server to
212 - "builds.sr.ht".debug-host = mkDefault "0.0.0.0";
213 - "builds.sr.ht".debug-port = mkDefault port;
214 - # Configures the SQLAlchemy connection string for the database.
215 - "builds.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
216 - # Set to "yes" to automatically run migrations on package upgrade.
217 - "builds.sr.ht".migrate-on-upgrade = mkDefault "yes";
218 - # builds.sr.ht's OAuth client ID and secret for meta.sr.ht
219 - # Register your client at meta.example.org/oauth
220 - "builds.sr.ht".oauth-client-id = mkDefault null;
221 - "builds.sr.ht".oauth-client-secret = mkDefault null;
222 - # The redis connection used for the celery worker
223 - "builds.sr.ht".redis = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/3";
224 - # The shell used for ssh
225 - "builds.sr.ht".shell = mkDefault "runner-shell";
226 - # Register the builds.sr.ht dispatcher
227 - "git.sr.ht::dispatch".${builtins.unsafeDiscardStringContext "${pkgs.sourcehut.buildsrht}/bin/buildsrht-keys"} = mkDefault "${user}:${user}";
228 -
229 - # Location for build logs, images, and control command
230 - } // lib.attrsets.optionalAttrs scfg.enableWorker {
231 - # Default worker stores logs that are accessible via this address:port
232 - "builds.sr.ht::worker".name = mkDefault "127.0.0.1:5020";
233 - "builds.sr.ht::worker".buildlogs = mkDefault "${scfg.statePath}/logs";
234 - "builds.sr.ht::worker".images = mkDefault "${image_dir}/images";
235 - "builds.sr.ht::worker".controlcmd = mkDefault "${image_dir}/images/control";
236 - "builds.sr.ht::worker".timeout = mkDefault "3m";
237 - };
238 -
239 - services.nginx.virtualHosts."logs.${cfg.originBase}" =
240 - if scfg.enableWorker then {
241 - listen = with builtins; let address = split ":" cfg.settings."builds.sr.ht::worker".name;
242 - in [{ addr = elemAt address 0; port = lib.toInt (elemAt address 2); }];
243 - locations."/logs".root = "${scfg.statePath}";
244 - } else { };
245 -
246 - services.nginx.virtualHosts."builds.${cfg.originBase}" = {
247 - forceSSL = true;
248 - locations."/".proxyPass = "http://${cfg.address}:${toString port}";
249 - locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
250 - locations."/static".root = "${pkgs.sourcehut.buildsrht}/${pkgs.sourcehut.python.sitePackages}/buildsrht";
251 - };
252 - };
253 -}
254 diff --git a/nixos/modules/services/misc/sourcehut/default.nix b/nixos/modules/services/misc/sourcehut/default.nix
255 index c84a75b0ca0..1bd21c278e0 100644
256 --- a/nixos/modules/services/misc/sourcehut/default.nix
257 +++ b/nixos/modules/services/misc/sourcehut/default.nix
258 @@ -1,14 +1,90 @@
259 { config, pkgs, lib, ... }:
260 -
261 with lib;
262 let
263 + inherit (config.services) nginx postfix postgresql redis;
264 + inherit (config.users) users groups;
265 cfg = config.services.sourcehut;
266 - cfgIni = cfg.settings;
267 - settingsFormat = pkgs.formats.ini { };
268 + domain = cfg.settings."sr.ht".global-domain;
269 + settingsFormat = pkgs.formats.ini {
270 + listToValue = concatMapStringsSep "," (generators.mkValueStringDefault {});
271 + mkKeyValue = k: v:
272 + if v == null then ""
273 + else generators.mkKeyValueDefault {
274 + mkValueString = v:
275 + if v == true then "yes"
276 + else if v == false then "no"
277 + else generators.mkValueStringDefault {} v;
278 + } "=" k v;
279 + };
280 + configIniOfService = srv: settingsFormat.generate "sourcehut-${srv}-config.ini"
281 + # Each service needs access to only a subset of sections (and secrets).
282 + (filterAttrs (k: v: v != null)
283 + (mapAttrs (section: v:
284 + let srvMatch = builtins.match "^([a-z]*)\\.sr\\.ht(::.*)?$" section; in
285 + if srvMatch == null # Include sections shared by all services
286 + || head srvMatch == srv # Include sections for the service being configured
287 + then v
288 + # Enable Web links and integrations between services.
289 + else if tail srvMatch == [ null ] && elem (head srvMatch) cfg.services
290 + then {
291 + inherit (v) origin;
292 + # mansrht crashes without it
293 + oauth-client-id = v.oauth-client-id or null;
294 + }
295 + # Drop sub-sections of other services
296 + else null)
297 + (recursiveUpdate cfg.settings {
298 + # Those paths are mounted using BindPaths= or BindReadOnlyPaths=
299 + # for services needing access to them.
300 + "builds.sr.ht::worker".buildlogs = "/var/log/sourcehut/buildsrht-worker";
301 + "git.sr.ht".post-update-script = "/usr/bin/gitsrht-update-hook";
302 + "git.sr.ht".repos = "/var/lib/sourcehut/gitsrht/repos";
303 + "hg.sr.ht".changegroup-script = "/usr/bin/hgsrht-hook-changegroup";
304 + "hg.sr.ht".repos = "/var/lib/sourcehut/hgsrht/repos";
305 + # Making this a per service option despite being in a global section,
306 + # so that it uses the redis-server used by the service.
307 + "sr.ht".redis-host = cfg.${srv}.redis.host;
308 + })));
309 + commonServiceSettings = srv: {
310 + origin = mkOption {
311 + description = "URL ${srv}.sr.ht is being served at (protocol://domain)";
312 + type = types.str;
313 + default = "https://${srv}.${domain}";
314 + defaultText = "https://${srv}.example.com";
315 + };
316 + debug-host = mkOption {
317 + description = "Address to bind the debug server to.";
318 + type = with types; nullOr str;
319 + default = null;
320 + };
321 + debug-port = mkOption {
322 + description = "Port to bind the debug server to.";
323 + type = with types; nullOr str;
324 + default = null;
325 + };
326 + connection-string = mkOption {
327 + description = "SQLAlchemy connection string for the database.";
328 + type = types.str;
329 + default = "postgresql:///localhost?user=${srv}srht&host=/run/postgresql";
330 + };
331 + migrate-on-upgrade = mkEnableOption "automatic migrations on package upgrade" // { default = true; };
332 + oauth-client-id = mkOption {
333 + description = "${srv}.sr.ht's OAuth client id for meta.sr.ht.";
334 + type = types.str;
335 + };
336 + oauth-client-secret = mkOption {
337 + description = "${srv}.sr.ht's OAuth client secret for meta.sr.ht.";
338 + type = types.path;
339 + apply = s: "<" + toString s;
340 + };
341 + };
342
343 # Specialized python containing all the modules
344 python = pkgs.sourcehut.python.withPackages (ps: with ps; [
345 gunicorn
346 + eventlet
347 + # For monitoring Celery: sudo -u listssrht celery --app listssrht.process -b redis+socket:///run/redis-sourcehut/redis.sock?virtual_host=5 flower
348 + flower
349 # Sourcehut services
350 srht
351 buildsrht
352 @@ -19,72 +95,37 @@ let
353 listssrht
354 mansrht
355 metasrht
356 + # Not a python package
357 + #pagessrht
358 pastesrht
359 todosrht
360 ]);
361 + mkOptionNullOrStr = description: mkOption {
362 + inherit description;
363 + type = with types; nullOr str;
364 + default = null;
365 + };
366 in
367 {
368 - imports =
369 - [
370 - ./git.nix
371 - ./hg.nix
372 - ./hub.nix
373 - ./todo.nix
374 - ./man.nix
375 - ./meta.nix
376 - ./paste.nix
377 - ./builds.nix
378 - ./lists.nix
379 - ./dispatch.nix
380 - (mkRemovedOptionModule [ "services" "sourcehut" "nginx" "enable" ] ''
381 - The sourcehut module supports `nginx` as a local reverse-proxy by default and doesn't
382 - support other reverse-proxies officially.
383 -
384 - However it's possible to use an alternative reverse-proxy by
385 -
386 - * disabling nginx
387 - * adjusting the relevant settings for server addresses and ports directly
388 -
389 - Further details about this can be found in the `Sourcehut`-section of the NixOS-manual.
390 - '')
391 - ];
392 -
393 options.services.sourcehut = {
394 - enable = mkOption {
395 - type = types.bool;
396 - default = false;
397 - description = ''
398 - Enable sourcehut - git hosting, continuous integration, mailing list, ticket tracking,
399 - task dispatching, wiki and account management services
400 - '';
401 - };
402 + enable = mkEnableOption ''
403 + sourcehut - git hosting, continuous integration, mailing list, ticket tracking,
404 + task dispatching, wiki and account management services
405 + '';
406
407 services = mkOption {
408 - type = types.nonEmptyListOf (types.enum [ "builds" "dispatch" "git" "hub" "hg" "lists" "man" "meta" "paste" "todo" ]);
409 - default = [ "man" "meta" "paste" ];
410 - example = [ "builds" "dispatch" "git" "hub" "hg" "lists" "man" "meta" "paste" "todo" ];
411 + type = with types; listOf (enum
412 + [ "builds" "dispatch" "git" "hg" "hub" "lists" "man" "meta" "pages" "paste" "todo" ]);
413 + defaultText = "locally enabled services";
414 description = ''
415 - Services to enable on the sourcehut network.
416 + Services that may be displayed as links in the title bar of the Web interface.
417 '';
418 };
419
420 - originBase = mkOption {
421 + listenAddress = mkOption {
422 type = types.str;
423 - default = with config.networking; hostName + lib.optionalString (domain != null) ".${domain}";
424 - defaultText = literalExpression ''
425 - with config.networking; hostName + optionalString (domain != null) ".''${domain}"
426 - '';
427 - description = ''
428 - Host name used by reverse-proxy and for default settings. Will host services at git."''${originBase}". For example: git.sr.ht
429 - '';
430 - };
431 -
432 - address = mkOption {
433 - type = types.str;
434 - default = "127.0.0.1";
435 - description = ''
436 - Address to bind to.
437 - '';
438 + default = "localhost";
439 + description = "Address to bind to.";
440 };
441
442 python = mkOption {
443 @@ -97,105 +138,1247 @@ in
444 '';
445 };
446
447 - statePath = mkOption {
448 - type = types.path;
449 - default = "/var/lib/sourcehut";
450 - description = ''
451 - Root state path for the sourcehut network. If left as the default value
452 - this directory will automatically be created before the sourcehut server
453 - starts, otherwise the sysadmin is responsible for ensuring the
454 - directory exists with appropriate ownership and permissions.
455 - '';
456 + minio = {
457 + enable = mkEnableOption ''local minio integration'';
458 + };
459 +
460 + nginx = {
461 + enable = mkEnableOption ''local nginx integration'';
462 + virtualHost = mkOption {
463 + type = types.attrs;
464 + default = {};
465 + description = "Virtual-host configuration merged with all Sourcehut's virtual-hosts.";
466 + };
467 + };
468 +
469 + postfix = {
470 + enable = mkEnableOption ''local postfix integration'';
471 + };
472 +
473 + postgresql = {
474 + enable = mkEnableOption ''local postgresql integration'';
475 + };
476 +
477 + redis = {
478 + enable = mkEnableOption ''local redis integration in a dedicated redis-server'';
479 };
480
481 settings = mkOption {
482 type = lib.types.submodule {
483 freeformType = settingsFormat.type;
484 + options."sr.ht" = {
485 + global-domain = mkOption {
486 + description = "Global domain name.";
487 + type = types.str;
488 + example = "example.com";
489 + };
490 + environment = mkOption {
491 + description = "Values other than \"production\" adds a banner to each page.";
492 + type = types.enum [ "development" "production" ];
493 + default = "development";
494 + };
495 + network-key = mkOption {
496 + description = ''
497 + An absolute file path (which should be outside the Nix-store)
498 + to a secret key to encrypt internal messages with. Use <code>srht-keygen network</code> to
499 + generate this key. It must be consistent between all services and nodes.
500 + '';
501 + type = types.path;
502 + apply = s: "<" + toString s;
503 + };
504 + owner-email = mkOption {
505 + description = "Owner's email.";
506 + type = types.str;
507 + default = "contact@example.com";
508 + };
509 + owner-name = mkOption {
510 + description = "Owner's name.";
511 + type = types.str;
512 + default = "John Doe";
513 + };
514 + site-blurb = mkOption {
515 + description = "Blurb for your site.";
516 + type = types.str;
517 + default = "the hacker's forge";
518 + };
519 + site-info = mkOption {
520 + description = "The top-level info page for your site.";
521 + type = types.str;
522 + default = "https://sourcehut.org";
523 + };
524 + service-key = mkOption {
525 + description = ''
526 + An absolute file path (which should be outside the Nix-store)
527 + to a key used for encrypting session cookies. Use <code>srht-keygen service</code> to
528 + generate the service key. This must be shared between each node of the same
529 + service (e.g. git1.sr.ht and git2.sr.ht), but different services may use
530 + different keys. If you configure all of your services with the same
531 + config.ini, you may use the same service-key for all of them.
532 + '';
533 + type = types.path;
534 + apply = s: "<" + toString s;
535 + };
536 + site-name = mkOption {
537 + description = "The name of your network of sr.ht-based sites.";
538 + type = types.str;
539 + default = "sourcehut";
540 + };
541 + source-url = mkOption {
542 + description = "The source code for your fork of sr.ht.";
543 + type = types.str;
544 + default = "https://git.sr.ht/~sircmpwn/srht";
545 + };
546 + };
547 + options.mail = {
548 + smtp-host = mkOptionNullOrStr "Outgoing SMTP host.";
549 + smtp-port = mkOption {
550 + description = "Outgoing SMTP port.";
551 + type = with types; nullOr port;
552 + default = null;
553 + };
554 + smtp-user = mkOptionNullOrStr "Outgoing SMTP user.";
555 + smtp-password = mkOptionNullOrStr "Outgoing SMTP password.";
556 + smtp-from = mkOptionNullOrStr "Outgoing SMTP FROM.";
557 + error-to = mkOptionNullOrStr "Address receiving application exceptions";
558 + error-from = mkOptionNullOrStr "Address sending application exceptions";
559 + pgp-privkey = mkOptionNullOrStr ''
560 + An absolute file path (which should be outside the Nix-store)
561 + to an OpenPGP private key.
562 +
563 + Your PGP key information (DO NOT mix up pub and priv here)
564 + You must remove the password from your secret key, if present.
565 + You can do this with <code>gpg --edit-key [key-id]</code>,
566 + then use the <code>passwd</code> command and do not enter a new password.
567 + '';
568 + pgp-pubkey = mkOptionNullOrStr "OpenPGP public key.";
569 + pgp-key-id = mkOptionNullOrStr "OpenPGP key identifier.";
570 + };
571 + options.objects = {
572 + s3-upstream = mkOption {
573 + description = "Configure the S3-compatible object storage service.";
574 + type = with types; nullOr str;
575 + default = null;
576 + };
577 + s3-access-key = mkOption {
578 + description = "Access key to the S3-compatible object storage service";
579 + type = with types; nullOr str;
580 + default = null;
581 + };
582 + s3-secret-key = mkOption {
583 + description = ''
584 + An absolute file path (which should be outside the Nix-store)
585 + to the secret key of the S3-compatible object storage service.
586 + '';
587 + type = with types; nullOr path;
588 + default = null;
589 + apply = mapNullable (s: "<" + toString s);
590 + };
591 + };
592 + options.webhooks = {
593 + private-key = mkOption {
594 + description = ''
595 + An absolute file path (which should be outside the Nix-store)
596 + to a base64-encoded Ed25519 key for signing webhook payloads.
597 + This should be consistent for all *.sr.ht sites,
598 + as this key will be used to verify signatures
599 + from other sites in your network.
600 + Use the <code>srht-keygen webhook</code> command to generate a key.
601 + '';
602 + type = types.path;
603 + apply = s: "<" + toString s;
604 + };
605 + };
606 +
607 + options."dispatch.sr.ht" = commonServiceSettings "dispatch" // {
608 + };
609 + options."dispatch.sr.ht::github" = {
610 + oauth-client-id = mkOptionNullOrStr "OAuth client id.";
611 + oauth-client-secret = mkOptionNullOrStr "OAuth client secret.";
612 + };
613 + options."dispatch.sr.ht::gitlab" = {
614 + enabled = mkEnableOption "GitLab integration";
615 + canonical-upstream = mkOption {
616 + type = types.str;
617 + description = "Canonical upstream.";
618 + default = "gitlab.com";
619 + };
620 + repo-cache = mkOption {
621 + type = types.str;
622 + description = "Repository cache directory.";
623 + default = "./repo-cache";
624 + };
625 + "gitlab.com" = mkOption {
626 + type = with types; nullOr str;
627 + description = "GitLab id and secret.";
628 + default = null;
629 + example = "GitLab:application id:secret";
630 + };
631 + };
632 +
633 + options."builds.sr.ht" = commonServiceSettings "builds" // {
634 + allow-free = mkEnableOption "nonpaying users to submit builds";
635 + redis = mkOption {
636 + description = "The Redis connection used for the Celery worker.";
637 + type = types.str;
638 + default = "redis+socket:///run/redis-sourcehut-buildsrht/redis.sock?virtual_host=2";
639 + };
640 + shell = mkOption {
641 + description = ''
642 + Scripts used to launch on SSH connection.
643 + <literal>/usr/bin/master-shell</literal> on master,
644 + <literal>/usr/bin/runner-shell</literal> on runner.
645 + If master and worker are on the same system
646 + set to <literal>/usr/bin/runner-shell</literal>.
647 + '';
648 + type = types.enum ["/usr/bin/master-shell" "/usr/bin/runner-shell"];
649 + default = "/usr/bin/master-shell";
650 + };
651 + };
652 + options."builds.sr.ht::worker" = {
653 + bind-address = mkOption {
654 + description = ''
655 + HTTP bind address for serving local build information/monitoring.
656 + '';
657 + type = types.str;
658 + default = "localhost:8080";
659 + };
660 + buildlogs = mkOption {
661 + description = "Path to write build logs.";
662 + type = types.str;
663 + default = "/var/log/sourcehut/buildsrht-worker";
664 + };
665 + name = mkOption {
666 + description = ''
667 + Listening address and listening port
668 + of the build runner (with HTTP port if not 80).
669 + '';
670 + type = types.str;
671 + default = "localhost:5020";
672 + };
673 + timeout = mkOption {
674 + description = ''
675 + Max build duration.
676 + See <link xlink:href="https://golang.org/pkg/time/#ParseDuration"/>.
677 + '';
678 + type = types.str;
679 + default = "3m";
680 + };
681 + };
682 +
683 + options."git.sr.ht" = commonServiceSettings "git" // {
684 + outgoing-domain = mkOption {
685 + description = "Outgoing domain.";
686 + type = types.str;
687 + default = "https://git.localhost.localdomain";
688 + };
689 + post-update-script = mkOption {
690 + description = ''
691 + A post-update script which is installed in every git repo.
692 + This setting is propagated to newer and existing repositories.
693 + '';
694 + type = types.path;
695 + default = "${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook";
696 + defaultText = "\${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook";
697 + };
698 + repos = mkOption {
699 + description = ''
700 + Path to git repositories on disk.
701 + If changing the default, you must ensure that
702 + the gitsrht's user as read and write access to it.
703 + '';
704 + type = types.str;
705 + default = "/var/lib/sourcehut/gitsrht/repos";
706 + };
707 + webhooks = mkOption {
708 + description = "The Redis connection used for the webhooks worker.";
709 + type = types.str;
710 + default = "redis+socket:///run/redis-sourcehut-gitsrht/redis.sock?virtual_host=1";
711 + };
712 + };
713 + options."git.sr.ht::api" = {
714 + internal-ipnet = mkOption {
715 + description = ''
716 + Set of IP subnets which are permitted to utilize internal API
717 + authentication. This should be limited to the subnets
718 + from which your *.sr.ht services are running.
719 + See <xref linkend="opt-services.sourcehut.listenAddress"/>.
720 + '';
721 + type = with types; listOf str;
722 + default = [ "127.0.0.0/8" "::1/128" ];
723 + };
724 + };
725 +
726 + options."hg.sr.ht" = commonServiceSettings "hg" // {
727 + changegroup-script = mkOption {
728 + description = ''
729 + A changegroup script which is installed in every mercurial repo.
730 + This setting is propagated to newer and existing repositories.
731 + '';
732 + type = types.str;
733 + default = "${cfg.python}/bin/hgsrht-hook-changegroup";
734 + defaultText = "\${cfg.python}/bin/hgsrht-hook-changegroup";
735 + };
736 + repos = mkOption {
737 + description = ''
738 + Path to mercurial repositories on disk.
739 + If changing the default, you must ensure that
740 + the hgsrht's user as read and write access to it.
741 + '';
742 + type = types.str;
743 + default = "/var/lib/sourcehut/hgsrht/repos";
744 + };
745 + srhtext = mkOptionNullOrStr ''
746 + Path to the srht mercurial extension
747 + (defaults to where the hgsrht code is)
748 + '';
749 + clone_bundle_threshold = mkOption {
750 + description = ".hg/store size (in MB) past which the nightly job generates clone bundles.";
751 + type = types.ints.unsigned;
752 + default = 50;
753 + };
754 + hg_ssh = mkOption {
755 + description = "Path to hg-ssh (if not in $PATH).";
756 + type = types.str;
757 + default = "${pkgs.mercurial}/bin/hg-ssh";
758 + defaultText = "\${pkgs.mercurial}/bin/hg-ssh";
759 + };
760 + webhooks = mkOption {
761 + description = "The Redis connection used for the webhooks worker.";
762 + type = types.str;
763 + default = "redis+socket:///run/redis-sourcehut-hgsrht/redis.sock?virtual_host=1";
764 + };
765 + };
766 +
767 + options."hub.sr.ht" = commonServiceSettings "hub" // {
768 + };
769 +
770 + options."lists.sr.ht" = commonServiceSettings "lists" // {
771 + allow-new-lists = mkEnableOption "Allow creation of new lists.";
772 + notify-from = mkOption {
773 + description = "Outgoing email for notifications generated by users.";
774 + type = types.str;
775 + default = "lists-notify@localhost.localdomain";
776 + };
777 + posting-domain = mkOption {
778 + description = "Posting domain.";
779 + type = types.str;
780 + default = "lists.localhost.localdomain";
781 + };
782 + redis = mkOption {
783 + description = "The Redis connection used for the Celery worker.";
784 + type = types.str;
785 + default = "redis+socket:///run/redis-sourcehut-listssrht/redis.sock?virtual_host=2";
786 + };
787 + webhooks = mkOption {
788 + description = "The Redis connection used for the webhooks worker.";
789 + type = types.str;
790 + default = "redis+socket:///run/redis-sourcehut-listssrht/redis.sock?virtual_host=1";
791 + };
792 + };
793 + options."lists.sr.ht::worker" = {
794 + reject-mimetypes = mkOption {
795 + description = ''
796 + Comma-delimited list of Content-Types to reject. Messages with Content-Types
797 + included in this list are rejected. Multipart messages are always supported,
798 + and each part is checked against this list.
799 +
800 + Uses fnmatch for wildcard expansion.
801 + '';
802 + type = with types; listOf str;
803 + default = ["text/html"];
804 + };
805 + reject-url = mkOption {
806 + description = "Reject URL.";
807 + type = types.str;
808 + default = "https://man.sr.ht/lists.sr.ht/etiquette.md";
809 + };
810 + sock = mkOption {
811 + description = ''
812 + Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
813 + Alternatively, specify IP:PORT and an SMTP server will be run instead.
814 + '';
815 + type = types.str;
816 + default = "/tmp/lists.sr.ht-lmtp.sock";
817 + };
818 + sock-group = mkOption {
819 + description = ''
820 + The lmtp daemon will make the unix socket group-read/write
821 + for users in this group.
822 + '';
823 + type = types.str;
824 + default = "postfix";
825 + };
826 + };
827 +
828 + options."man.sr.ht" = commonServiceSettings "man" // {
829 + };
830 +
831 + options."meta.sr.ht" =
832 + removeAttrs (commonServiceSettings "meta")
833 + ["oauth-client-id" "oauth-client-secret"] // {
834 + api-origin = mkOption {
835 + description = "Origin URL for API, 100 more than web.";
836 + type = types.str;
837 + default = "http://${cfg.listenAddress}:${toString (cfg.meta.port + 100)}";
838 + defaultText = ''http://<xref linkend="opt-services.sourcehut.listenAddress"/>:''${toString (<xref linkend="opt-services.sourcehut.meta.port"/> + 100)}'';
839 + };
840 + webhooks = mkOption {
841 + description = "The Redis connection used for the webhooks worker.";
842 + type = types.str;
843 + default = "redis+socket:///run/redis-sourcehut-metasrht/redis.sock?virtual_host=1";
844 + };
845 + welcome-emails = mkEnableOption "sending stock sourcehut welcome emails after signup";
846 + };
847 + options."meta.sr.ht::api" = {
848 + internal-ipnet = mkOption {
849 + description = ''
850 + Set of IP subnets which are permitted to utilize internal API
851 + authentication. This should be limited to the subnets
852 + from which your *.sr.ht services are running.
853 + See <xref linkend="opt-services.sourcehut.listenAddress"/>.
854 + '';
855 + type = with types; listOf str;
856 + default = [ "127.0.0.0/8" "::1/128" ];
857 + };
858 + };
859 + options."meta.sr.ht::aliases" = mkOption {
860 + description = "Aliases for the client IDs of commonly used OAuth clients.";
861 + type = with types; attrsOf int;
862 + default = {};
863 + example = { "git.sr.ht" = 12345; };
864 + };
865 + options."meta.sr.ht::billing" = {
866 + enabled = mkEnableOption "the billing system";
867 + stripe-public-key = mkOptionNullOrStr "Public key for Stripe. Get your keys at https://dashboard.stripe.com/account/apikeys";
868 + stripe-secret-key = mkOptionNullOrStr ''
869 + An absolute file path (which should be outside the Nix-store)
870 + to a secret key for Stripe. Get your keys at https://dashboard.stripe.com/account/apikeys
871 + '' // {
872 + apply = mapNullable (s: "<" + toString s);
873 + };
874 + };
875 + options."meta.sr.ht::settings" = {
876 + registration = mkEnableOption "public registration";
877 + onboarding-redirect = mkOption {
878 + description = "Where to redirect new users upon registration.";
879 + type = types.str;
880 + default = "https://meta.localhost.localdomain";
881 + };
882 + user-invites = mkOption {
883 + description = ''
884 + How many invites each user is issued upon registration
885 + (only applicable if open registration is disabled).
886 + '';
887 + type = types.ints.unsigned;
888 + default = 5;
889 + };
890 + };
891 +
892 + options."pages.sr.ht" = commonServiceSettings "pages" // {
893 + gemini-certs = mkOption {
894 + description = ''
895 + An absolute file path (which should be outside the Nix-store)
896 + to Gemini certificates.
897 + '';
898 + type = with types; nullOr path;
899 + default = null;
900 + };
901 + max-site-size = mkOption {
902 + description = "Maximum size of any given site (post-gunzip), in MiB.";
903 + type = types.int;
904 + default = 1024;
905 + };
906 + user-domain = mkOption {
907 + description = ''
908 + Configures the user domain, if enabled.
909 + All users are given &lt;username&gt;.this.domain.
910 + '';
911 + type = with types; nullOr str;
912 + default = null;
913 + };
914 + };
915 + options."pages.sr.ht::api" = {
916 + internal-ipnet = mkOption {
917 + description = ''
918 + Set of IP subnets which are permitted to utilize internal API
919 + authentication. This should be limited to the subnets
920 + from which your *.sr.ht services are running.
921 + See <xref linkend="opt-services.sourcehut.listenAddress"/>.
922 + '';
923 + type = with types; listOf str;
924 + default = [ "127.0.0.0/8" "::1/128" ];
925 + };
926 + };
927 +
928 + options."paste.sr.ht" = commonServiceSettings "paste" // {
929 + };
930 +
931 + options."todo.sr.ht" = commonServiceSettings "todo" // {
932 + notify-from = mkOption {
933 + description = "Outgoing email for notifications generated by users.";
934 + type = types.str;
935 + default = "todo-notify@localhost.localdomain";
936 + };
937 + webhooks = mkOption {
938 + description = "The Redis connection used for the webhooks worker.";
939 + type = types.str;
940 + default = "redis+socket:///run/redis-sourcehut-todosrht/redis.sock?virtual_host=1";
941 + };
942 + };
943 + options."todo.sr.ht::mail" = {
944 + posting-domain = mkOption {
945 + description = "Posting domain.";
946 + type = types.str;
947 + default = "todo.localhost.localdomain";
948 + };
949 + sock = mkOption {
950 + description = ''
951 + Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
952 + Alternatively, specify IP:PORT and an SMTP server will be run instead.
953 + '';
954 + type = types.str;
955 + default = "/tmp/todo.sr.ht-lmtp.sock";
956 + };
957 + sock-group = mkOption {
958 + description = ''
959 + The lmtp daemon will make the unix socket group-read/write
960 + for users in this group.
961 + '';
962 + type = types.str;
963 + default = "postfix";
964 + };
965 + };
966 };
967 default = { };
968 description = ''
969 The configuration for the sourcehut network.
970 '';
971 };
972 - };
973
974 - config = mkIf cfg.enable {
975 - assertions =
976 - [
977 - {
978 - assertion = with cfgIni.webhooks; private-key != null && stringLength private-key == 44;
979 - message = "The webhook's private key must be defined and of a 44 byte length.";
980 - }
981 + builds = {
982 + enableWorker = mkEnableOption ''
983 + worker for builds.sr.ht
984
985 - {
986 - assertion = hasAttrByPath [ "meta.sr.ht" "origin" ] cfgIni && cfgIni."meta.sr.ht".origin != null;
987 - message = "meta.sr.ht's origin must be defined.";
988 - }
989 - ];
990 + <warning><para>
991 + For smaller deployments, job runners can be installed alongside the master server
992 + but even if you only build your own software, integration with other services
993 + may cause you to run untrusted builds
994 + (e.g. automatic testing of patches via listssrht).
995 + See <link xlink:href="https://man.sr.ht/builds.sr.ht/configuration.md#security-model"/>.
996 + </para></warning>
997 + '';
998
999 - virtualisation.docker.enable = true;
1000 - environment.etc."sr.ht/config.ini".source =
1001 - settingsFormat.generate "sourcehut-config.ini" (mapAttrsRecursive
1002 - (
1003 - path: v: if v == null then "" else v
1004 - )
1005 - cfg.settings);
1006 + images = mkOption {
1007 + type = with types; attrsOf (attrsOf (attrsOf package));
1008 + default = { };
1009 + example = lib.literalExpression ''(let
1010 + # Pinning unstable to allow usage with flakes and limit rebuilds.
1011 + pkgs_unstable = builtins.fetchGit {
1012 + url = "https://github.com/NixOS/nixpkgs";
1013 + rev = "ff96a0fa5635770390b184ae74debea75c3fd534";
1014 + ref = "nixos-unstable";
1015 + };
1016 + image_from_nixpkgs = (import ("${pkgs.sourcehut.buildsrht}/lib/images/nixos/image.nix") {
1017 + pkgs = (import pkgs_unstable {});
1018 + });
1019 + in
1020 + {
1021 + nixos.unstable.x86_64 = image_from_nixpkgs;
1022 + }
1023 + )'';
1024 + description = ''
1025 + Images for builds.sr.ht. Each package should be distro.release.arch and point to a /nix/store/package/root.img.qcow2.
1026 + '';
1027 + };
1028 + };
1029
1030 - environment.systemPackages = [ pkgs.sourcehut.coresrht ];
1031 + git = {
1032 + package = mkOption {
1033 + type = types.package;
1034 + default = pkgs.git;
1035 + example = literalExpression "pkgs.gitFull";
1036 + description = ''
1037 + Git package for git.sr.ht. This can help silence collisions.
1038 + '';
1039 + };
1040 + fcgiwrap.preforkProcess = mkOption {
1041 + description = "Number of fcgiwrap processes to prefork.";
1042 + type = types.int;
1043 + default = 4;
1044 + };
1045 + };
1046
1047 - # PostgreSQL server
1048 - services.postgresql.enable = mkOverride 999 true;
1049 - # Mail server
1050 - services.postfix.enable = mkOverride 999 true;
1051 - # Cron daemon
1052 - services.cron.enable = mkOverride 999 true;
1053 - # Redis server
1054 - services.redis.enable = mkOverride 999 true;
1055 - services.redis.bind = mkOverride 999 "127.0.0.1";
1056 + hg = {
1057 + package = mkOption {
1058 + type = types.package;
1059 + default = pkgs.mercurial;
1060 + description = ''
1061 + Mercurial package for hg.sr.ht. This can help silence collisions.
1062 + '';
1063 + };
1064 + cloneBundles = mkOption {
1065 + type = types.bool;
1066 + default = false;
1067 + description = ''
1068 + Generate clonebundles (which require more disk space but dramatically speed up cloning large repositories).
1069 + '';
1070 + };
1071 + };
1072
1073 - services.sourcehut.settings = {
1074 - # The name of your network of sr.ht-based sites
1075 - "sr.ht".site-name = mkDefault "sourcehut";
1076 - # The top-level info page for your site
1077 - "sr.ht".site-info = mkDefault "https://sourcehut.org";
1078 - # {{ site-name }}, {{ site-blurb }}
1079 - "sr.ht".site-blurb = mkDefault "the hacker's forge";
1080 - # If this != production, we add a banner to each page
1081 - "sr.ht".environment = mkDefault "development";
1082 - # Contact information for the site owners
1083 - "sr.ht".owner-name = mkDefault "Drew DeVault";
1084 - "sr.ht".owner-email = mkDefault "sir@cmpwn.com";
1085 - # The source code for your fork of sr.ht
1086 - "sr.ht".source-url = mkDefault "https://git.sr.ht/~sircmpwn/srht";
1087 - # A secret key to encrypt session cookies with
1088 - "sr.ht".secret-key = mkDefault null;
1089 - "sr.ht".global-domain = mkDefault null;
1090 -
1091 - # Outgoing SMTP settings
1092 - mail.smtp-host = mkDefault null;
1093 - mail.smtp-port = mkDefault null;
1094 - mail.smtp-user = mkDefault null;
1095 - mail.smtp-password = mkDefault null;
1096 - mail.smtp-from = mkDefault null;
1097 - # Application exceptions are emailed to this address
1098 - mail.error-to = mkDefault null;
1099 - mail.error-from = mkDefault null;
1100 - # Your PGP key information (DO NOT mix up pub and priv here)
1101 - # You must remove the password from your secret key, if present.
1102 - # You can do this with gpg --edit-key [key-id], then use the passwd
1103 - # command and do not enter a new password.
1104 - mail.pgp-privkey = mkDefault null;
1105 - mail.pgp-pubkey = mkDefault null;
1106 - mail.pgp-key-id = mkDefault null;
1107 -
1108 - # base64-encoded Ed25519 key for signing webhook payloads. This should be
1109 - # consistent for all *.sr.ht sites, as we'll use this key to verify signatures
1110 - # from other sites in your network.
1111 - #
1112 - # Use the srht-webhook-keygen command to generate a key.
1113 - webhooks.private-key = mkDefault null;
1114 + lists = {
1115 + process = {
1116 + extraArgs = mkOption {
1117 + type = with types; listOf str;
1118 + default = [ "--loglevel DEBUG" "--pool eventlet" "--without-heartbeat" ];
1119 + description = "Extra arguments passed to the Celery responsible for processing mails.";
1120 + };
1121 + celeryConfig = mkOption {
1122 + type = types.lines;
1123 + default = "";
1124 + description = "Content of the <literal>celeryconfig.py</literal> used by the Celery of <literal>listssrht-process</literal>.";
1125 + };
1126 + };
1127 };
1128 };
1129 +
1130 + config = mkIf cfg.enable (mkMerge [
1131 + {
1132 + environment.systemPackages = [ pkgs.sourcehut.coresrht ];
1133 +
1134 + services.sourcehut.settings = {
1135 + "git.sr.ht".outgoing-domain = mkDefault "https://git.${domain}";
1136 + "lists.sr.ht".notify-from = mkDefault "lists-notify@${domain}";
1137 + "lists.sr.ht".posting-domain = mkDefault "lists.${domain}";
1138 + "meta.sr.ht::settings".onboarding-redirect = mkDefault "https://meta.${domain}";
1139 + "todo.sr.ht".notify-from = mkDefault "todo-notify@${domain}";
1140 + "todo.sr.ht::mail".posting-domain = mkDefault "todo.${domain}";
1141 + };
1142 + }
1143 + (mkIf cfg.postgresql.enable {
1144 + assertions = [
1145 + { assertion = postgresql.enable;
1146 + message = "postgresql must be enabled and configured";
1147 + }
1148 + ];
1149 + })
1150 + (mkIf cfg.postfix.enable {
1151 + assertions = [
1152 + { assertion = postfix.enable;
1153 + message = "postfix must be enabled and configured";
1154 + }
1155 + ];
1156 + # Needed for sharing the LMTP sockets with JoinsNamespaceOf=
1157 + systemd.services.postfix.serviceConfig.PrivateTmp = true;
1158 + })
1159 + (mkIf cfg.redis.enable {
1160 + services.redis.vmOverCommit = mkDefault true;
1161 + })
1162 + (mkIf cfg.nginx.enable {
1163 + assertions = [
1164 + { assertion = nginx.enable;
1165 + message = "nginx must be enabled and configured";
1166 + }
1167 + ];
1168 + # For proxyPass= in virtual-hosts for Sourcehut services.
1169 + services.nginx.recommendedProxySettings = mkDefault true;
1170 + })
1171 + (mkIf (cfg.builds.enable || cfg.git.enable || cfg.hg.enable) {
1172 + services.openssh = {
1173 + # Note that sshd will continue to honor AuthorizedKeysFile.
1174 + # Note that you may want automatically rotate
1175 + # or link to /dev/null the following log files:
1176 + # - /var/log/gitsrht-dispatch
1177 + # - /var/log/{build,git,hg}srht-keys
1178 + # - /var/log/{git,hg}srht-shell
1179 + # - /var/log/gitsrht-update-hook
1180 + authorizedKeysCommand = ''/etc/ssh/sourcehut/subdir/srht-dispatch "%u" "%h" "%t" "%k"'';
1181 + # srht-dispatch will setuid/setgid according to [git.sr.ht::dispatch]
1182 + authorizedKeysCommandUser = "root";
1183 + extraConfig = ''
1184 + PermitUserEnvironment SRHT_*
1185 + '';
1186 + };
1187 + environment.etc."ssh/sourcehut/config.ini".source =
1188 + settingsFormat.generate "sourcehut-dispatch-config.ini"
1189 + (filterAttrs (k: v: k == "git.sr.ht::dispatch")
1190 + cfg.settings);
1191 + environment.etc."ssh/sourcehut/subdir/srht-dispatch" = {
1192 + # sshd_config(5): The program must be owned by root, not writable by group or others
1193 + mode = "0755";
1194 + source = pkgs.writeShellScript "srht-dispatch" ''
1195 + set -e
1196 + cd /etc/ssh/sourcehut/subdir
1197 + ${cfg.python}/bin/gitsrht-dispatch "$@"
1198 + '';
1199 + };
1200 + systemd.services.sshd = {
1201 + #path = optional cfg.git.enable [ cfg.git.package ];
1202 + serviceConfig = {
1203 + BindReadOnlyPaths =
1204 + # Note that those /usr/bin/* paths are hardcoded in multiple places in *.sr.ht,
1205 + # for instance to get the user from the [git.sr.ht::dispatch] settings.
1206 + # *srht-keys needs to:
1207 + # - access a redis-server in [sr.ht] redis-host,
1208 + # - access the PostgreSQL server in [*.sr.ht] connection-string,
1209 + # - query metasrht-api (through the HTTP API).
1210 + # Using this has the side effect of creating empty files in /usr/bin/
1211 + optionals cfg.builds.enable [
1212 + "${pkgs.writeShellScript "buildsrht-keys-wrapper" ''
1213 + set -e
1214 + cd /run/sourcehut/buildsrht/subdir
1215 + set -x
1216 + exec -a "$0" ${pkgs.sourcehut.buildsrht}/bin/buildsrht-keys "$@"
1217 + ''}:/usr/bin/buildsrht-keys"
1218 + "${pkgs.sourcehut.buildsrht}/bin/master-shell:/usr/bin/master-shell"
1219 + "${pkgs.sourcehut.buildsrht}/bin/runner-shell:/usr/bin/runner-shell"
1220 + ] ++
1221 + optionals cfg.git.enable [
1222 + # /path/to/gitsrht-keys calls /path/to/gitsrht-shell,
1223 + # or [git.sr.ht] shell= if set.
1224 + "${pkgs.writeShellScript "gitsrht-keys-wrapper" ''
1225 + set -e
1226 + cd /run/sourcehut/gitsrht/subdir
1227 + set -x
1228 + exec -a "$0" ${pkgs.sourcehut.gitsrht}/bin/gitsrht-keys "$@"
1229 + ''}:/usr/bin/gitsrht-keys"
1230 + "${pkgs.writeShellScript "gitsrht-shell-wrapper" ''
1231 + set -e
1232 + cd /run/sourcehut/gitsrht/subdir
1233 + set -x
1234 + exec -a "$0" ${pkgs.sourcehut.gitsrht}/bin/gitsrht-shell "$@"
1235 + ''}:/usr/bin/gitsrht-shell"
1236 + "${pkgs.writeShellScript "gitsrht-update-hook" ''
1237 + set -e
1238 + test -e "''${PWD%/*}"/config.ini ||
1239 + # Git hooks are run relative to their repository's directory,
1240 + # but gitsrht-update-hook looks up ../config.ini
1241 + ln -s /run/sourcehut/gitsrht/config.ini "''${PWD%/*}"/config.ini
1242 + # hooks/post-update calls /usr/bin/gitsrht-update-hook as hooks/stage-3
1243 + # but this wrapper being a bash script, it overrides $0 with /usr/bin/gitsrht-update-hook
1244 + # hence this hack to put hooks/stage-3 back into gitsrht-update-hook's $0
1245 + if test "''${STAGE3:+set}"
1246 + then
1247 + set -x
1248 + exec -a hooks/stage-3 ${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook "$@"
1249 + else
1250 + export STAGE3=set
1251 + set -x
1252 + exec -a "$0" ${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook "$@"
1253 + fi
1254 + ''}:/usr/bin/gitsrht-update-hook"
1255 + ] ++
1256 + optionals cfg.hg.enable [
1257 + # /path/to/hgsrht-keys calls /path/to/hgsrht-shell,
1258 + # or [hg.sr.ht] shell= if set.
1259 + "${pkgs.writeShellScript "hgsrht-keys-wrapper" ''
1260 + set -e
1261 + cd /run/sourcehut/hgsrht/subdir
1262 + set -x
1263 + exec -a "$0" ${pkgs.sourcehut.hgsrht}/bin/hgsrht-keys "$@"
1264 + ''}:/usr/bin/hgsrht-keys"
1265 + "${pkgs.writeShellScript "hgsrht-shell-wrapper" ''
1266 + set -e
1267 + cd /run/sourcehut/hgsrht/subdir
1268 + set -x
1269 + exec -a "$0" ${pkgs.sourcehut.hgsrht}/bin/hgsrht-shell "$@"
1270 + ''}:/usr/bin/hgsrht-shell"
1271 + # Mercurial's changegroup hooks are run relative to their repository's directory,
1272 + # but hgsrht-hook-changegroup looks up ./config.ini
1273 + "${pkgs.writeShellScript "hgsrht-hook-changegroup" ''
1274 + set -e
1275 + test -e "''$PWD"/config.ini ||
1276 + ln -s /run/sourcehut/hgsrht/config.ini "''$PWD"/config.ini
1277 + set -x
1278 + exec -a "$0" ${cfg.python}/bin/hgsrht-hook-changegroup "$@"
1279 + ''}:/usr/bin/hgsrht-hook-changegroup"
1280 + ];
1281 + };
1282 + };
1283 + })
1284 + ]);
1285 +
1286 + imports = [
1287 +
1288 + (import ./service.nix "builds" {
1289 + inherit configIniOfService;
1290 + srvsrht = "buildsrht";
1291 + port = 5002;
1292 + # TODO: a celery worker on the master and worker are apparently needed
1293 + extraServices.buildsrht-worker = let
1294 + qemuPackage = pkgs.qemu_kvm;
1295 + serviceName = "buildsrht-worker";
1296 + statePath = "/var/lib/sourcehut/${serviceName}";
1297 + in mkIf cfg.builds.enableWorker {
1298 + path = [ pkgs.openssh pkgs.docker ];
1299 + preStart = ''
1300 + set -x
1301 + if test -z "$(docker images -q qemu:latest 2>/dev/null)" \
1302 + || test "$(cat ${statePath}/docker-image-qemu)" != "${qemuPackage.version}"
1303 + then
1304 + # Create and import qemu:latest image for docker
1305 + ${pkgs.dockerTools.streamLayeredImage {
1306 + name = "qemu";
1307 + tag = "latest";
1308 + contents = [ qemuPackage ];
1309 + }} | docker load
1310 + # Mark down current package version
1311 + echo '${qemuPackage.version}' >${statePath}/docker-image-qemu
1312 + fi
1313 + '';
1314 + serviceConfig = {
1315 + ExecStart = "${pkgs.sourcehut.buildsrht}/bin/builds.sr.ht-worker";
1316 + BindPaths = [ cfg.settings."builds.sr.ht::worker".buildlogs ];
1317 + LogsDirectory = [ "sourcehut/${serviceName}" ];
1318 + RuntimeDirectory = [ "sourcehut/${serviceName}/subdir" ];
1319 + StateDirectory = [ "sourcehut/${serviceName}" ];
1320 + TimeoutStartSec = "1800s";
1321 + # builds.sr.ht-worker looks up ../config.ini
1322 + WorkingDirectory = "-"+"/run/sourcehut/${serviceName}/subdir";
1323 + };
1324 + };
1325 + extraConfig = let
1326 + image_dirs = flatten (
1327 + mapAttrsToList (distro: revs:
1328 + mapAttrsToList (rev: archs:
1329 + mapAttrsToList (arch: image:
1330 + pkgs.runCommand "buildsrht-images" { } ''
1331 + mkdir -p $out/${distro}/${rev}/${arch}
1332 + ln -s ${image}/*.qcow2 $out/${distro}/${rev}/${arch}/root.img.qcow2
1333 + ''
1334 + ) archs
1335 + ) revs
1336 + ) cfg.builds.images
1337 + );
1338 + image_dir_pre = pkgs.symlinkJoin {
1339 + name = "builds.sr.ht-worker-images-pre";
1340 + paths = image_dirs;
1341 + # FIXME: not working, apparently because ubuntu/latest is a broken link
1342 + # ++ [ "${pkgs.sourcehut.buildsrht}/lib/images" ];
1343 + };
1344 + image_dir = pkgs.runCommand "builds.sr.ht-worker-images" { } ''
1345 + mkdir -p $out/images
1346 + cp -Lr ${image_dir_pre}/* $out/images
1347 + '';
1348 + in mkMerge [
1349 + {
1350 + users.users.${cfg.builds.user}.shell = pkgs.bash;
1351 +
1352 + virtualisation.docker.enable = true;
1353 +
1354 + services.sourcehut.settings = mkMerge [
1355 + { # Note that git.sr.ht::dispatch is not a typo,
1356 + # gitsrht-dispatch always use this section
1357 + "git.sr.ht::dispatch"."/usr/bin/buildsrht-keys" =
1358 + mkDefault "${cfg.builds.user}:${cfg.builds.group}";
1359 + }
1360 + (mkIf cfg.builds.enableWorker {
1361 + "builds.sr.ht::worker".shell = "/usr/bin/runner-shell";
1362 + "builds.sr.ht::worker".images = mkDefault "${image_dir}/images";
1363 + "builds.sr.ht::worker".controlcmd = mkDefault "${image_dir}/images/control";
1364 + })
1365 + ];
1366 + }
1367 + (mkIf cfg.builds.enableWorker {
1368 + users.groups = {
1369 + docker.members = [ cfg.builds.user ];
1370 + };
1371 + })
1372 + (mkIf (cfg.builds.enableWorker && cfg.nginx.enable) {
1373 + # Allow nginx access to buildlogs
1374 + users.users.${nginx.user}.extraGroups = [ cfg.builds.group ];
1375 + systemd.services.nginx = {
1376 + serviceConfig.BindReadOnlyPaths = [ cfg.settings."builds.sr.ht::worker".buildlogs ];
1377 + };
1378 + services.nginx.virtualHosts."logs.${domain}" = mkMerge [ {
1379 + /* FIXME: is a listen needed?
1380 + listen = with builtins;
1381 + # FIXME: not compatible with IPv6
1382 + let address = split ":" cfg.settings."builds.sr.ht::worker".name; in
1383 + [{ addr = elemAt address 0; port = lib.toInt (elemAt address 2); }];
1384 + */
1385 + locations."/logs/".alias = cfg.settings."builds.sr.ht::worker".buildlogs + "/";
1386 + } cfg.nginx.virtualHost ];
1387 + })
1388 + ];
1389 + })
1390 +
1391 + (import ./service.nix "dispatch" {
1392 + inherit configIniOfService;
1393 + port = 5005;
1394 + })
1395 +
1396 + (import ./service.nix "git" (let
1397 + baseService = {
1398 + path = [ cfg.git.package ];
1399 + serviceConfig.BindPaths = [ "${cfg.settings."git.sr.ht".repos}:/var/lib/sourcehut/gitsrht/repos" ];
1400 + };
1401 + in {
1402 + inherit configIniOfService;
1403 + mainService = mkMerge [ baseService {
1404 + serviceConfig.StateDirectory = [ "sourcehut/gitsrht" "sourcehut/gitsrht/repos" ];
1405 + preStart = mkIf (!versionAtLeast config.system.stateVersion "22.05") (mkBefore ''
1406 + # Fix Git hooks of repositories pre-dating https://github.com/NixOS/nixpkgs/pull/133984
1407 + (
1408 + set +f
1409 + shopt -s nullglob
1410 + for h in /var/lib/sourcehut/gitsrht/repos/~*/*/hooks/{pre-receive,update,post-update}
1411 + do ln -fnsv /usr/bin/gitsrht-update-hook "$h"; done
1412 + )
1413 + '');
1414 + } ];
1415 + port = 5001;
1416 + webhooks = true;
1417 + extraTimers.gitsrht-periodic = {
1418 + service = baseService;
1419 + timerConfig.OnCalendar = ["*:0/20"];
1420 + };
1421 + extraConfig = mkMerge [
1422 + {
1423 + # https://stackoverflow.com/questions/22314298/git-push-results-in-fatal-protocol-error-bad-line-length-character-this
1424 + # Probably could use gitsrht-shell if output is restricted to just parameters...
1425 + users.users.${cfg.git.user}.shell = pkgs.bash;
1426 + services.sourcehut.settings = {
1427 + "git.sr.ht::dispatch"."/usr/bin/gitsrht-keys" =
1428 + mkDefault "${cfg.git.user}:${cfg.git.group}";
1429 + };
1430 + systemd.services.sshd = baseService;
1431 + }
1432 + (mkIf cfg.nginx.enable {
1433 + services.nginx.virtualHosts."git.${domain}" = {
1434 + locations."/authorize" = {
1435 + proxyPass = "http://${cfg.listenAddress}:${toString cfg.git.port}";
1436 + extraConfig = ''
1437 + proxy_pass_request_body off;
1438 + proxy_set_header Content-Length "";
1439 + proxy_set_header X-Original-URI $request_uri;
1440 + '';
1441 + };
1442 + locations."~ ^/([^/]+)/([^/]+)/(HEAD|info/refs|objects/info/.*|git-upload-pack).*$" = {
1443 + root = "/var/lib/sourcehut/gitsrht/repos";
1444 + fastcgiParams = {
1445 + GIT_HTTP_EXPORT_ALL = "";
1446 + GIT_PROJECT_ROOT = "$document_root";
1447 + PATH_INFO = "$uri";
1448 + SCRIPT_FILENAME = "${cfg.git.package}/bin/git-http-backend";
1449 + };
1450 + extraConfig = ''
1451 + auth_request /authorize;
1452 + fastcgi_read_timeout 500s;
1453 + fastcgi_pass unix:/run/gitsrht-fcgiwrap.sock;
1454 + gzip off;
1455 + '';
1456 + };
1457 + };
1458 + systemd.sockets.gitsrht-fcgiwrap = {
1459 + before = [ "nginx.service" ];
1460 + wantedBy = [ "sockets.target" "gitsrht.service" ];
1461 + # This path remains accessible to nginx.service, which has no RootDirectory=
1462 + socketConfig.ListenStream = "/run/gitsrht-fcgiwrap.sock";
1463 + socketConfig.SocketUser = nginx.user;
1464 + socketConfig.SocketMode = "600";
1465 + };
1466 + })
1467 + ];
1468 + extraServices.gitsrht-fcgiwrap = mkIf cfg.nginx.enable {
1469 + serviceConfig = {
1470 + # Socket is passed by gitsrht-fcgiwrap.socket
1471 + ExecStart = "${pkgs.fcgiwrap}/sbin/fcgiwrap -c ${toString cfg.git.fcgiwrap.preforkProcess}";
1472 + # No need for config.ini
1473 + ExecStartPre = mkForce [];
1474 + User = null;
1475 + DynamicUser = true;
1476 + BindReadOnlyPaths = [ "${cfg.settings."git.sr.ht".repos}:/var/lib/sourcehut/gitsrht/repos" ];
1477 + IPAddressDeny = "any";
1478 + InaccessiblePaths = [ "-+/run/postgresql" "-+/run/redis-sourcehut" ];
1479 + PrivateNetwork = true;
1480 + RestrictAddressFamilies = mkForce [ "none" ];
1481 + SystemCallFilter = mkForce [
1482 + "@system-service"
1483 + "~@aio" "~@keyring" "~@memlock" "~@privileged" "~@resources" "~@setuid"
1484 + # @timer is needed for alarm()
1485 + ];
1486 + };
1487 + };
1488 + }))
1489 +
1490 + (import ./service.nix "hg" (let
1491 + baseService = {
1492 + path = [ cfg.hg.package ];
1493 + serviceConfig.BindPaths = [ "${cfg.settings."hg.sr.ht".repos}:/var/lib/sourcehut/hgsrht/repos" ];
1494 + };
1495 + in {
1496 + inherit configIniOfService;
1497 + mainService = mkMerge [ baseService {
1498 + serviceConfig.StateDirectory = [ "sourcehut/hgsrht" "sourcehut/hgsrht/repos" ];
1499 + } ];
1500 + port = 5010;
1501 + webhooks = true;
1502 + extraTimers.hgsrht-periodic = {
1503 + service = baseService;
1504 + timerConfig.OnCalendar = ["*:0/20"];
1505 + };
1506 + extraTimers.hgsrht-clonebundles = mkIf cfg.hg.cloneBundles {
1507 + service = baseService;
1508 + timerConfig.OnCalendar = ["daily"];
1509 + timerConfig.AccuracySec = "1h";
1510 + };
1511 + extraConfig = mkMerge [
1512 + {
1513 + users.users.${cfg.hg.user}.shell = pkgs.bash;
1514 + services.sourcehut.settings = {
1515 + # Note that git.sr.ht::dispatch is not a typo,
1516 + # gitsrht-dispatch always uses this section.
1517 + "git.sr.ht::dispatch"."/usr/bin/hgsrht-keys" =
1518 + mkDefault "${cfg.hg.user}:${cfg.hg.group}";
1519 + };
1520 + systemd.services.sshd = baseService;
1521 + }
1522 + (mkIf cfg.nginx.enable {
1523 + # Allow nginx access to repositories
1524 + users.users.${nginx.user}.extraGroups = [ cfg.hg.group ];
1525 + services.nginx.virtualHosts."hg.${domain}" = {
1526 + locations."/authorize" = {
1527 + proxyPass = "http://${cfg.listenAddress}:${toString cfg.hg.port}";
1528 + extraConfig = ''
1529 + proxy_pass_request_body off;
1530 + proxy_set_header Content-Length "";
1531 + proxy_set_header X-Original-URI $request_uri;
1532 + '';
1533 + };
1534 + # Let clients reach pull bundles. We don't really need to lock this down even for
1535 + # private repos because the bundles are named after the revision hashes...
1536 + # so someone would need to know or guess a SHA value to download anything.
1537 + # TODO: proxyPass to an hg serve service?
1538 + locations."~ ^/[~^][a-z0-9_]+/[a-zA-Z0-9_.-]+/\\.hg/bundles/.*$" = {
1539 + root = "/var/lib/nginx/hgsrht/repos";
1540 + extraConfig = ''
1541 + auth_request /authorize;
1542 + gzip off;
1543 + '';
1544 + };
1545 + };
1546 + systemd.services.nginx = {
1547 + serviceConfig.BindReadOnlyPaths = [ "${cfg.settings."hg.sr.ht".repos}:/var/lib/nginx/hgsrht/repos" ];
1548 + };
1549 + })
1550 + ];
1551 + }))
1552 +
1553 + (import ./service.nix "hub" {
1554 + inherit configIniOfService;
1555 + port = 5014;
1556 + extraConfig = {
1557 + services.nginx = mkIf cfg.nginx.enable {
1558 + virtualHosts."hub.${domain}" = mkMerge [ {
1559 + serverAliases = [ domain ];
1560 + } cfg.nginx.virtualHost ];
1561 + };
1562 + };
1563 + })
1564 +
1565 + (import ./service.nix "lists" (let
1566 + srvsrht = "listssrht";
1567 + in {
1568 + inherit configIniOfService;
1569 + port = 5006;
1570 + webhooks = true;
1571 + # Receive the mail from Postfix and enqueue them into Redis and PostgreSQL
1572 + extraServices.listssrht-lmtp = {
1573 + wants = [ "postfix.service" ];
1574 + unitConfig.JoinsNamespaceOf = optional cfg.postfix.enable "postfix.service";
1575 + serviceConfig.ExecStart = "${cfg.python}/bin/listssrht-lmtp";
1576 + # Avoid crashing: os.chown(sock, os.getuid(), sock_gid)
1577 + serviceConfig.PrivateUsers = mkForce false;
1578 + };
1579 + # Dequeue the mails from Redis and dispatch them
1580 + extraServices.listssrht-process = {
1581 + serviceConfig = {
1582 + preStart = ''
1583 + cp ${pkgs.writeText "${srvsrht}-webhooks-celeryconfig.py" cfg.lists.process.celeryConfig} \
1584 + /run/sourcehut/${srvsrht}-webhooks/celeryconfig.py
1585 + '';
1586 + ExecStart = "${cfg.python}/bin/celery --app listssrht.process worker --hostname listssrht-process@%%h " + concatStringsSep " " cfg.lists.process.extraArgs;
1587 + # Avoid crashing: os.getloadavg()
1588 + ProcSubset = mkForce "all";
1589 + };
1590 + };
1591 + extraConfig = mkIf cfg.postfix.enable {
1592 + users.groups.${postfix.group}.members = [ cfg.lists.user ];
1593 + services.sourcehut.settings."lists.sr.ht::mail".sock-group = postfix.group;
1594 + services.postfix = {
1595 + destination = [ "lists.${domain}" ];
1596 + # FIXME: an accurate recipient list should be queried
1597 + # from the lists.sr.ht PostgreSQL database to avoid backscattering.
1598 + # But usernames are unfortunately not in that database but in meta.sr.ht.
1599 + # Note that two syntaxes are allowed:
1600 + # - ~username/list-name@lists.${domain}
1601 + # - u.username.list-name@lists.${domain}
1602 + localRecipients = [ "@lists.${domain}" ];
1603 + transport = ''
1604 + lists.${domain} lmtp:unix:${cfg.settings."lists.sr.ht::worker".sock}
1605 + '';
1606 + };
1607 + };
1608 + }))
1609 +
1610 + (import ./service.nix "man" {
1611 + inherit configIniOfService;
1612 + port = 5004;
1613 + })
1614 +
1615 + (import ./service.nix "meta" {
1616 + inherit configIniOfService;
1617 + port = 5000;
1618 + webhooks = true;
1619 + extraServices.metasrht-api = {
1620 + serviceConfig.Restart = "always";
1621 + serviceConfig.RestartSec = "2s";
1622 + preStart = "set -x\n" + concatStringsSep "\n\n" (attrValues (mapAttrs (k: s:
1623 + let srvMatch = builtins.match "^([a-z]*)\\.sr\\.ht$" k;
1624 + srv = head srvMatch;
1625 + in
1626 + # Configure client(s) as "preauthorized"
1627 + optionalString (srvMatch != null && cfg.${srv}.enable && ((s.oauth-client-id or null) != null)) ''
1628 + # Configure ${srv}'s OAuth client as "preauthorized"
1629 + ${postgresql.package}/bin/psql '${cfg.settings."meta.sr.ht".connection-string}' \
1630 + -c "UPDATE oauthclient SET preauthorized = true WHERE client_id = '${s.oauth-client-id}'"
1631 + ''
1632 + ) cfg.settings));
1633 + serviceConfig.ExecStart = "${pkgs.sourcehut.metasrht}/bin/metasrht-api -b ${cfg.listenAddress}:${toString (cfg.meta.port + 100)}";
1634 + };
1635 + extraTimers.metasrht-daily.timerConfig = {
1636 + OnCalendar = ["daily"];
1637 + AccuracySec = "1h";
1638 + };
1639 + extraConfig = mkMerge [
1640 + {
1641 + assertions = [
1642 + { assertion = let s = cfg.settings."meta.sr.ht::billing"; in
1643 + s.enabled == "yes" -> (s.stripe-public-key != null && s.stripe-secret-key != null);
1644 + message = "If meta.sr.ht::billing is enabled, the keys must be defined.";
1645 + }
1646 + ];
1647 + environment.systemPackages = optional cfg.meta.enable
1648 + (pkgs.writeShellScriptBin "metasrht-manageuser" ''
1649 + set -eux
1650 + if test "$(${pkgs.coreutils}/bin/id -n -u)" != '${cfg.meta.user}'
1651 + then exec sudo -u '${cfg.meta.user}' "$0" "$@"
1652 + else
1653 + # In order to load config.ini
1654 + if cd /run/sourcehut/metasrht
1655 + then exec ${cfg.python}/bin/metasrht-manageuser "$@"
1656 + else cat <<EOF
1657 + Please run: sudo systemctl start metasrht
1658 + EOF
1659 + exit 1
1660 + fi
1661 + fi
1662 + '');
1663 + }
1664 + (mkIf cfg.nginx.enable {
1665 + services.nginx.virtualHosts."meta.${domain}" = {
1666 + locations."/query" = {
1667 + proxyPass = cfg.settings."meta.sr.ht".api-origin;
1668 + extraConfig = ''
1669 + if ($request_method = 'OPTIONS') {
1670 + add_header 'Access-Control-Allow-Origin' '*';
1671 + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
1672 + add_header 'Access-Control-Allow-Headers' 'User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
1673 + add_header 'Access-Control-Max-Age' 1728000;
1674 + add_header 'Content-Type' 'text/plain; charset=utf-8';
1675 + add_header 'Content-Length' 0;
1676 + return 204;
1677 + }
1678 +
1679 + add_header 'Access-Control-Allow-Origin' '*';
1680 + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
1681 + add_header 'Access-Control-Allow-Headers' 'User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
1682 + add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
1683 + '';
1684 + };
1685 + };
1686 + })
1687 + ];
1688 + })
1689 +
1690 + (import ./service.nix "pages" {
1691 + inherit configIniOfService;
1692 + port = 5112;
1693 + mainService = let
1694 + srvsrht = "pagessrht";
1695 + version = pkgs.sourcehut.${srvsrht}.version;
1696 + stateDir = "/var/lib/sourcehut/${srvsrht}";
1697 + iniKey = "pages.sr.ht";
1698 + in {
1699 + preStart = mkBefore ''
1700 + set -x
1701 + # Use the /run/sourcehut/${srvsrht}/config.ini
1702 + # installed by a previous ExecStartPre= in baseService
1703 + cd /run/sourcehut/${srvsrht}
1704 +
1705 + if test ! -e ${stateDir}/db; then
1706 + ${postgresql.package}/bin/psql '${cfg.settings.${iniKey}.connection-string}' -f ${pkgs.sourcehut.pagessrht}/share/sql/schema.sql
1707 + echo ${version} >${stateDir}/db
1708 + fi
1709 +
1710 + ${optionalString cfg.settings.${iniKey}.migrate-on-upgrade ''
1711 + # Just try all the migrations because they're not linked to the version
1712 + for sql in ${pkgs.sourcehut.pagessrht}/share/sql/migrations/*.sql; do
1713 + ${postgresql.package}/bin/psql '${cfg.settings.${iniKey}.connection-string}' -f "$sql" || true
1714 + done
1715 + ''}
1716 +
1717 + # Disable webhook
1718 + touch ${stateDir}/webhook
1719 + '';
1720 + serviceConfig = {
1721 + ExecStart = mkForce "${pkgs.sourcehut.pagessrht}/bin/pages.sr.ht -b ${cfg.listenAddress}:${toString cfg.pages.port}";
1722 + };
1723 + };
1724 + })
1725 +
1726 + (import ./service.nix "paste" {
1727 + inherit configIniOfService;
1728 + port = 5011;
1729 + })
1730 +
1731 + (import ./service.nix "todo" {
1732 + inherit configIniOfService;
1733 + port = 5003;
1734 + webhooks = true;
1735 + extraServices.todosrht-lmtp = {
1736 + wants = [ "postfix.service" ];
1737 + unitConfig.JoinsNamespaceOf = optional cfg.postfix.enable "postfix.service";
1738 + serviceConfig.ExecStart = "${cfg.python}/bin/todosrht-lmtp";
1739 + # Avoid crashing: os.chown(sock, os.getuid(), sock_gid)
1740 + serviceConfig.PrivateUsers = mkForce false;
1741 + };
1742 + extraConfig = mkIf cfg.postfix.enable {
1743 + users.groups.${postfix.group}.members = [ cfg.todo.user ];
1744 + services.sourcehut.settings."todo.sr.ht::mail".sock-group = postfix.group;
1745 + services.postfix = {
1746 + destination = [ "todo.${domain}" ];
1747 + # FIXME: an accurate recipient list should be queried
1748 + # from the todo.sr.ht PostgreSQL database to avoid backscattering.
1749 + # But usernames are unfortunately not in that database but in meta.sr.ht.
1750 + # Note that two syntaxes are allowed:
1751 + # - ~username/tracker-name@todo.${domain}
1752 + # - u.username.tracker-name@todo.${domain}
1753 + localRecipients = [ "@todo.${domain}" ];
1754 + transport = ''
1755 + todo.${domain} lmtp:unix:${cfg.settings."todo.sr.ht::mail".sock}
1756 + '';
1757 + };
1758 + };
1759 + })
1760 +
1761 + (mkRenamedOptionModule [ "services" "sourcehut" "originBase" ]
1762 + [ "services" "sourcehut" "settings" "sr.ht" "global-domain" ])
1763 + (mkRenamedOptionModule [ "services" "sourcehut" "address" ]
1764 + [ "services" "sourcehut" "listenAddress" ])
1765 +
1766 + ];
1767 +
1768 meta.doc = ./sourcehut.xml;
1769 - meta.maintainers = with maintainers; [ tomberek ];
1770 + meta.maintainers = with maintainers; [ julm tomberek ];
1771 }
1772 diff --git a/nixos/modules/services/misc/sourcehut/dispatch.nix b/nixos/modules/services/misc/sourcehut/dispatch.nix
1773 deleted file mode 100644
1774 index a9db17bebe8..00000000000
1775 --- a/nixos/modules/services/misc/sourcehut/dispatch.nix
1776 +++ /dev/null
1777 @@ -1,125 +0,0 @@
1778 -{ config, lib, pkgs, ... }:
1779 -
1780 -with lib;
1781 -let
1782 - cfg = config.services.sourcehut;
1783 - cfgIni = cfg.settings;
1784 - scfg = cfg.dispatch;
1785 - iniKey = "dispatch.sr.ht";
1786 -
1787 - drv = pkgs.sourcehut.dispatchsrht;
1788 -in
1789 -{
1790 - options.services.sourcehut.dispatch = {
1791 - user = mkOption {
1792 - type = types.str;
1793 - default = "dispatchsrht";
1794 - description = ''
1795 - User for dispatch.sr.ht.
1796 - '';
1797 - };
1798 -
1799 - port = mkOption {
1800 - type = types.port;
1801 - default = 5005;
1802 - description = ''
1803 - Port on which the "dispatch" module should listen.
1804 - '';
1805 - };
1806 -
1807 - database = mkOption {
1808 - type = types.str;
1809 - default = "dispatch.sr.ht";
1810 - description = ''
1811 - PostgreSQL database name for dispatch.sr.ht.
1812 - '';
1813 - };
1814 -
1815 - statePath = mkOption {
1816 - type = types.path;
1817 - default = "${cfg.statePath}/dispatchsrht";
1818 - description = ''
1819 - State path for dispatch.sr.ht.
1820 - '';
1821 - };
1822 - };
1823 -
1824 - config = with scfg; lib.mkIf (cfg.enable && elem "dispatch" cfg.services) {
1825 -
1826 - users = {
1827 - users = {
1828 - "${user}" = {
1829 - isSystemUser = true;
1830 - group = user;
1831 - description = "dispatch.sr.ht user";
1832 - };
1833 - };
1834 -
1835 - groups = {
1836 - "${user}" = { };
1837 - };
1838 - };
1839 -
1840 - services.postgresql = {
1841 - authentication = ''
1842 - local ${database} ${user} trust
1843 - '';
1844 - ensureDatabases = [ database ];
1845 - ensureUsers = [
1846 - {
1847 - name = user;
1848 - ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
1849 - }
1850 - ];
1851 - };
1852 -
1853 - systemd = {
1854 - tmpfiles.rules = [
1855 - "d ${statePath} 0750 ${user} ${user} -"
1856 - ];
1857 -
1858 - services.dispatchsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
1859 - after = [ "postgresql.service" "network.target" ];
1860 - requires = [ "postgresql.service" ];
1861 - wantedBy = [ "multi-user.target" ];
1862 -
1863 - description = "dispatch.sr.ht website service";
1864 -
1865 - serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
1866 - };
1867 - };
1868 -
1869 - services.sourcehut.settings = {
1870 - # URL dispatch.sr.ht is being served at (protocol://domain)
1871 - "dispatch.sr.ht".origin = mkDefault "http://dispatch.${cfg.originBase}";
1872 - # Address and port to bind the debug server to
1873 - "dispatch.sr.ht".debug-host = mkDefault "0.0.0.0";
1874 - "dispatch.sr.ht".debug-port = mkDefault port;
1875 - # Configures the SQLAlchemy connection string for the database.
1876 - "dispatch.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
1877 - # Set to "yes" to automatically run migrations on package upgrade.
1878 - "dispatch.sr.ht".migrate-on-upgrade = mkDefault "yes";
1879 - # dispatch.sr.ht's OAuth client ID and secret for meta.sr.ht
1880 - # Register your client at meta.example.org/oauth
1881 - "dispatch.sr.ht".oauth-client-id = mkDefault null;
1882 - "dispatch.sr.ht".oauth-client-secret = mkDefault null;
1883 -
1884 - # Github Integration
1885 - "dispatch.sr.ht::github".oauth-client-id = mkDefault null;
1886 - "dispatch.sr.ht::github".oauth-client-secret = mkDefault null;
1887 -
1888 - # Gitlab Integration
1889 - "dispatch.sr.ht::gitlab".enabled = mkDefault null;
1890 - "dispatch.sr.ht::gitlab".canonical-upstream = mkDefault "gitlab.com";
1891 - "dispatch.sr.ht::gitlab".repo-cache = mkDefault "./repo-cache";
1892 - # "dispatch.sr.ht::gitlab"."gitlab.com" = mkDefault "GitLab:application id:secret";
1893 - };
1894 -
1895 - services.nginx.virtualHosts."dispatch.${cfg.originBase}" = {
1896 - forceSSL = true;
1897 - locations."/".proxyPass = "http://${cfg.address}:${toString port}";
1898 - locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
1899 - locations."/static".root = "${pkgs.sourcehut.dispatchsrht}/${pkgs.sourcehut.python.sitePackages}/dispatchsrht";
1900 - };
1901 - };
1902 -}
1903 diff --git a/nixos/modules/services/misc/sourcehut/git.nix b/nixos/modules/services/misc/sourcehut/git.nix
1904 deleted file mode 100644
1905 index 2653d77876d..00000000000
1906 --- a/nixos/modules/services/misc/sourcehut/git.nix
1907 +++ /dev/null
1908 @@ -1,215 +0,0 @@
1909 -{ config, lib, pkgs, ... }:
1910 -
1911 -with lib;
1912 -let
1913 - cfg = config.services.sourcehut;
1914 - scfg = cfg.git;
1915 - iniKey = "git.sr.ht";
1916 -
1917 - rcfg = config.services.redis;
1918 - drv = pkgs.sourcehut.gitsrht;
1919 -in
1920 -{
1921 - options.services.sourcehut.git = {
1922 - user = mkOption {
1923 - type = types.str;
1924 - visible = false;
1925 - internal = true;
1926 - readOnly = true;
1927 - default = "git";
1928 - description = ''
1929 - User for git.sr.ht.
1930 - '';
1931 - };
1932 -
1933 - port = mkOption {
1934 - type = types.port;
1935 - default = 5001;
1936 - description = ''
1937 - Port on which the "git" module should listen.
1938 - '';
1939 - };
1940 -
1941 - database = mkOption {
1942 - type = types.str;
1943 - default = "git.sr.ht";
1944 - description = ''
1945 - PostgreSQL database name for git.sr.ht.
1946 - '';
1947 - };
1948 -
1949 - statePath = mkOption {
1950 - type = types.path;
1951 - default = "${cfg.statePath}/gitsrht";
1952 - description = ''
1953 - State path for git.sr.ht.
1954 - '';
1955 - };
1956 -
1957 - package = mkOption {
1958 - type = types.package;
1959 - default = pkgs.git;
1960 - defaultText = literalExpression "pkgs.git";
1961 - example = literalExpression "pkgs.gitFull";
1962 - description = ''
1963 - Git package for git.sr.ht. This can help silence collisions.
1964 - '';
1965 - };
1966 - };
1967 -
1968 - config = with scfg; lib.mkIf (cfg.enable && elem "git" cfg.services) {
1969 - # sshd refuses to run with `Unsafe AuthorizedKeysCommand ... bad ownership or modes for directory /nix/store`
1970 - environment.etc."ssh/gitsrht-dispatch" = {
1971 - mode = "0755";
1972 - text = ''
1973 - #! ${pkgs.stdenv.shell}
1974 - ${cfg.python}/bin/gitsrht-dispatch "$@"
1975 - '';
1976 - };
1977 -
1978 - # Needs this in the $PATH when sshing into the server
1979 - environment.systemPackages = [ cfg.git.package ];
1980 -
1981 - users = {
1982 - users = {
1983 - "${user}" = {
1984 - isSystemUser = true;
1985 - group = user;
1986 - # https://stackoverflow.com/questions/22314298/git-push-results-in-fatal-protocol-error-bad-line-length-character-this
1987 - # Probably could use gitsrht-shell if output is restricted to just parameters...
1988 - shell = pkgs.bash;
1989 - description = "git.sr.ht user";
1990 - };
1991 - };
1992 -
1993 - groups = {
1994 - "${user}" = { };
1995 - };
1996 - };
1997 -
1998 - services = {
1999 - cron.systemCronJobs = [ "*/20 * * * * ${cfg.python}/bin/gitsrht-periodic" ];
2000 - fcgiwrap.enable = true;
2001 -
2002 - openssh.authorizedKeysCommand = ''/etc/ssh/gitsrht-dispatch "%u" "%h" "%t" "%k"'';
2003 - openssh.authorizedKeysCommandUser = "root";
2004 - openssh.extraConfig = ''
2005 - PermitUserEnvironment SRHT_*
2006 - '';
2007 -
2008 - postgresql = {
2009 - authentication = ''
2010 - local ${database} ${user} trust
2011 - '';
2012 - ensureDatabases = [ database ];
2013 - ensureUsers = [
2014 - {
2015 - name = user;
2016 - ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
2017 - }
2018 - ];
2019 - };
2020 - };
2021 -
2022 - systemd = {
2023 - tmpfiles.rules = [
2024 - # /var/log is owned by root
2025 - "f /var/log/git-srht-shell 0644 ${user} ${user} -"
2026 -
2027 - "d ${statePath} 0750 ${user} ${user} -"
2028 - "d ${cfg.settings."${iniKey}".repos} 2755 ${user} ${user} -"
2029 - ];
2030 -
2031 - services = {
2032 - gitsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
2033 - after = [ "redis.service" "postgresql.service" "network.target" ];
2034 - requires = [ "redis.service" "postgresql.service" ];
2035 - wantedBy = [ "multi-user.target" ];
2036 -
2037 - # Needs internally to create repos at the very least
2038 - path = [ pkgs.git ];
2039 - description = "git.sr.ht website service";
2040 -
2041 - serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
2042 - };
2043 -
2044 - gitsrht-webhooks = {
2045 - after = [ "postgresql.service" "network.target" ];
2046 - requires = [ "postgresql.service" ];
2047 - wantedBy = [ "multi-user.target" ];
2048 -
2049 - description = "git.sr.ht webhooks service";
2050 - serviceConfig = {
2051 - Type = "simple";
2052 - User = user;
2053 - Restart = "always";
2054 - };
2055 -
2056 - serviceConfig.ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
2057 - };
2058 - };
2059 - };
2060 -
2061 - services.sourcehut.settings = {
2062 - # URL git.sr.ht is being served at (protocol://domain)
2063 - "git.sr.ht".origin = mkDefault "http://git.${cfg.originBase}";
2064 - # Address and port to bind the debug server to
2065 - "git.sr.ht".debug-host = mkDefault "0.0.0.0";
2066 - "git.sr.ht".debug-port = mkDefault port;
2067 - # Configures the SQLAlchemy connection string for the database.
2068 - "git.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
2069 - # Set to "yes" to automatically run migrations on package upgrade.
2070 - "git.sr.ht".migrate-on-upgrade = mkDefault "yes";
2071 - # The redis connection used for the webhooks worker
2072 - "git.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/1";
2073 -
2074 - # A post-update script which is installed in every git repo.
2075 - "git.sr.ht".post-update-script = mkDefault "${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook";
2076 -
2077 - # git.sr.ht's OAuth client ID and secret for meta.sr.ht
2078 - # Register your client at meta.example.org/oauth
2079 - "git.sr.ht".oauth-client-id = mkDefault null;
2080 - "git.sr.ht".oauth-client-secret = mkDefault null;
2081 - # Path to git repositories on disk
2082 - "git.sr.ht".repos = mkDefault "/var/lib/git";
2083 -
2084 - "git.sr.ht".outgoing-domain = mkDefault "http://git.${cfg.originBase}";
2085 -
2086 - # The authorized keys hook uses this to dispatch to various handlers
2087 - # The format is a program to exec into as the key, and the user to match as the
2088 - # value. When someone tries to log in as this user, this program is executed
2089 - # and is expected to omit an AuthorizedKeys file.
2090 - #
2091 - # Discard of the string context is in order to allow derivation-derived strings.
2092 - # This is safe if the relevant package is installed which will be the case if the setting is utilized.
2093 - "git.sr.ht::dispatch".${builtins.unsafeDiscardStringContext "${pkgs.sourcehut.gitsrht}/bin/gitsrht-keys"} = mkDefault "${user}:${user}";
2094 - };
2095 -
2096 - services.nginx.virtualHosts."git.${cfg.originBase}" = {
2097 - forceSSL = true;
2098 - locations."/".proxyPass = "http://${cfg.address}:${toString port}";
2099 - locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
2100 - locations."/static".root = "${pkgs.sourcehut.gitsrht}/${pkgs.sourcehut.python.sitePackages}/gitsrht";
2101 - extraConfig = ''
2102 - location = /authorize {
2103 - proxy_pass http://${cfg.address}:${toString port};
2104 - proxy_pass_request_body off;
2105 - proxy_set_header Content-Length "";
2106 - proxy_set_header X-Original-URI $request_uri;
2107 - }
2108 - location ~ ^/([^/]+)/([^/]+)/(HEAD|info/refs|objects/info/.*|git-upload-pack).*$ {
2109 - auth_request /authorize;
2110 - root /var/lib/git;
2111 - fastcgi_pass unix:/run/fcgiwrap.sock;
2112 - fastcgi_param SCRIPT_FILENAME ${pkgs.git}/bin/git-http-backend;
2113 - fastcgi_param PATH_INFO $uri;
2114 - fastcgi_param GIT_PROJECT_ROOT $document_root;
2115 - fastcgi_read_timeout 500s;
2116 - include ${pkgs.nginx}/conf/fastcgi_params;
2117 - gzip off;
2118 - }
2119 - '';
2120 -
2121 - };
2122 - };
2123 -}
2124 diff --git a/nixos/modules/services/misc/sourcehut/hg.nix b/nixos/modules/services/misc/sourcehut/hg.nix
2125 deleted file mode 100644
2126 index 5cd36bb0455..00000000000
2127 --- a/nixos/modules/services/misc/sourcehut/hg.nix
2128 +++ /dev/null
2129 @@ -1,173 +0,0 @@
2130 -{ config, lib, pkgs, ... }:
2131 -
2132 -with lib;
2133 -let
2134 - cfg = config.services.sourcehut;
2135 - scfg = cfg.hg;
2136 - iniKey = "hg.sr.ht";
2137 -
2138 - rcfg = config.services.redis;
2139 - drv = pkgs.sourcehut.hgsrht;
2140 -in
2141 -{
2142 - options.services.sourcehut.hg = {
2143 - user = mkOption {
2144 - type = types.str;
2145 - internal = true;
2146 - readOnly = true;
2147 - default = "hg";
2148 - description = ''
2149 - User for hg.sr.ht.
2150 - '';
2151 - };
2152 -
2153 - port = mkOption {
2154 - type = types.port;
2155 - default = 5010;
2156 - description = ''
2157 - Port on which the "hg" module should listen.
2158 - '';
2159 - };
2160 -
2161 - database = mkOption {
2162 - type = types.str;
2163 - default = "hg.sr.ht";
2164 - description = ''
2165 - PostgreSQL database name for hg.sr.ht.
2166 - '';
2167 - };
2168 -
2169 - statePath = mkOption {
2170 - type = types.path;
2171 - default = "${cfg.statePath}/hgsrht";
2172 - description = ''
2173 - State path for hg.sr.ht.
2174 - '';
2175 - };
2176 -
2177 - cloneBundles = mkOption {
2178 - type = types.bool;
2179 - default = false;
2180 - description = ''
2181 - Generate clonebundles (which require more disk space but dramatically speed up cloning large repositories).
2182 - '';
2183 - };
2184 - };
2185 -
2186 - config = with scfg; lib.mkIf (cfg.enable && elem "hg" cfg.services) {
2187 - # In case it ever comes into being
2188 - environment.etc."ssh/hgsrht-dispatch" = {
2189 - mode = "0755";
2190 - text = ''
2191 - #! ${pkgs.stdenv.shell}
2192 - ${cfg.python}/bin/gitsrht-dispatch $@
2193 - '';
2194 - };
2195 -
2196 - environment.systemPackages = [ pkgs.mercurial ];
2197 -
2198 - users = {
2199 - users = {
2200 - "${user}" = {
2201 - isSystemUser = true;
2202 - group = user;
2203 - # Assuming hg.sr.ht needs this too
2204 - shell = pkgs.bash;
2205 - description = "hg.sr.ht user";
2206 - };
2207 - };
2208 -
2209 - groups = {
2210 - "${user}" = { };
2211 - };
2212 - };
2213 -
2214 - services = {
2215 - cron.systemCronJobs = [ "*/20 * * * * ${cfg.python}/bin/hgsrht-periodic" ]
2216 - ++ optional cloneBundles "0 * * * * ${cfg.python}/bin/hgsrht-clonebundles";
2217 -
2218 - openssh.authorizedKeysCommand = ''/etc/ssh/hgsrht-dispatch "%u" "%h" "%t" "%k"'';
2219 - openssh.authorizedKeysCommandUser = "root";
2220 - openssh.extraConfig = ''
2221 - PermitUserEnvironment SRHT_*
2222 - '';
2223 -
2224 - postgresql = {
2225 - authentication = ''
2226 - local ${database} ${user} trust
2227 - '';
2228 - ensureDatabases = [ database ];
2229 - ensureUsers = [
2230 - {
2231 - name = user;
2232 - ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
2233 - }
2234 - ];
2235 - };
2236 - };
2237 -
2238 - systemd = {
2239 - tmpfiles.rules = [
2240 - # /var/log is owned by root
2241 - "f /var/log/hg-srht-shell 0644 ${user} ${user} -"
2242 -
2243 - "d ${statePath} 0750 ${user} ${user} -"
2244 - "d ${cfg.settings."${iniKey}".repos} 2755 ${user} ${user} -"
2245 - ];
2246 -
2247 - services.hgsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
2248 - after = [ "redis.service" "postgresql.service" "network.target" ];
2249 - requires = [ "redis.service" "postgresql.service" ];
2250 - wantedBy = [ "multi-user.target" ];
2251 -
2252 - path = [ pkgs.mercurial ];
2253 - description = "hg.sr.ht website service";
2254 -
2255 - serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
2256 - };
2257 - };
2258 -
2259 - services.sourcehut.settings = {
2260 - # URL hg.sr.ht is being served at (protocol://domain)
2261 - "hg.sr.ht".origin = mkDefault "http://hg.${cfg.originBase}";
2262 - # Address and port to bind the debug server to
2263 - "hg.sr.ht".debug-host = mkDefault "0.0.0.0";
2264 - "hg.sr.ht".debug-port = mkDefault port;
2265 - # Configures the SQLAlchemy connection string for the database.
2266 - "hg.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
2267 - # The redis connection used for the webhooks worker
2268 - "hg.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/1";
2269 - # A post-update script which is installed in every mercurial repo.
2270 - "hg.sr.ht".changegroup-script = mkDefault "${cfg.python}/bin/hgsrht-hook-changegroup";
2271 - # hg.sr.ht's OAuth client ID and secret for meta.sr.ht
2272 - # Register your client at meta.example.org/oauth
2273 - "hg.sr.ht".oauth-client-id = mkDefault null;
2274 - "hg.sr.ht".oauth-client-secret = mkDefault null;
2275 - # Path to mercurial repositories on disk
2276 - "hg.sr.ht".repos = mkDefault "/var/lib/hg";
2277 - # Path to the srht mercurial extension
2278 - # (defaults to where the hgsrht code is)
2279 - # "hg.sr.ht".srhtext = mkDefault null;
2280 - # .hg/store size (in MB) past which the nightly job generates clone bundles.
2281 - # "hg.sr.ht".clone_bundle_threshold = mkDefault 50;
2282 - # Path to hg-ssh (if not in $PATH)
2283 - # "hg.sr.ht".hg_ssh = mkDefault /path/to/hg-ssh;
2284 -
2285 - # The authorized keys hook uses this to dispatch to various handlers
2286 - # The format is a program to exec into as the key, and the user to match as the
2287 - # value. When someone tries to log in as this user, this program is executed
2288 - # and is expected to omit an AuthorizedKeys file.
2289 - #
2290 - # Uncomment the relevant lines to enable the various sr.ht dispatchers.
2291 - "hg.sr.ht::dispatch"."/run/current-system/sw/bin/hgsrht-keys" = mkDefault "${user}:${user}";
2292 - };
2293 -
2294 - # TODO: requires testing and addition of hg-specific requirements
2295 - services.nginx.virtualHosts."hg.${cfg.originBase}" = {
2296 - forceSSL = true;
2297 - locations."/".proxyPass = "http://${cfg.address}:${toString port}";
2298 - locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
2299 - locations."/static".root = "${pkgs.sourcehut.hgsrht}/${pkgs.sourcehut.python.sitePackages}/hgsrht";
2300 - };
2301 - };
2302 -}
2303 diff --git a/nixos/modules/services/misc/sourcehut/hub.nix b/nixos/modules/services/misc/sourcehut/hub.nix
2304 deleted file mode 100644
2305 index be3ea21011c..00000000000
2306 --- a/nixos/modules/services/misc/sourcehut/hub.nix
2307 +++ /dev/null
2308 @@ -1,118 +0,0 @@
2309 -{ config, lib, pkgs, ... }:
2310 -
2311 -with lib;
2312 -let
2313 - cfg = config.services.sourcehut;
2314 - cfgIni = cfg.settings;
2315 - scfg = cfg.hub;
2316 - iniKey = "hub.sr.ht";
2317 -
2318 - drv = pkgs.sourcehut.hubsrht;
2319 -in
2320 -{
2321 - options.services.sourcehut.hub = {
2322 - user = mkOption {
2323 - type = types.str;
2324 - default = "hubsrht";
2325 - description = ''
2326 - User for hub.sr.ht.
2327 - '';
2328 - };
2329 -
2330 - port = mkOption {
2331 - type = types.port;
2332 - default = 5014;
2333 - description = ''
2334 - Port on which the "hub" module should listen.
2335 - '';
2336 - };
2337 -
2338 - database = mkOption {
2339 - type = types.str;
2340 - default = "hub.sr.ht";
2341 - description = ''
2342 - PostgreSQL database name for hub.sr.ht.
2343 - '';
2344 - };
2345 -
2346 - statePath = mkOption {
2347 - type = types.path;
2348 - default = "${cfg.statePath}/hubsrht";
2349 - description = ''
2350 - State path for hub.sr.ht.
2351 - '';
2352 - };
2353 - };
2354 -
2355 - config = with scfg; lib.mkIf (cfg.enable && elem "hub" cfg.services) {
2356 - users = {
2357 - users = {
2358 - "${user}" = {
2359 - isSystemUser = true;
2360 - group = user;
2361 - description = "hub.sr.ht user";
2362 - };
2363 - };
2364 -
2365 - groups = {
2366 - "${user}" = { };
2367 - };
2368 - };
2369 -
2370 - services.postgresql = {
2371 - authentication = ''
2372 - local ${database} ${user} trust
2373 - '';
2374 - ensureDatabases = [ database ];
2375 - ensureUsers = [
2376 - {
2377 - name = user;
2378 - ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
2379 - }
2380 - ];
2381 - };
2382 -
2383 - systemd = {
2384 - tmpfiles.rules = [
2385 - "d ${statePath} 0750 ${user} ${user} -"
2386 - ];
2387 -
2388 - services.hubsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
2389 - after = [ "postgresql.service" "network.target" ];
2390 - requires = [ "postgresql.service" ];
2391 - wantedBy = [ "multi-user.target" ];
2392 -
2393 - description = "hub.sr.ht website service";
2394 -
2395 - serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
2396 - };
2397 - };
2398 -
2399 - services.sourcehut.settings = {
2400 - # URL hub.sr.ht is being served at (protocol://domain)
2401 - "hub.sr.ht".origin = mkDefault "http://hub.${cfg.originBase}";
2402 - # Address and port to bind the debug server to
2403 - "hub.sr.ht".debug-host = mkDefault "0.0.0.0";
2404 - "hub.sr.ht".debug-port = mkDefault port;
2405 - # Configures the SQLAlchemy connection string for the database.
2406 - "hub.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
2407 - # Set to "yes" to automatically run migrations on package upgrade.
2408 - "hub.sr.ht".migrate-on-upgrade = mkDefault "yes";
2409 - # hub.sr.ht's OAuth client ID and secret for meta.sr.ht
2410 - # Register your client at meta.example.org/oauth
2411 - "hub.sr.ht".oauth-client-id = mkDefault null;
2412 - "hub.sr.ht".oauth-client-secret = mkDefault null;
2413 - };
2414 -
2415 - services.nginx.virtualHosts."${cfg.originBase}" = {
2416 - forceSSL = true;
2417 - locations."/".proxyPass = "http://${cfg.address}:${toString port}";
2418 - locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
2419 - locations."/static".root = "${pkgs.sourcehut.hubsrht}/${pkgs.sourcehut.python.sitePackages}/hubsrht";
2420 - };
2421 - services.nginx.virtualHosts."hub.${cfg.originBase}" = {
2422 - globalRedirect = "${cfg.originBase}";
2423 - forceSSL = true;
2424 - };
2425 - };
2426 -}
2427 diff --git a/nixos/modules/services/misc/sourcehut/lists.nix b/nixos/modules/services/misc/sourcehut/lists.nix
2428 deleted file mode 100644
2429 index 7b1fe9fd463..00000000000
2430 --- a/nixos/modules/services/misc/sourcehut/lists.nix
2431 +++ /dev/null
2432 @@ -1,185 +0,0 @@
2433 -# Email setup is fairly involved, useful references:
2434 -# https://drewdevault.com/2018/08/05/Local-mail-server.html
2435 -
2436 -{ config, lib, pkgs, ... }:
2437 -
2438 -with lib;
2439 -let
2440 - cfg = config.services.sourcehut;
2441 - cfgIni = cfg.settings;
2442 - scfg = cfg.lists;
2443 - iniKey = "lists.sr.ht";
2444 -
2445 - rcfg = config.services.redis;
2446 - drv = pkgs.sourcehut.listssrht;
2447 -in
2448 -{
2449 - options.services.sourcehut.lists = {
2450 - user = mkOption {
2451 - type = types.str;
2452 - default = "listssrht";
2453 - description = ''
2454 - User for lists.sr.ht.
2455 - '';
2456 - };
2457 -
2458 - port = mkOption {
2459 - type = types.port;
2460 - default = 5006;
2461 - description = ''
2462 - Port on which the "lists" module should listen.
2463 - '';
2464 - };
2465 -
2466 - database = mkOption {
2467 - type = types.str;
2468 - default = "lists.sr.ht";
2469 - description = ''
2470 - PostgreSQL database name for lists.sr.ht.
2471 - '';
2472 - };
2473 -
2474 - statePath = mkOption {
2475 - type = types.path;
2476 - default = "${cfg.statePath}/listssrht";
2477 - description = ''
2478 - State path for lists.sr.ht.
2479 - '';
2480 - };
2481 - };
2482 -
2483 - config = with scfg; lib.mkIf (cfg.enable && elem "lists" cfg.services) {
2484 - users = {
2485 - users = {
2486 - "${user}" = {
2487 - isSystemUser = true;
2488 - group = user;
2489 - extraGroups = [ "postfix" ];
2490 - description = "lists.sr.ht user";
2491 - };
2492 - };
2493 - groups = {
2494 - "${user}" = { };
2495 - };
2496 - };
2497 -
2498 - services.postgresql = {
2499 - authentication = ''
2500 - local ${database} ${user} trust
2501 - '';
2502 - ensureDatabases = [ database ];
2503 - ensureUsers = [
2504 - {
2505 - name = user;
2506 - ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
2507 - }
2508 - ];
2509 - };
2510 -
2511 - systemd = {
2512 - tmpfiles.rules = [
2513 - "d ${statePath} 0750 ${user} ${user} -"
2514 - ];
2515 -
2516 - services = {
2517 - listssrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
2518 - after = [ "postgresql.service" "network.target" ];
2519 - requires = [ "postgresql.service" ];
2520 - wantedBy = [ "multi-user.target" ];
2521 -
2522 - description = "lists.sr.ht website service";
2523 -
2524 - serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
2525 - };
2526 -
2527 - listssrht-process = {
2528 - after = [ "postgresql.service" "network.target" ];
2529 - requires = [ "postgresql.service" ];
2530 - wantedBy = [ "multi-user.target" ];
2531 -
2532 - description = "lists.sr.ht process service";
2533 - serviceConfig = {
2534 - Type = "simple";
2535 - User = user;
2536 - Restart = "always";
2537 - ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.process worker --loglevel=info";
2538 - };
2539 - };
2540 -
2541 - listssrht-lmtp = {
2542 - after = [ "postgresql.service" "network.target" ];
2543 - requires = [ "postgresql.service" ];
2544 - wantedBy = [ "multi-user.target" ];
2545 -
2546 - description = "lists.sr.ht process service";
2547 - serviceConfig = {
2548 - Type = "simple";
2549 - User = user;
2550 - Restart = "always";
2551 - ExecStart = "${cfg.python}/bin/listssrht-lmtp";
2552 - };
2553 - };
2554 -
2555 -
2556 - listssrht-webhooks = {
2557 - after = [ "postgresql.service" "network.target" ];
2558 - requires = [ "postgresql.service" ];
2559 - wantedBy = [ "multi-user.target" ];
2560 -
2561 - description = "lists.sr.ht webhooks service";
2562 - serviceConfig = {
2563 - Type = "simple";
2564 - User = user;
2565 - Restart = "always";
2566 - ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
2567 - };
2568 - };
2569 - };
2570 - };
2571 -
2572 - services.sourcehut.settings = {
2573 - # URL lists.sr.ht is being served at (protocol://domain)
2574 - "lists.sr.ht".origin = mkDefault "http://lists.${cfg.originBase}";
2575 - # Address and port to bind the debug server to
2576 - "lists.sr.ht".debug-host = mkDefault "0.0.0.0";
2577 - "lists.sr.ht".debug-port = mkDefault port;
2578 - # Configures the SQLAlchemy connection string for the database.
2579 - "lists.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
2580 - # Set to "yes" to automatically run migrations on package upgrade.
2581 - "lists.sr.ht".migrate-on-upgrade = mkDefault "yes";
2582 - # lists.sr.ht's OAuth client ID and secret for meta.sr.ht
2583 - # Register your client at meta.example.org/oauth
2584 - "lists.sr.ht".oauth-client-id = mkDefault null;
2585 - "lists.sr.ht".oauth-client-secret = mkDefault null;
2586 - # Outgoing email for notifications generated by users
2587 - "lists.sr.ht".notify-from = mkDefault "CHANGEME@example.org";
2588 - # The redis connection used for the webhooks worker
2589 - "lists.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/2";
2590 - # The redis connection used for the celery worker
2591 - "lists.sr.ht".redis = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/4";
2592 - # Network-key
2593 - "lists.sr.ht".network-key = mkDefault null;
2594 - # Allow creation
2595 - "lists.sr.ht".allow-new-lists = mkDefault "no";
2596 - # Posting Domain
2597 - "lists.sr.ht".posting-domain = mkDefault "lists.${cfg.originBase}";
2598 -
2599 - # Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
2600 - # Alternatively, specify IP:PORT and an SMTP server will be run instead.
2601 - "lists.sr.ht::worker".sock = mkDefault "/tmp/lists.sr.ht-lmtp.sock";
2602 - # The lmtp daemon will make the unix socket group-read/write for users in this
2603 - # group.
2604 - "lists.sr.ht::worker".sock-group = mkDefault "postfix";
2605 - "lists.sr.ht::worker".reject-url = mkDefault "https://man.sr.ht/lists.sr.ht/etiquette.md";
2606 - "lists.sr.ht::worker".reject-mimetypes = mkDefault "text/html";
2607 -
2608 - };
2609 -
2610 - services.nginx.virtualHosts."lists.${cfg.originBase}" = {
2611 - forceSSL = true;
2612 - locations."/".proxyPass = "http://${cfg.address}:${toString port}";
2613 - locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
2614 - locations."/static".root = "${pkgs.sourcehut.listssrht}/${pkgs.sourcehut.python.sitePackages}/listssrht";
2615 - };
2616 - };
2617 -}
2618 diff --git a/nixos/modules/services/misc/sourcehut/man.nix b/nixos/modules/services/misc/sourcehut/man.nix
2619 deleted file mode 100644
2620 index 7693396d187..00000000000
2621 --- a/nixos/modules/services/misc/sourcehut/man.nix
2622 +++ /dev/null
2623 @@ -1,122 +0,0 @@
2624 -{ config, lib, pkgs, ... }:
2625 -
2626 -with lib;
2627 -let
2628 - cfg = config.services.sourcehut;
2629 - cfgIni = cfg.settings;
2630 - scfg = cfg.man;
2631 - iniKey = "man.sr.ht";
2632 -
2633 - drv = pkgs.sourcehut.mansrht;
2634 -in
2635 -{
2636 - options.services.sourcehut.man = {
2637 - user = mkOption {
2638 - type = types.str;
2639 - default = "mansrht";
2640 - description = ''
2641 - User for man.sr.ht.
2642 - '';
2643 - };
2644 -
2645 - port = mkOption {
2646 - type = types.port;
2647 - default = 5004;
2648 - description = ''
2649 - Port on which the "man" module should listen.
2650 - '';
2651 - };
2652 -
2653 - database = mkOption {
2654 - type = types.str;
2655 - default = "man.sr.ht";
2656 - description = ''
2657 - PostgreSQL database name for man.sr.ht.
2658 - '';
2659 - };
2660 -
2661 - statePath = mkOption {
2662 - type = types.path;
2663 - default = "${cfg.statePath}/mansrht";
2664 - description = ''
2665 - State path for man.sr.ht.
2666 - '';
2667 - };
2668 - };
2669 -
2670 - config = with scfg; lib.mkIf (cfg.enable && elem "man" cfg.services) {
2671 - assertions =
2672 - [
2673 - {
2674 - assertion = hasAttrByPath [ "git.sr.ht" "oauth-client-id" ] cfgIni;
2675 - message = "man.sr.ht needs access to git.sr.ht.";
2676 - }
2677 - ];
2678 -
2679 - users = {
2680 - users = {
2681 - "${user}" = {
2682 - isSystemUser = true;
2683 - group = user;
2684 - description = "man.sr.ht user";
2685 - };
2686 - };
2687 -
2688 - groups = {
2689 - "${user}" = { };
2690 - };
2691 - };
2692 -
2693 - services.postgresql = {
2694 - authentication = ''
2695 - local ${database} ${user} trust
2696 - '';
2697 - ensureDatabases = [ database ];
2698 - ensureUsers = [
2699 - {
2700 - name = user;
2701 - ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
2702 - }
2703 - ];
2704 - };
2705 -
2706 - systemd = {
2707 - tmpfiles.rules = [
2708 - "d ${statePath} 0750 ${user} ${user} -"
2709 - ];
2710 -
2711 - services.mansrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
2712 - after = [ "postgresql.service" "network.target" ];
2713 - requires = [ "postgresql.service" ];
2714 - wantedBy = [ "multi-user.target" ];
2715 -
2716 - description = "man.sr.ht website service";
2717 -
2718 - serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
2719 - };
2720 - };
2721 -
2722 - services.sourcehut.settings = {
2723 - # URL man.sr.ht is being served at (protocol://domain)
2724 - "man.sr.ht".origin = mkDefault "http://man.${cfg.originBase}";
2725 - # Address and port to bind the debug server to
2726 - "man.sr.ht".debug-host = mkDefault "0.0.0.0";
2727 - "man.sr.ht".debug-port = mkDefault port;
2728 - # Configures the SQLAlchemy connection string for the database.
2729 - "man.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
2730 - # Set to "yes" to automatically run migrations on package upgrade.
2731 - "man.sr.ht".migrate-on-upgrade = mkDefault "yes";
2732 - # man.sr.ht's OAuth client ID and secret for meta.sr.ht
2733 - # Register your client at meta.example.org/oauth
2734 - "man.sr.ht".oauth-client-id = mkDefault null;
2735 - "man.sr.ht".oauth-client-secret = mkDefault null;
2736 - };
2737 -
2738 - services.nginx.virtualHosts."man.${cfg.originBase}" = {
2739 - forceSSL = true;
2740 - locations."/".proxyPass = "http://${cfg.address}:${toString port}";
2741 - locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
2742 - locations."/static".root = "${pkgs.sourcehut.mansrht}/${pkgs.sourcehut.python.sitePackages}/mansrht";
2743 - };
2744 - };
2745 -}
2746 diff --git a/nixos/modules/services/misc/sourcehut/meta.nix b/nixos/modules/services/misc/sourcehut/meta.nix
2747 deleted file mode 100644
2748 index 56127a824eb..00000000000
2749 --- a/nixos/modules/services/misc/sourcehut/meta.nix
2750 +++ /dev/null
2751 @@ -1,211 +0,0 @@
2752 -{ config, lib, pkgs, ... }:
2753 -
2754 -with lib;
2755 -let
2756 - cfg = config.services.sourcehut;
2757 - cfgIni = cfg.settings;
2758 - scfg = cfg.meta;
2759 - iniKey = "meta.sr.ht";
2760 -
2761 - rcfg = config.services.redis;
2762 - drv = pkgs.sourcehut.metasrht;
2763 -in
2764 -{
2765 - options.services.sourcehut.meta = {
2766 - user = mkOption {
2767 - type = types.str;
2768 - default = "metasrht";
2769 - description = ''
2770 - User for meta.sr.ht.
2771 - '';
2772 - };
2773 -
2774 - port = mkOption {
2775 - type = types.port;
2776 - default = 5000;
2777 - description = ''
2778 - Port on which the "meta" module should listen.
2779 - '';
2780 - };
2781 -
2782 - database = mkOption {
2783 - type = types.str;
2784 - default = "meta.sr.ht";
2785 - description = ''
2786 - PostgreSQL database name for meta.sr.ht.
2787 - '';
2788 - };
2789 -
2790 - statePath = mkOption {
2791 - type = types.path;
2792 - default = "${cfg.statePath}/metasrht";
2793 - description = ''
2794 - State path for meta.sr.ht.
2795 - '';
2796 - };
2797 - };
2798 -
2799 - config = with scfg; lib.mkIf (cfg.enable && elem "meta" cfg.services) {
2800 - assertions =
2801 - [
2802 - {
2803 - assertion = with cfgIni."meta.sr.ht::billing"; enabled == "yes" -> (stripe-public-key != null && stripe-secret-key != null);
2804 - message = "If meta.sr.ht::billing is enabled, the keys should be defined.";
2805 - }
2806 - ];
2807 -
2808 - users = {
2809 - users = {
2810 - ${user} = {
2811 - isSystemUser = true;
2812 - group = user;
2813 - description = "meta.sr.ht user";
2814 - };
2815 - };
2816 -
2817 - groups = {
2818 - "${user}" = { };
2819 - };
2820 - };
2821 -
2822 - services.cron.systemCronJobs = [ "0 0 * * * ${cfg.python}/bin/metasrht-daily" ];
2823 - services.postgresql = {
2824 - authentication = ''
2825 - local ${database} ${user} trust
2826 - '';
2827 - ensureDatabases = [ database ];
2828 - ensureUsers = [
2829 - {
2830 - name = user;
2831 - ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
2832 - }
2833 - ];
2834 - };
2835 -
2836 - systemd = {
2837 - tmpfiles.rules = [
2838 - "d ${statePath} 0750 ${user} ${user} -"
2839 - ];
2840 -
2841 - services = {
2842 - metasrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
2843 - after = [ "postgresql.service" "network.target" ];
2844 - requires = [ "postgresql.service" ];
2845 - wantedBy = [ "multi-user.target" ];
2846 -
2847 - description = "meta.sr.ht website service";
2848 -
2849 - preStart = ''
2850 - # Configure client(s) as "preauthorized"
2851 - ${concatMapStringsSep "\n\n"
2852 - (attr: ''
2853 - if ! test -e "${statePath}/${attr}.oauth" || [ "$(cat ${statePath}/${attr}.oauth)" != "${cfgIni."${attr}".oauth-client-id}" ]; then
2854 - # Configure ${attr}'s OAuth client as "preauthorized"
2855 - psql ${database} \
2856 - -c "UPDATE oauthclient SET preauthorized = true WHERE client_id = '${cfgIni."${attr}".oauth-client-id}'"
2857 -
2858 - printf "%s" "${cfgIni."${attr}".oauth-client-id}" > "${statePath}/${attr}.oauth"
2859 - fi
2860 - '')
2861 - (builtins.attrNames (filterAttrs
2862 - (k: v: !(hasInfix "::" k) && builtins.hasAttr "oauth-client-id" v && v.oauth-client-id != null)
2863 - cfg.settings))}
2864 - '';
2865 -
2866 - serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
2867 - };
2868 -
2869 - metasrht-api = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
2870 - after = [ "postgresql.service" "network.target" ];
2871 - requires = [ "postgresql.service" ];
2872 - wantedBy = [ "multi-user.target" ];
2873 -
2874 - description = "meta.sr.ht api service";
2875 -
2876 - preStart = ''
2877 - # Configure client(s) as "preauthorized"
2878 - ${concatMapStringsSep "\n\n"
2879 - (attr: ''
2880 - if ! test -e "${statePath}/${attr}.oauth" || [ "$(cat ${statePath}/${attr}.oauth)" != "${cfgIni."${attr}".oauth-client-id}" ]; then
2881 - # Configure ${attr}'s OAuth client as "preauthorized"
2882 - psql ${database} \
2883 - -c "UPDATE oauthclient SET preauthorized = true WHERE client_id = '${cfgIni."${attr}".oauth-client-id}'"
2884 -
2885 - printf "%s" "${cfgIni."${attr}".oauth-client-id}" > "${statePath}/${attr}.oauth"
2886 - fi
2887 - '')
2888 - (builtins.attrNames (filterAttrs
2889 - (k: v: !(hasInfix "::" k) && builtins.hasAttr "oauth-client-id" v && v.oauth-client-id != null)
2890 - cfg.settings))}
2891 - '';
2892 -
2893 - serviceConfig.ExecStart = "${pkgs.sourcehut.metasrht}/bin/metasrht-api -b :${toString (port + 100)}";
2894 - };
2895 -
2896 - metasrht-webhooks = {
2897 - after = [ "postgresql.service" "network.target" ];
2898 - requires = [ "postgresql.service" ];
2899 - wantedBy = [ "multi-user.target" ];
2900 -
2901 - description = "meta.sr.ht webhooks service";
2902 - serviceConfig = {
2903 - Type = "simple";
2904 - User = user;
2905 - Restart = "always";
2906 - ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
2907 - };
2908 -
2909 - };
2910 - };
2911 - };
2912 -
2913 - services.sourcehut.settings = {
2914 - # URL meta.sr.ht is being served at (protocol://domain)
2915 - "meta.sr.ht".origin = mkDefault "https://meta.${cfg.originBase}";
2916 - # Address and port to bind the debug server to
2917 - "meta.sr.ht".debug-host = mkDefault "0.0.0.0";
2918 - "meta.sr.ht".debug-port = mkDefault port;
2919 - # Configures the SQLAlchemy connection string for the database.
2920 - "meta.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
2921 - # Set to "yes" to automatically run migrations on package upgrade.
2922 - "meta.sr.ht".migrate-on-upgrade = mkDefault "yes";
2923 - # If "yes", the user will be sent the stock sourcehut welcome emails after
2924 - # signup (requires cron to be configured properly). These are specific to the
2925 - # sr.ht instance so you probably want to patch these before enabling this.
2926 - "meta.sr.ht".welcome-emails = mkDefault "no";
2927 -
2928 - # The redis connection used for the webhooks worker
2929 - "meta.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/6";
2930 -
2931 - # If "no", public registration will not be permitted.
2932 - "meta.sr.ht::settings".registration = mkDefault "no";
2933 - # Where to redirect new users upon registration
2934 - "meta.sr.ht::settings".onboarding-redirect = mkDefault "https://meta.${cfg.originBase}";
2935 - # How many invites each user is issued upon registration (only applicable if
2936 - # open registration is disabled)
2937 - "meta.sr.ht::settings".user-invites = mkDefault 5;
2938 -
2939 - # Origin URL for API, 100 more than web
2940 - "meta.sr.ht".api-origin = mkDefault "http://localhost:5100";
2941 -
2942 - # You can add aliases for the client IDs of commonly used OAuth clients here.
2943 - #
2944 - # Example:
2945 - "meta.sr.ht::aliases" = mkDefault { };
2946 - # "meta.sr.ht::aliases"."git.sr.ht" = 12345;
2947 -
2948 - # "yes" to enable the billing system
2949 - "meta.sr.ht::billing".enabled = mkDefault "no";
2950 - # Get your keys at https://dashboard.stripe.com/account/apikeys
2951 - "meta.sr.ht::billing".stripe-public-key = mkDefault null;
2952 - "meta.sr.ht::billing".stripe-secret-key = mkDefault null;
2953 - };
2954 -
2955 - services.nginx.virtualHosts."meta.${cfg.originBase}" = {
2956 - forceSSL = true;
2957 - locations."/".proxyPass = "http://${cfg.address}:${toString port}";
2958 - locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
2959 - locations."/static".root = "${pkgs.sourcehut.metasrht}/${pkgs.sourcehut.python.sitePackages}/metasrht";
2960 - };
2961 - };
2962 -}
2963 diff --git a/nixos/modules/services/misc/sourcehut/paste.nix b/nixos/modules/services/misc/sourcehut/paste.nix
2964 deleted file mode 100644
2965 index b2d5151969e..00000000000
2966 --- a/nixos/modules/services/misc/sourcehut/paste.nix
2967 +++ /dev/null
2968 @@ -1,133 +0,0 @@
2969 -{ config, lib, pkgs, ... }:
2970 -
2971 -with lib;
2972 -let
2973 - cfg = config.services.sourcehut;
2974 - cfgIni = cfg.settings;
2975 - scfg = cfg.paste;
2976 - iniKey = "paste.sr.ht";
2977 -
2978 - rcfg = config.services.redis;
2979 - drv = pkgs.sourcehut.pastesrht;
2980 -in
2981 -{
2982 - options.services.sourcehut.paste = {
2983 - user = mkOption {
2984 - type = types.str;
2985 - default = "pastesrht";
2986 - description = ''
2987 - User for paste.sr.ht.
2988 - '';
2989 - };
2990 -
2991 - port = mkOption {
2992 - type = types.port;
2993 - default = 5011;
2994 - description = ''
2995 - Port on which the "paste" module should listen.
2996 - '';
2997 - };
2998 -
2999 - database = mkOption {
3000 - type = types.str;
3001 - default = "paste.sr.ht";
3002 - description = ''
3003 - PostgreSQL database name for paste.sr.ht.
3004 - '';
3005 - };
3006 -
3007 - statePath = mkOption {
3008 - type = types.path;
3009 - default = "${cfg.statePath}/pastesrht";
3010 - description = ''
3011 - State path for pastesrht.sr.ht.
3012 - '';
3013 - };
3014 - };
3015 -
3016 - config = with scfg; lib.mkIf (cfg.enable && elem "paste" cfg.services) {
3017 - users = {
3018 - users = {
3019 - "${user}" = {
3020 - isSystemUser = true;
3021 - group = user;
3022 - description = "paste.sr.ht user";
3023 - };
3024 - };
3025 -
3026 - groups = {
3027 - "${user}" = { };
3028 - };
3029 - };
3030 -
3031 - services.postgresql = {
3032 - authentication = ''
3033 - local ${database} ${user} trust
3034 - '';
3035 - ensureDatabases = [ database ];
3036 - ensureUsers = [
3037 - {
3038 - name = user;
3039 - ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
3040 - }
3041 - ];
3042 - };
3043 -
3044 - systemd = {
3045 - tmpfiles.rules = [
3046 - "d ${statePath} 0750 ${user} ${user} -"
3047 - ];
3048 -
3049 - services = {
3050 - pastesrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
3051 - after = [ "postgresql.service" "network.target" ];
3052 - requires = [ "postgresql.service" ];
3053 - wantedBy = [ "multi-user.target" ];
3054 -
3055 - description = "paste.sr.ht website service";
3056 -
3057 - serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
3058 - };
3059 -
3060 - pastesrht-webhooks = {
3061 - after = [ "postgresql.service" "network.target" ];
3062 - requires = [ "postgresql.service" ];
3063 - wantedBy = [ "multi-user.target" ];
3064 -
3065 - description = "paste.sr.ht webhooks service";
3066 - serviceConfig = {
3067 - Type = "simple";
3068 - User = user;
3069 - Restart = "always";
3070 - ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
3071 - };
3072 -
3073 - };
3074 - };
3075 - };
3076 -
3077 - services.sourcehut.settings = {
3078 - # URL paste.sr.ht is being served at (protocol://domain)
3079 - "paste.sr.ht".origin = mkDefault "http://paste.${cfg.originBase}";
3080 - # Address and port to bind the debug server to
3081 - "paste.sr.ht".debug-host = mkDefault "0.0.0.0";
3082 - "paste.sr.ht".debug-port = mkDefault port;
3083 - # Configures the SQLAlchemy connection string for the database.
3084 - "paste.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
3085 - # Set to "yes" to automatically run migrations on package upgrade.
3086 - "paste.sr.ht".migrate-on-upgrade = mkDefault "yes";
3087 - # paste.sr.ht's OAuth client ID and secret for meta.sr.ht
3088 - # Register your client at meta.example.org/oauth
3089 - "paste.sr.ht".oauth-client-id = mkDefault null;
3090 - "paste.sr.ht".oauth-client-secret = mkDefault null;
3091 - "paste.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/5";
3092 - };
3093 -
3094 - services.nginx.virtualHosts."paste.${cfg.originBase}" = {
3095 - forceSSL = true;
3096 - locations."/".proxyPass = "http://${cfg.address}:${toString port}";
3097 - locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
3098 - locations."/static".root = "${pkgs.sourcehut.pastesrht}/${pkgs.sourcehut.python.sitePackages}/pastesrht";
3099 - };
3100 - };
3101 -}
3102 diff --git a/nixos/modules/services/misc/sourcehut/service.nix b/nixos/modules/services/misc/sourcehut/service.nix
3103 index 65b4ad020f9..f1706ad0a6a 100644
3104 --- a/nixos/modules/services/misc/sourcehut/service.nix
3105 +++ b/nixos/modules/services/misc/sourcehut/service.nix
3106 @@ -1,66 +1,375 @@
3107 -{ config, pkgs, lib }:
3108 -serviceCfg: serviceDrv: iniKey: attrs:
3109 +srv:
3110 +{ configIniOfService
3111 +, srvsrht ? "${srv}srht" # Because "buildsrht" does not follow that pattern (missing an "s").
3112 +, iniKey ? "${srv}.sr.ht"
3113 +, webhooks ? false
3114 +, extraTimers ? {}
3115 +, mainService ? {}
3116 +, extraServices ? {}
3117 +, extraConfig ? {}
3118 +, port
3119 +}:
3120 +{ config, lib, pkgs, ... }:
3121 +
3122 +with lib;
3123 let
3124 + inherit (config.services) postgresql;
3125 + redis = config.services.redis.servers."sourcehut-${srvsrht}";
3126 + inherit (config.users) users;
3127 cfg = config.services.sourcehut;
3128 - cfgIni = cfg.settings."${iniKey}";
3129 - pgSuperUser = config.services.postgresql.superUser;
3130 -
3131 - setupDB = pkgs.writeScript "${serviceDrv.pname}-gen-db" ''
3132 - #! ${cfg.python}/bin/python
3133 - from ${serviceDrv.pname}.app import db
3134 - db.create()
3135 - '';
3136 + configIni = configIniOfService srv;
3137 + srvCfg = cfg.${srv};
3138 + baseService = serviceName: { allowStripe ? false }: extraService: let
3139 + runDir = "/run/sourcehut/${serviceName}";
3140 + rootDir = "/run/sourcehut/chroots/${serviceName}";
3141 + in
3142 + mkMerge [ extraService {
3143 + after = [ "network.target" ] ++
3144 + optional cfg.postgresql.enable "postgresql.service" ++
3145 + optional cfg.redis.enable "redis-sourcehut-${srvsrht}.service";
3146 + requires =
3147 + optional cfg.postgresql.enable "postgresql.service" ++
3148 + optional cfg.redis.enable "redis-sourcehut-${srvsrht}.service";
3149 + path = [ pkgs.gawk ];
3150 + environment.HOME = runDir;
3151 + serviceConfig = {
3152 + User = mkDefault srvCfg.user;
3153 + Group = mkDefault srvCfg.group;
3154 + RuntimeDirectory = [
3155 + "sourcehut/${serviceName}"
3156 + # Used by *srht-keys which reads ../config.ini
3157 + "sourcehut/${serviceName}/subdir"
3158 + "sourcehut/chroots/${serviceName}"
3159 + ];
3160 + RuntimeDirectoryMode = "2750";
3161 + # No need for the chroot path once inside the chroot
3162 + InaccessiblePaths = [ "-+${rootDir}" ];
3163 + # g+rx is for group members (eg. fcgiwrap or nginx)
3164 + # to read Git/Mercurial repositories, buildlogs, etc.
3165 + # o+x is for intermediate directories created by BindPaths= and like,
3166 + # as they're owned by root:root.
3167 + UMask = "0026";
3168 + RootDirectory = rootDir;
3169 + RootDirectoryStartOnly = true;
3170 + PrivateTmp = true;
3171 + MountAPIVFS = true;
3172 + # config.ini is looked up in there, before /etc/srht/config.ini
3173 + # Note that it fails to be set in ExecStartPre=
3174 + WorkingDirectory = mkDefault ("-"+runDir);
3175 + BindReadOnlyPaths = [
3176 + builtins.storeDir
3177 + "/etc"
3178 + "/run/booted-system"
3179 + "/run/current-system"
3180 + "/run/systemd"
3181 + ] ++
3182 + optional cfg.postgresql.enable "/run/postgresql" ++
3183 + optional cfg.redis.enable "/run/redis-sourcehut-${srvsrht}";
3184 + # LoadCredential= are unfortunately not available in ExecStartPre=
3185 + # Hence this one is run as root (the +) with RootDirectoryStartOnly=
3186 + # to reach credentials wherever they are.
3187 + # Note that each systemd service gets its own ${runDir}/config.ini file.
3188 + ExecStartPre = mkBefore [("+"+pkgs.writeShellScript "${serviceName}-credentials" ''
3189 + set -x
3190 + # Replace values begining with a '<' by the content of the file whose name is after.
3191 + gawk '{ if (match($0,/^([^=]+=)<(.+)/,m)) { getline f < m[2]; print m[1] f } else print $0 }' ${configIni} |
3192 + ${optionalString (!allowStripe) "gawk '!/^stripe-secret-key=/' |"}
3193 + install -o ${srvCfg.user} -g root -m 400 /dev/stdin ${runDir}/config.ini
3194 + '')];
3195 + # The following options are only for optimizing:
3196 + # systemd-analyze security
3197 + AmbientCapabilities = "";
3198 + CapabilityBoundingSet = "";
3199 + # ProtectClock= adds DeviceAllow=char-rtc r
3200 + DeviceAllow = "";
3201 + LockPersonality = true;
3202 + MemoryDenyWriteExecute = true;
3203 + NoNewPrivileges = true;
3204 + PrivateDevices = true;
3205 + PrivateMounts = true;
3206 + PrivateNetwork = mkDefault false;
3207 + PrivateUsers = true;
3208 + ProcSubset = "pid";
3209 + ProtectClock = true;
3210 + ProtectControlGroups = true;
3211 + ProtectHome = true;
3212 + ProtectHostname = true;
3213 + ProtectKernelLogs = true;
3214 + ProtectKernelModules = true;
3215 + ProtectKernelTunables = true;
3216 + ProtectProc = "invisible";
3217 + ProtectSystem = "strict";
3218 + RemoveIPC = true;
3219 + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
3220 + RestrictNamespaces = true;
3221 + RestrictRealtime = true;
3222 + RestrictSUIDSGID = true;
3223 + #SocketBindAllow = [ "tcp:${toString srvCfg.port}" "tcp:${toString srvCfg.prometheusPort}" ];
3224 + #SocketBindDeny = "any";
3225 + SystemCallFilter = [
3226 + "@system-service"
3227 + "~@aio" "~@keyring" "~@memlock" "~@privileged" "~@resources" "~@timer"
3228 + "@chown" "@setuid"
3229 + ];
3230 + SystemCallArchitectures = "native";
3231 + };
3232 + } ];
3233 in
3234 -with serviceCfg; with lib; recursiveUpdate
3235 {
3236 - environment.HOME = statePath;
3237 - path = [ config.services.postgresql.package ] ++ (attrs.path or [ ]);
3238 - restartTriggers = [ config.environment.etc."sr.ht/config.ini".source ];
3239 - serviceConfig = {
3240 - Type = "simple";
3241 - User = user;
3242 - Group = user;
3243 - Restart = "always";
3244 - WorkingDirectory = statePath;
3245 - } // (if (cfg.statePath == "/var/lib/sourcehut/${serviceDrv.pname}") then {
3246 - StateDirectory = [ "sourcehut/${serviceDrv.pname}" ];
3247 - } else {})
3248 - ;
3249 + options.services.sourcehut.${srv} = {
3250 + enable = mkEnableOption "${srv} service";
3251
3252 - preStart = ''
3253 - if ! test -e ${statePath}/db; then
3254 - # Setup the initial database
3255 - ${setupDB}
3256 + user = mkOption {
3257 + type = types.str;
3258 + default = srvsrht;
3259 + description = ''
3260 + User for ${srv}.sr.ht.
3261 + '';
3262 + };
3263
3264 - # Set the initial state of the database for future database upgrades
3265 - if test -e ${cfg.python}/bin/${serviceDrv.pname}-migrate; then
3266 - # Run alembic stamp head once to tell alembic the schema is up-to-date
3267 - ${cfg.python}/bin/${serviceDrv.pname}-migrate stamp head
3268 - fi
3269 + group = mkOption {
3270 + type = types.str;
3271 + default = srvsrht;
3272 + description = ''
3273 + Group for ${srv}.sr.ht.
3274 + Membership grants access to the Git/Mercurial repositories by default,
3275 + but not to the config.ini file (where secrets are).
3276 + '';
3277 + };
3278
3279 - printf "%s" "${serviceDrv.version}" > ${statePath}/db
3280 - fi
3281 + port = mkOption {
3282 + type = types.port;
3283 + default = port;
3284 + description = ''
3285 + Port on which the "${srv}" backend should listen.
3286 + '';
3287 + };
3288
3289 - # Update copy of each users' profile to the latest
3290 - # See https://lists.sr.ht/~sircmpwn/sr.ht-admins/<20190302181207.GA13778%40cirno.my.domain>
3291 - if ! test -e ${statePath}/webhook; then
3292 - # Update ${iniKey}'s users' profile copy to the latest
3293 - ${cfg.python}/bin/srht-update-profiles ${iniKey}
3294 + redis = {
3295 + host = mkOption {
3296 + type = types.str;
3297 + default = "unix:/run/redis-sourcehut-${srvsrht}/redis.sock?db=0";
3298 + example = "redis://shared.wireguard:6379/0";
3299 + description = ''
3300 + The redis host URL. This is used for caching and temporary storage, and must
3301 + be shared between nodes (e.g. git1.sr.ht and git2.sr.ht), but need not be
3302 + shared between services. It may be shared between services, however, with no
3303 + ill effect, if this better suits your infrastructure.
3304 + '';
3305 + };
3306 + };
3307
3308 - touch ${statePath}/webhook
3309 - fi
3310 + postgresql = {
3311 + database = mkOption {
3312 + type = types.str;
3313 + default = "${srv}.sr.ht";
3314 + description = ''
3315 + PostgreSQL database name for the ${srv}.sr.ht service,
3316 + used if <xref linkend="opt-services.sourcehut.postgresql.enable"/> is <literal>true</literal>.
3317 + '';
3318 + };
3319 + };
3320
3321 - ${optionalString (builtins.hasAttr "migrate-on-upgrade" cfgIni && cfgIni.migrate-on-upgrade == "yes") ''
3322 - if [ "$(cat ${statePath}/db)" != "${serviceDrv.version}" ]; then
3323 - # Manage schema migrations using alembic
3324 - ${cfg.python}/bin/${serviceDrv.pname}-migrate -a upgrade head
3325 + gunicorn = {
3326 + extraArgs = mkOption {
3327 + type = with types; listOf str;
3328 + default = ["--timeout 120" "--workers 1" "--log-level=info"];
3329 + description = "Extra arguments passed to Gunicorn.";
3330 + };
3331 + };
3332 + } // optionalAttrs webhooks {
3333 + webhooks = {
3334 + extraArgs = mkOption {
3335 + type = with types; listOf str;
3336 + default = ["--loglevel DEBUG" "--pool eventlet" "--without-heartbeat"];
3337 + description = "Extra arguments passed to the Celery responsible for webhooks.";
3338 + };
3339 + celeryConfig = mkOption {
3340 + type = types.lines;
3341 + default = "";
3342 + description = "Content of the <literal>celeryconfig.py</literal> used by the Celery responsible for webhooks.";
3343 + };
3344 + };
3345 + };
3346
3347 - # Mark down current package version
3348 - printf "%s" "${serviceDrv.version}" > ${statePath}/db
3349 - fi
3350 - ''}
3351 + config = lib.mkIf (cfg.enable && srvCfg.enable) (mkMerge [ extraConfig {
3352 + users = {
3353 + users = {
3354 + "${srvCfg.user}" = {
3355 + isSystemUser = true;
3356 + group = mkDefault srvCfg.group;
3357 + description = mkDefault "sourcehut user for ${srv}.sr.ht";
3358 + };
3359 + };
3360 + groups = {
3361 + "${srvCfg.group}" = { };
3362 + } // optionalAttrs (cfg.postgresql.enable
3363 + && hasSuffix "0" (postgresql.settings.unix_socket_permissions or "")) {
3364 + "postgres".members = [ srvCfg.user ];
3365 + } // optionalAttrs (cfg.redis.enable
3366 + && hasSuffix "0" (redis.settings.unixsocketperm or "")) {
3367 + "redis-sourcehut-${srvsrht}".members = [ srvCfg.user ];
3368 + };
3369 + };
3370
3371 - ${attrs.preStart or ""}
3372 - '';
3373 + services.nginx = mkIf cfg.nginx.enable {
3374 + virtualHosts."${srv}.${cfg.settings."sr.ht".global-domain}" = mkMerge [ {
3375 + forceSSL = mkDefault true;
3376 + locations."/".proxyPass = "http://${cfg.listenAddress}:${toString srvCfg.port}";
3377 + locations."/static" = {
3378 + root = "${pkgs.sourcehut.${srvsrht}}/${pkgs.sourcehut.python.sitePackages}/${srvsrht}";
3379 + extraConfig = mkDefault ''
3380 + expires 30d;
3381 + '';
3382 + };
3383 + } cfg.nginx.virtualHost ];
3384 + };
3385 +
3386 + services.postgresql = mkIf cfg.postgresql.enable {
3387 + authentication = ''
3388 + local ${srvCfg.postgresql.database} ${srvCfg.user} trust
3389 + '';
3390 + ensureDatabases = [ srvCfg.postgresql.database ];
3391 + ensureUsers = map (name: {
3392 + inherit name;
3393 + ensurePermissions = { "DATABASE \"${srvCfg.postgresql.database}\"" = "ALL PRIVILEGES"; };
3394 + }) [srvCfg.user];
3395 + };
3396 +
3397 + services.sourcehut.services = mkDefault (filter (s: cfg.${s}.enable)
3398 + [ "builds" "dispatch" "git" "hg" "hub" "lists" "man" "meta" "pages" "paste" "todo" ]);
3399 +
3400 + services.sourcehut.settings = mkMerge [
3401 + {
3402 + "${srv}.sr.ht".origin = mkDefault "https://${srv}.${cfg.settings."sr.ht".global-domain}";
3403 + }
3404 +
3405 + (mkIf cfg.postgresql.enable {
3406 + "${srv}.sr.ht".connection-string = mkDefault "postgresql:///${srvCfg.postgresql.database}?user=${srvCfg.user}&host=/run/postgresql";
3407 + })
3408 + ];
3409 +
3410 + services.redis.servers."sourcehut-${srvsrht}" = mkIf cfg.redis.enable {
3411 + enable = true;
3412 + databases = 3;
3413 + syslog = true;
3414 + # TODO: set a more informed value
3415 + save = mkDefault [ [1800 10] [300 100] ];
3416 + settings = {
3417 + # TODO: set a more informed value
3418 + maxmemory = "128MB";
3419 + maxmemory-policy = "volatile-ttl";
3420 + };
3421 + };
3422 +
3423 + systemd.services = mkMerge [
3424 + {
3425 + "${srvsrht}" = baseService srvsrht { allowStripe = srv == "meta"; } (mkMerge [
3426 + {
3427 + description = "sourcehut ${srv}.sr.ht website service";
3428 + before = optional cfg.nginx.enable "nginx.service";
3429 + wants = optional cfg.nginx.enable "nginx.service";
3430 + wantedBy = [ "multi-user.target" ];
3431 + path = optional cfg.postgresql.enable postgresql.package;
3432 + # Beware: change in credentials' content will not trigger restart.
3433 + restartTriggers = [ configIni ];
3434 + serviceConfig = {
3435 + Type = "simple";
3436 + Restart = mkDefault "always";
3437 + #RestartSec = mkDefault "2min";
3438 + StateDirectory = [ "sourcehut/${srvsrht}" ];
3439 + StateDirectoryMode = "2750";
3440 + ExecStart = "${cfg.python}/bin/gunicorn ${srvsrht}.app:app --name ${srvsrht} --bind ${cfg.listenAddress}:${toString srvCfg.port} " + concatStringsSep " " srvCfg.gunicorn.extraArgs;
3441 + };
3442 + preStart = let
3443 + version = pkgs.sourcehut.${srvsrht}.version;
3444 + stateDir = "/var/lib/sourcehut/${srvsrht}";
3445 + in mkBefore ''
3446 + set -x
3447 + # Use the /run/sourcehut/${srvsrht}/config.ini
3448 + # installed by a previous ExecStartPre= in baseService
3449 + cd /run/sourcehut/${srvsrht}
3450 +
3451 + if test ! -e ${stateDir}/db; then
3452 + # Setup the initial database.
3453 + # Note that it stamps the alembic head afterward
3454 + ${cfg.python}/bin/${srvsrht}-initdb
3455 + echo ${version} >${stateDir}/db
3456 + fi
3457 +
3458 + ${optionalString cfg.settings.${iniKey}.migrate-on-upgrade ''
3459 + if [ "$(cat ${stateDir}/db)" != "${version}" ]; then
3460 + # Manage schema migrations using alembic
3461 + ${cfg.python}/bin/${srvsrht}-migrate -a upgrade head
3462 + echo ${version} >${stateDir}/db
3463 + fi
3464 + ''}
3465 +
3466 + # Update copy of each users' profile to the latest
3467 + # See https://lists.sr.ht/~sircmpwn/sr.ht-admins/<20190302181207.GA13778%40cirno.my.domain>
3468 + if test ! -e ${stateDir}/webhook; then
3469 + # Update ${iniKey}'s users' profile copy to the latest
3470 + ${cfg.python}/bin/srht-update-profiles ${iniKey}
3471 + touch ${stateDir}/webhook
3472 + fi
3473 + '';
3474 + } mainService ]);
3475 + }
3476 +
3477 + (mkIf webhooks {
3478 + "${srvsrht}-webhooks" = baseService "${srvsrht}-webhooks" {}
3479 + {
3480 + description = "sourcehut ${srv}.sr.ht webhooks service";
3481 + after = [ "${srvsrht}.service" ];
3482 + wantedBy = [ "${srvsrht}.service" ];
3483 + partOf = [ "${srvsrht}.service" ];
3484 + preStart = ''
3485 + cp ${pkgs.writeText "${srvsrht}-webhooks-celeryconfig.py" srvCfg.webhooks.celeryConfig} \
3486 + /run/sourcehut/${srvsrht}-webhooks/celeryconfig.py
3487 + '';
3488 + serviceConfig = {
3489 + Type = "simple";
3490 + Restart = "always";
3491 + ExecStart = "${cfg.python}/bin/celery --app ${srvsrht}.webhooks worker --hostname ${srvsrht}-webhooks@%%h " + concatStringsSep " " srvCfg.webhooks.extraArgs;
3492 + # Avoid crashing: os.getloadavg()
3493 + ProcSubset = mkForce "all";
3494 + };
3495 + };
3496 + })
3497 +
3498 + (mapAttrs (timerName: timer: (baseService timerName {} (mkMerge [
3499 + {
3500 + description = "sourcehut ${timerName} service";
3501 + after = [ "network.target" "${srvsrht}.service" ];
3502 + serviceConfig = {
3503 + Type = "oneshot";
3504 + ExecStart = "${cfg.python}/bin/${timerName}";
3505 + };
3506 + }
3507 + (timer.service or {})
3508 + ]))) extraTimers)
3509 +
3510 + (mapAttrs (serviceName: extraService: baseService serviceName {} (mkMerge [
3511 + {
3512 + description = "sourcehut ${serviceName} service";
3513 + # So that extraServices have the PostgreSQL database initialized.
3514 + after = [ "${srvsrht}.service" ];
3515 + wantedBy = [ "${srvsrht}.service" ];
3516 + partOf = [ "${srvsrht}.service" ];
3517 + serviceConfig = {
3518 + Type = "simple";
3519 + Restart = mkDefault "always";
3520 + };
3521 + }
3522 + extraService
3523 + ])) extraServices)
3524 + ];
3525 +
3526 + systemd.timers = mapAttrs (timerName: timer:
3527 + {
3528 + description = "sourcehut timer for ${timerName}";
3529 + wantedBy = [ "timers.target" ];
3530 + inherit (timer) timerConfig;
3531 + }) extraTimers;
3532 + } ]);
3533 }
3534 - (builtins.removeAttrs attrs [ "path" "preStart" ])
3535 diff --git a/nixos/modules/services/misc/sourcehut/sourcehut.xml b/nixos/modules/services/misc/sourcehut/sourcehut.xml
3536 index ab9a8c6cb4b..41094f65a94 100644
3537 --- a/nixos/modules/services/misc/sourcehut/sourcehut.xml
3538 +++ b/nixos/modules/services/misc/sourcehut/sourcehut.xml
3539 @@ -14,13 +14,12 @@
3540 <title>Basic usage</title>
3541 <para>
3542 Sourcehut is a Python and Go based set of applications.
3543 - <literal><link linkend="opt-services.sourcehut.enable">services.sourcehut</link></literal>
3544 - by default will use
3545 + This NixOS module also provides basic configuration integrating Sourcehut into locally running
3546 <literal><link linkend="opt-services.nginx.enable">services.nginx</link></literal>,
3547 - <literal><link linkend="opt-services.nginx.enable">services.redis</link></literal>,
3548 - <literal><link linkend="opt-services.nginx.enable">services.cron</link></literal>,
3549 + <literal><link linkend="opt-services.redis.servers">services.redis.servers.sourcehut</link></literal>,
3550 + <literal><link linkend="opt-services.postfix.enable">services.postfix</link></literal>
3551 and
3552 - <literal><link linkend="opt-services.postgresql.enable">services.postgresql</link></literal>.
3553 + <literal><link linkend="opt-services.postgresql.enable">services.postgresql</link></literal> services.
3554 </para>
3555
3556 <para>
3557 @@ -42,18 +41,23 @@ in {
3558
3559 services.sourcehut = {
3560 <link linkend="opt-services.sourcehut.enable">enable</link> = true;
3561 - <link linkend="opt-services.sourcehut.originBase">originBase</link> = fqdn;
3562 - <link linkend="opt-services.sourcehut.services">services</link> = [ "meta" "man" "git" ];
3563 + <link linkend="opt-services.sourcehut.git.enable">git.enable</link> = true;
3564 + <link linkend="opt-services.sourcehut.man.enable">man.enable</link> = true;
3565 + <link linkend="opt-services.sourcehut.meta.enable">meta.enable</link> = true;
3566 + <link linkend="opt-services.sourcehut.nginx.enable">nginx.enable</link> = true;
3567 + <link linkend="opt-services.sourcehut.postfix.enable">postfix.enable</link> = true;
3568 + <link linkend="opt-services.sourcehut.postgresql.enable">postgresql.enable</link> = true;
3569 + <link linkend="opt-services.sourcehut.redis.enable">redis.enable</link> = true;
3570 <link linkend="opt-services.sourcehut.settings">settings</link> = {
3571 "sr.ht" = {
3572 environment = "production";
3573 global-domain = fqdn;
3574 origin = "https://${fqdn}";
3575 # Produce keys with srht-keygen from <package>sourcehut.coresrht</package>.
3576 - network-key = "SECRET";
3577 - service-key = "SECRET";
3578 + network-key = "/run/keys/path/to/network-key";
3579 + service-key = "/run/keys/path/to/service-key";
3580 };
3581 - webhooks.private-key= "SECRET";
3582 + webhooks.private-key= "/run/keys/path/to/webhook-key";
3583 };
3584 };
3585
3586 diff --git a/nixos/modules/services/misc/sourcehut/todo.nix b/nixos/modules/services/misc/sourcehut/todo.nix
3587 deleted file mode 100644
3588 index aec773b0669..00000000000
3589 --- a/nixos/modules/services/misc/sourcehut/todo.nix
3590 +++ /dev/null
3591 @@ -1,161 +0,0 @@
3592 -{ config, lib, pkgs, ... }:
3593 -
3594 -with lib;
3595 -let
3596 - cfg = config.services.sourcehut;
3597 - cfgIni = cfg.settings;
3598 - scfg = cfg.todo;
3599 - iniKey = "todo.sr.ht";
3600 -
3601 - rcfg = config.services.redis;
3602 - drv = pkgs.sourcehut.todosrht;
3603 -in
3604 -{
3605 - options.services.sourcehut.todo = {
3606 - user = mkOption {
3607 - type = types.str;
3608 - default = "todosrht";
3609 - description = ''
3610 - User for todo.sr.ht.
3611 - '';
3612 - };
3613 -
3614 - port = mkOption {
3615 - type = types.port;
3616 - default = 5003;
3617 - description = ''
3618 - Port on which the "todo" module should listen.
3619 - '';
3620 - };
3621 -
3622 - database = mkOption {
3623 - type = types.str;
3624 - default = "todo.sr.ht";
3625 - description = ''
3626 - PostgreSQL database name for todo.sr.ht.
3627 - '';
3628 - };
3629 -
3630 - statePath = mkOption {
3631 - type = types.path;
3632 - default = "${cfg.statePath}/todosrht";
3633 - description = ''
3634 - State path for todo.sr.ht.
3635 - '';
3636 - };
3637 - };
3638 -
3639 - config = with scfg; lib.mkIf (cfg.enable && elem "todo" cfg.services) {
3640 - users = {
3641 - users = {
3642 - "${user}" = {
3643 - isSystemUser = true;
3644 - group = user;
3645 - extraGroups = [ "postfix" ];
3646 - description = "todo.sr.ht user";
3647 - };
3648 - };
3649 - groups = {
3650 - "${user}" = { };
3651 - };
3652 - };
3653 -
3654 - services.postgresql = {
3655 - authentication = ''
3656 - local ${database} ${user} trust
3657 - '';
3658 - ensureDatabases = [ database ];
3659 - ensureUsers = [
3660 - {
3661 - name = user;
3662 - ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
3663 - }
3664 - ];
3665 - };
3666 -
3667 - systemd = {
3668 - tmpfiles.rules = [
3669 - "d ${statePath} 0750 ${user} ${user} -"
3670 - ];
3671 -
3672 - services = {
3673 - todosrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
3674 - after = [ "postgresql.service" "network.target" ];
3675 - requires = [ "postgresql.service" ];
3676 - wantedBy = [ "multi-user.target" ];
3677 -
3678 - description = "todo.sr.ht website service";
3679 -
3680 - serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
3681 - };
3682 -
3683 - todosrht-lmtp = {
3684 - after = [ "postgresql.service" "network.target" ];
3685 - bindsTo = [ "postgresql.service" ];
3686 - wantedBy = [ "multi-user.target" ];
3687 -
3688 - description = "todo.sr.ht process service";
3689 - serviceConfig = {
3690 - Type = "simple";
3691 - User = user;
3692 - Restart = "always";
3693 - ExecStart = "${cfg.python}/bin/todosrht-lmtp";
3694 - };
3695 - };
3696 -
3697 - todosrht-webhooks = {
3698 - after = [ "postgresql.service" "network.target" ];
3699 - requires = [ "postgresql.service" ];
3700 - wantedBy = [ "multi-user.target" ];
3701 -
3702 - description = "todo.sr.ht webhooks service";
3703 - serviceConfig = {
3704 - Type = "simple";
3705 - User = user;
3706 - Restart = "always";
3707 - ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
3708 - };
3709 -
3710 - };
3711 - };
3712 - };
3713 -
3714 - services.sourcehut.settings = {
3715 - # URL todo.sr.ht is being served at (protocol://domain)
3716 - "todo.sr.ht".origin = mkDefault "http://todo.${cfg.originBase}";
3717 - # Address and port to bind the debug server to
3718 - "todo.sr.ht".debug-host = mkDefault "0.0.0.0";
3719 - "todo.sr.ht".debug-port = mkDefault port;
3720 - # Configures the SQLAlchemy connection string for the database.
3721 - "todo.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
3722 - # Set to "yes" to automatically run migrations on package upgrade.
3723 - "todo.sr.ht".migrate-on-upgrade = mkDefault "yes";
3724 - # todo.sr.ht's OAuth client ID and secret for meta.sr.ht
3725 - # Register your client at meta.example.org/oauth
3726 - "todo.sr.ht".oauth-client-id = mkDefault null;
3727 - "todo.sr.ht".oauth-client-secret = mkDefault null;
3728 - # Outgoing email for notifications generated by users
3729 - "todo.sr.ht".notify-from = mkDefault "CHANGEME@example.org";
3730 - # The redis connection used for the webhooks worker
3731 - "todo.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/1";
3732 - # Network-key
3733 - "todo.sr.ht".network-key = mkDefault null;
3734 -
3735 - # Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
3736 - # Alternatively, specify IP:PORT and an SMTP server will be run instead.
3737 - "todo.sr.ht::mail".sock = mkDefault "/tmp/todo.sr.ht-lmtp.sock";
3738 - # The lmtp daemon will make the unix socket group-read/write for users in this
3739 - # group.
3740 - "todo.sr.ht::mail".sock-group = mkDefault "postfix";
3741 -
3742 - "todo.sr.ht::mail".posting-domain = mkDefault "todo.${cfg.originBase}";
3743 - };
3744 -
3745 - services.nginx.virtualHosts."todo.${cfg.originBase}" = {
3746 - forceSSL = true;
3747 - locations."/".proxyPass = "http://${cfg.address}:${toString port}";
3748 - locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
3749 - locations."/static".root = "${pkgs.sourcehut.todosrht}/${pkgs.sourcehut.python.sitePackages}/todosrht";
3750 - };
3751 - };
3752 -}
3753 diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
3754 index f86cc2544da..0ccd312b304 100644
3755 --- a/nixos/tests/all-tests.nix
3756 +++ b/nixos/tests/all-tests.nix
3757 @@ -432,6 +432,7 @@ in
3758 solanum = handleTest ./solanum.nix {};
3759 solr = handleTest ./solr.nix {};
3760 sonarr = handleTest ./sonarr.nix {};
3761 + sourcehut = handleTest ./sourcehut.nix {};
3762 spacecookie = handleTest ./spacecookie.nix {};
3763 spark = handleTestOn ["x86_64-linux"] ./spark {};
3764 sslh = handleTest ./sslh.nix {};
3765 diff --git a/nixos/tests/sourcehut.nix b/nixos/tests/sourcehut.nix
3766 index b56a14ebf85..d1536c59322 100644
3767 --- a/nixos/tests/sourcehut.nix
3768 +++ b/nixos/tests/sourcehut.nix
3769 @@ -1,29 +1,197 @@
3770 -import ./make-test-python.nix ({ pkgs, ... }:
3771 +import ./make-test-python.nix ({ pkgs, lib, ... }:
3772 +let
3773 + domain = "sourcehut.localdomain";
3774
3775 + # Note that wildcard certificates just under the TLD (eg. *.com)
3776 + # would be rejected by clients like curl.
3777 + tls-cert = pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } ''
3778 + openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -days 36500 \
3779 + -subj '/CN=${domain}' -extensions v3_req \
3780 + -addext 'subjectAltName = DNS:*.${domain}'
3781 + install -D -t $out key.pem cert.pem
3782 + '';
3783 +
3784 + images = {
3785 + nixos.unstable.x86_64 =
3786 + let
3787 + systemConfig = { pkgs, ... }: {
3788 + # passwordless ssh server
3789 + services.openssh = {
3790 + enable = true;
3791 + permitRootLogin = "yes";
3792 + extraConfig = "PermitEmptyPasswords yes";
3793 + };
3794 +
3795 + users = {
3796 + mutableUsers = false;
3797 + # build user
3798 + extraUsers."build" = {
3799 + isNormalUser = true;
3800 + uid = 1000;
3801 + extraGroups = [ "wheel" ];
3802 + password = "";
3803 + };
3804 + users.root.password = "";
3805 + };
3806 +
3807 + security.sudo.wheelNeedsPassword = false;
3808 + nix.trustedUsers = [ "root" "build" ];
3809 + documentation.nixos.enable = false;
3810 +
3811 + # builds.sr.ht-image-specific network settings
3812 + networking = {
3813 + hostName = "build";
3814 + dhcpcd.enable = false;
3815 + defaultGateway.address = "10.0.2.2";
3816 + usePredictableInterfaceNames = false;
3817 + interfaces."eth0".ipv4.addresses = [{
3818 + address = "10.0.2.15";
3819 + prefixLength = 25;
3820 + }];
3821 + enableIPv6 = false;
3822 + nameservers = [
3823 + # OpenNIC anycast
3824 + "185.121.177.177"
3825 + "169.239.202.202"
3826 + # Google
3827 + "8.8.8.8"
3828 + ];
3829 + firewall.allowedTCPPorts = [ 22 ];
3830 + };
3831 +
3832 + environment.systemPackages = [
3833 + pkgs.gitMinimal
3834 + #pkgs.mercurial
3835 + pkgs.curl
3836 + pkgs.gnupg
3837 + ];
3838 + };
3839 + qemuConfig = { pkgs, ... }: {
3840 + imports = [ systemConfig ];
3841 + fileSystems."/".device = "/dev/disk/by-label/nixos";
3842 + boot.initrd.availableKernelModules = [
3843 + "ahci"
3844 + "ehci_pci"
3845 + "sd_mod"
3846 + "usb_storage"
3847 + "usbhid"
3848 + "virtio_balloon"
3849 + "virtio_blk"
3850 + "virtio_pci"
3851 + "virtio_ring"
3852 + "xhci_pci"
3853 + ];
3854 + boot.loader = {
3855 + grub = {
3856 + version = 2;
3857 + device = "/dev/vda";
3858 + };
3859 + timeout = 0;
3860 + };
3861 + };
3862 + config = (import (pkgs.path + "/nixos/lib/eval-config.nix") {
3863 + inherit pkgs; modules = [ qemuConfig ];
3864 + system = "x86_64-linux";
3865 + }).config;
3866 + in
3867 + import (pkgs.path + "/nixos/lib/make-disk-image.nix") {
3868 + inherit pkgs lib config;
3869 + diskSize = 16000;
3870 + format = "qcow2-compressed";
3871 + contents = [
3872 + { source = pkgs.writeText "gitconfig" ''
3873 + [user]
3874 + name = builds.sr.ht
3875 + email = build@sr.ht
3876 + '';
3877 + target = "/home/build/.gitconfig";
3878 + user = "build";
3879 + group = "users";
3880 + mode = "644";
3881 + }
3882 + ];
3883 + };
3884 + };
3885 +
3886 +in
3887 {
3888 name = "sourcehut";
3889
3890 meta.maintainers = [ pkgs.lib.maintainers.tomberek ];
3891
3892 - machine = { config, pkgs, ... }: {
3893 - virtualisation.memorySize = 2048;
3894 - networking.firewall.allowedTCPPorts = [ 80 ];
3895 + machine = { config, pkgs, nodes, ... }: {
3896 + # buildsrht needs space
3897 + virtualisation.diskSize = 4 * 1024;
3898 + virtualisation.memorySize = 2 * 1024;
3899 + networking.domain = domain;
3900 + networking.extraHosts = ''
3901 + ${config.networking.primaryIPAddress} meta.${domain}
3902 + ${config.networking.primaryIPAddress} builds.${domain}
3903 + '';
3904
3905 services.sourcehut = {
3906 enable = true;
3907 - services = [ "meta" ];
3908 - originBase = "sourcehut";
3909 - settings."sr.ht".service-key = "8888888888888888888888888888888888888888888888888888888888888888";
3910 - settings."sr.ht".network-key = "0000000000000000000000000000000000000000000=";
3911 - settings.webhooks.private-key = "0000000000000000000000000000000000000000000=";
3912 + services = [ "meta" "builds" ];
3913 + nginx.enable = true;
3914 + nginx.virtualHost = {
3915 + forceSSL = true;
3916 + sslCertificate = "${tls-cert}/cert.pem";
3917 + sslCertificateKey = "${tls-cert}/key.pem";
3918 + };
3919 + postgresql.enable = true;
3920 + redis.enable = true;
3921 +
3922 + meta.enable = true;
3923 + builds = {
3924 + enable = true;
3925 + # FIXME: see why it does not seem to activate fully.
3926 + #enableWorker = true;
3927 + inherit images;
3928 + };
3929 + settings."sr.ht" = {
3930 + global-domain = config.networking.domain;
3931 + service-key = pkgs.writeText "service-key" "8b327279b77e32a3620e2fc9aabce491cc46e7d821fd6713b2a2e650ce114d01";
3932 + network-key = pkgs.writeText "network-key" "cEEmc30BRBGkgQZcHFksiG7hjc6_dK1XR2Oo5Jb9_nQ=";
3933 + };
3934 + settings."builds.sr.ht" = {
3935 + oauth-client-secret = pkgs.writeText "buildsrht-oauth-client-secret" "2260e9c4d9b8dcedcef642860e0504bc";
3936 + oauth-client-id = "299db9f9c2013170";
3937 + };
3938 + settings.webhooks.private-key = pkgs.writeText "webhook-key" "Ra3IjxgFiwG9jxgp4WALQIZw/BMYt30xWiOsqD0J7EA=";
3939 + };
3940 +
3941 + networking.firewall.allowedTCPPorts = [ 443 ];
3942 + security.pki.certificateFiles = [ "${tls-cert}/cert.pem" ];
3943 + services.nginx = {
3944 + enable = true;
3945 + recommendedGzipSettings = true;
3946 + recommendedOptimisation = true;
3947 + recommendedTlsSettings = true;
3948 + recommendedProxySettings = true;
3949 + };
3950 +
3951 + services.postgresql = {
3952 + enable = true;
3953 + enableTCPIP = false;
3954 + settings.unix_socket_permissions = "0770";
3955 };
3956 };
3957
3958 testScript = ''
3959 start_all()
3960 machine.wait_for_unit("multi-user.target")
3961 +
3962 + # Testing metasrht
3963 + machine.wait_for_unit("metasrht-api.service")
3964 machine.wait_for_unit("metasrht.service")
3965 machine.wait_for_open_port(5000)
3966 - machine.succeed("curl -sL http://localhost:5000 | grep meta.sourcehut")
3967 + machine.succeed("curl -sL http://localhost:5000 | grep meta.${domain}")
3968 + machine.succeed("curl -sL http://meta.${domain} | grep meta.${domain}")
3969 +
3970 + # Testing buildsrht
3971 + machine.wait_for_unit("buildsrht.service")
3972 + machine.wait_for_open_port(5002)
3973 + machine.succeed("curl -sL http://localhost:5002 | grep builds.${domain}")
3974 + #machine.wait_for_unit("buildsrht-worker.service")
3975 '';
3976 })
3977 diff --git a/pkgs/applications/version-management/sourcehut/builds.nix b/pkgs/applications/version-management/sourcehut/builds.nix
3978 index c8163caf8ea..df0df206573 100644
3979 --- a/pkgs/applications/version-management/sourcehut/builds.nix
3980 +++ b/pkgs/applications/version-management/sourcehut/builds.nix
3981 @@ -11,13 +11,20 @@
3982 , python
3983 }:
3984 let
3985 - version = "0.66.7";
3986 + version = "0.74.2";
3987 +
3988 + src = fetchFromSourcehut {
3989 + owner = "~sircmpwn";
3990 + repo = "builds.sr.ht";
3991 + rev = version;
3992 + sha256 = "sha256-vdVKaI42pA0dnyMXhQ4AEaDgTtKcrH6hc9L6PFcl6ZA=";
3993 + };
3994
3995 buildWorker = src: buildGoModule {
3996 inherit src version;
3997 pname = "builds-sr-ht-worker";
3998
3999 - vendorSha256 = "sha256-giOaldV46aBqXyFH/cQVsbUr6Rb4VMhbBO86o48tRZY=";
4000 + vendorSha256 = "sha256-ZEarWM/33t+pNXUEIpfd/DkBkhu3UUg17Hh8XXWOepA=";
4001 };
4002 in
4003 buildPythonPackage rec {
4004 @@ -28,7 +35,7 @@ buildPythonPackage rec {
4005 owner = "~sircmpwn";
4006 repo = "builds.sr.ht";
4007 rev = version;
4008 - sha256 = "sha256-2MLs/DOXHjEYarXDVUcPZe3o0fmZbzVxn528SE72lhM=";
4009 + sha256 = "sha256-c2xp2uIP8+WeRMz0efA1H58Nkot65bc03e7rrrZk3jo=";
4010 };
4011
4012 nativeBuildInputs = srht.nativeBuildInputs;
4013 @@ -56,10 +63,12 @@ buildPythonPackage rec {
4014 cp ${buildWorker "${src}/worker"}/bin/worker $out/bin/builds.sr.ht-worker
4015 '';
4016
4017 + pythonImportsCheck = [ "buildsrht" ];
4018 +
4019 meta = with lib; {
4020 homepage = "https://git.sr.ht/~sircmpwn/builds.sr.ht";
4021 description = "Continuous integration service for the sr.ht network";
4022 - license = licenses.agpl3;
4023 + license = licenses.agpl3Only;
4024 maintainers = with maintainers; [ eadwu ];
4025 };
4026 }
4027 diff --git a/pkgs/applications/version-management/sourcehut/core.nix b/pkgs/applications/version-management/sourcehut/core.nix
4028 index 7c3a516ed9d..d5bbd2927b6 100644
4029 --- a/pkgs/applications/version-management/sourcehut/core.nix
4030 +++ b/pkgs/applications/version-management/sourcehut/core.nix
4031 @@ -25,17 +25,16 @@
4032 , sassc
4033 , nodejs
4034 , redis
4035 -, writeText
4036 }:
4037
4038 buildPythonPackage rec {
4039 pname = "srht";
4040 - version = "0.67.4";
4041 + version = "0.68.2";
4042
4043 src = fetchgit {
4044 url = "https://git.sr.ht/~sircmpwn/core.sr.ht";
4045 rev = version;
4046 - sha256 = "sha256-XvzFfcBK5Mq8p7xEBAF/eupUE1kkUBh5k+ByM/WA9bc=";
4047 + sha256 = "sha256-vLdKJgi3arF5PSODbCSCX7fWGZfgkOirGSdYzcNXRdk=";
4048 fetchSubmodules = true;
4049 };
4050
4051 @@ -46,6 +45,7 @@ buildPythonPackage rec {
4052 };
4053
4054 patches = [
4055 + # Disable check for npm
4056 ./disable-npm-install.patch
4057 ];
4058
4059 @@ -87,6 +87,7 @@ buildPythonPackage rec {
4060 '';
4061
4062 dontUseSetuptoolsCheck = true;
4063 + pythonImportsCheck = [ "srht" ];
4064
4065 meta = with lib; {
4066 homepage = "https://git.sr.ht/~sircmpwn/srht";
4067 diff --git a/pkgs/applications/version-management/sourcehut/default.nix b/pkgs/applications/version-management/sourcehut/default.nix
4068 index 401a1437b7d..00810f208cc 100644
4069 --- a/pkgs/applications/version-management/sourcehut/default.nix
4070 +++ b/pkgs/applications/version-management/sourcehut/default.nix
4071 @@ -22,6 +22,7 @@ let
4072 listssrht = self.callPackage ./lists.nix { };
4073 mansrht = self.callPackage ./man.nix { };
4074 metasrht = self.callPackage ./meta.nix { };
4075 + pagessrht = self.callPackage ./pages.nix { };
4076 pastesrht = self.callPackage ./paste.nix { };
4077 todosrht = self.callPackage ./todo.nix { };
4078
4079 @@ -40,6 +41,7 @@ with python.pkgs; recurseIntoAttrs {
4080 listssrht = toPythonApplication listssrht;
4081 mansrht = toPythonApplication mansrht;
4082 metasrht = toPythonApplication metasrht;
4083 + pagessrht = pagessrht;
4084 pastesrht = toPythonApplication pastesrht;
4085 todosrht = toPythonApplication todosrht;
4086 }
4087 diff --git a/pkgs/applications/version-management/sourcehut/dispatch.nix b/pkgs/applications/version-management/sourcehut/dispatch.nix
4088 index 637c6f9c1df..9456d0c998c 100644
4089 --- a/pkgs/applications/version-management/sourcehut/dispatch.nix
4090 +++ b/pkgs/applications/version-management/sourcehut/dispatch.nix
4091 @@ -9,13 +9,13 @@
4092
4093 buildPythonPackage rec {
4094 pname = "dispatchsrht";
4095 - version = "0.15.8";
4096 + version = "0.15.32";
4097
4098 src = fetchFromSourcehut {
4099 owner = "~sircmpwn";
4100 repo = "dispatch.sr.ht";
4101 rev = version;
4102 - sha256 = "sha256-zWCGPjIgMKHXHJUs9aciV7IFgo0rpahon6KXHDwcfss=";
4103 + sha256 = "sha256-4P4cXhjcZ8IBzpRfmYIJkzl9U4Plo36a48Pf/KjmhFY=";
4104 };
4105
4106 nativeBuildInputs = srht.nativeBuildInputs;
4107 @@ -31,10 +31,12 @@ buildPythonPackage rec {
4108 export SRHT_PATH=${srht}/${python.sitePackages}/srht
4109 '';
4110
4111 + pythonImportsCheck = [ "dispatchsrht" ];
4112 +
4113 meta = with lib; {
4114 homepage = "https://dispatch.sr.ht/~sircmpwn/dispatch.sr.ht";
4115 description = "Task dispatcher and service integration tool for the sr.ht network";
4116 - license = licenses.agpl3;
4117 + license = licenses.agpl3Only;
4118 maintainers = with maintainers; [ eadwu ];
4119 };
4120 }
4121 diff --git a/pkgs/applications/version-management/sourcehut/git.nix b/pkgs/applications/version-management/sourcehut/git.nix
4122 index e44fb9cd6c6..1ec331f4353 100644
4123 --- a/pkgs/applications/version-management/sourcehut/git.nix
4124 +++ b/pkgs/applications/version-management/sourcehut/git.nix
4125 @@ -8,13 +8,13 @@
4126 , scmsrht
4127 }:
4128 let
4129 - version = "0.72.8";
4130 + version = "0.73.6";
4131
4132 src = fetchFromSourcehut {
4133 owner = "~sircmpwn";
4134 repo = "git.sr.ht";
4135 rev = version;
4136 - sha256 = "sha256-AB2uzajO5PtcpJfbOOTfuDFM6is5K39v3AZJ1hShRNc=";
4137 + sha256 = "sha256-9WdeHXmyX5K/ldaE9AbSWTRlsrSDuKtm8QhthEHfmuU=";
4138 };
4139
4140 buildShell = src: buildGoModule {
4141 @@ -32,12 +32,14 @@ let
4142 buildKeys = src: buildGoModule {
4143 inherit src version;
4144 pname = "gitsrht-keys";
4145 - vendorSha256 = "1d94cqy7x0q0agwg515xxsbl70b3qrzxbzsyjhn1pbyj532brn7f";
4146 + vendorSha256 = "sha256-SOI7wimFthY+BwsDtMuyqKS1hCaEa3R90Q0qaA9boyE=";
4147 };
4148
4149 buildUpdateHook = src: buildGoModule {
4150 inherit src version;
4151 pname = "gitsrht-update-hook";
4152 + vendorSha256 = "sha256-L/tGwbBSwhtGhHcinCK/0lsp1ffXjiHXCmGgsY9s2Nc=";
4153 +
4154 vendorSha256 = "0fwzqpjv8x5y3w3bfjd0x0cvqjjak23m0zj88hf32jpw49xmjkih";
4155 };
4156
4157 @@ -72,10 +74,12 @@ buildPythonPackage rec {
4158 inherit updateHook;
4159 };
4160
4161 + pythonImportsCheck = [ "gitsrht" ];
4162 +
4163 meta = with lib; {
4164 homepage = "https://git.sr.ht/~sircmpwn/git.sr.ht";
4165 description = "Git repository hosting service for the sr.ht network";
4166 - license = licenses.agpl3;
4167 + license = licenses.agpl3Only;
4168 maintainers = with maintainers; [ eadwu ];
4169 };
4170 }
4171 diff --git a/pkgs/applications/version-management/sourcehut/hg.nix b/pkgs/applications/version-management/sourcehut/hg.nix
4172 index cddb76cabf2..e13b27d4139 100644
4173 --- a/pkgs/applications/version-management/sourcehut/hg.nix
4174 +++ b/pkgs/applications/version-management/sourcehut/hg.nix
4175 @@ -10,12 +10,12 @@
4176
4177 buildPythonPackage rec {
4178 pname = "hgsrht";
4179 - version = "0.27.4";
4180 + version = "0.28.1";
4181
4182 src = fetchhg {
4183 url = "https://hg.sr.ht/~sircmpwn/hg.sr.ht";
4184 rev = version;
4185 - sha256 = "1c0qfi0gmbfngvds6917fy9ii2iglawn429757rh7b4bvzn7n6mr";
4186 + sha256 = "ERMPaCtExZebwV1BrjyE/gGK7p/Nvf7ia+ZBO472bdw=";
4187 };
4188
4189 nativeBuildInputs = srht.nativeBuildInputs;
4190 @@ -32,10 +32,12 @@ buildPythonPackage rec {
4191 export SRHT_PATH=${srht}/${python.sitePackages}/srht
4192 '';
4193
4194 + pythonImportsCheck = [ "hgsrht" ];
4195 +
4196 meta = with lib; {
4197 homepage = "https://git.sr.ht/~sircmpwn/hg.sr.ht";
4198 description = "Mercurial repository hosting service for the sr.ht network";
4199 - license = licenses.agpl3;
4200 + license = licenses.agpl3Only;
4201 maintainers = with maintainers; [ eadwu ];
4202 };
4203 }
4204 diff --git a/pkgs/applications/version-management/sourcehut/hub.nix b/pkgs/applications/version-management/sourcehut/hub.nix
4205 index 17cb3fe4b61..9c834246def 100644
4206 --- a/pkgs/applications/version-management/sourcehut/hub.nix
4207 +++ b/pkgs/applications/version-management/sourcehut/hub.nix
4208 @@ -6,13 +6,13 @@
4209
4210 buildPythonPackage rec {
4211 pname = "hubsrht";
4212 - version = "0.13.1";
4213 + version = "0.13.16";
4214
4215 src = fetchFromSourcehut {
4216 owner = "~sircmpwn";
4217 repo = "hub.sr.ht";
4218 rev = version;
4219 - sha256 = "sha256-Kqzy4mh5Nn1emzHBco/LVuXro/tW3NX+OYqdEwBSQ/U=";
4220 + sha256 = "sha256-TyeEJvkRjqTs92+pKloJhpTnnkYaMZHZkdO6Da5Bq7c=";
4221 };
4222
4223 nativeBuildInputs = srht.nativeBuildInputs;
4224 @@ -26,11 +26,12 @@ buildPythonPackage rec {
4225 '';
4226
4227 dontUseSetuptoolsCheck = true;
4228 + pythonImportsCheck = [ "hubsrht" ];
4229
4230 meta = with lib; {
4231 homepage = "https://git.sr.ht/~sircmpwn/hub.sr.ht";
4232 description = "Project hub service for the sr.ht network";
4233 - license = licenses.agpl3;
4234 + license = licenses.agpl3Only;
4235 maintainers = with maintainers; [ eadwu ];
4236 };
4237 }
4238 diff --git a/pkgs/applications/version-management/sourcehut/lists.nix b/pkgs/applications/version-management/sourcehut/lists.nix
4239 index b419b49f7b5..4ffc2ac9dee 100644
4240 --- a/pkgs/applications/version-management/sourcehut/lists.nix
4241 +++ b/pkgs/applications/version-management/sourcehut/lists.nix
4242 @@ -12,13 +12,13 @@
4243
4244 buildPythonPackage rec {
4245 pname = "listssrht";
4246 - version = "0.48.19";
4247 + version = "0.50.2";
4248
4249 src = fetchFromSourcehut {
4250 owner = "~sircmpwn";
4251 repo = "lists.sr.ht";
4252 rev = version;
4253 - sha256 = "sha256-bsakEMyvWaxiE4/SGcAP4mlGG9jkdHfFxpt9H+TJn/8=";
4254 + sha256 = "sha256-2NO6WJCOwCqGuICnn425NbnemTm8vYBltJyrveUt1n0=";
4255 };
4256
4257 nativeBuildInputs = srht.nativeBuildInputs;
4258 @@ -37,10 +37,12 @@ buildPythonPackage rec {
4259 export SRHT_PATH=${srht}/${python.sitePackages}/srht
4260 '';
4261
4262 + pythonImportsCheck = [ "listssrht" ];
4263 +
4264 meta = with lib; {
4265 homepage = "https://git.sr.ht/~sircmpwn/lists.sr.ht";
4266 description = "Mailing list service for the sr.ht network";
4267 - license = licenses.agpl3;
4268 + license = licenses.agpl3Only;
4269 maintainers = with maintainers; [ eadwu ];
4270 };
4271 }
4272 diff --git a/pkgs/applications/version-management/sourcehut/man.nix b/pkgs/applications/version-management/sourcehut/man.nix
4273 index bd331f000a7..2d4d152e3aa 100644
4274 --- a/pkgs/applications/version-management/sourcehut/man.nix
4275 +++ b/pkgs/applications/version-management/sourcehut/man.nix
4276 @@ -8,13 +8,13 @@
4277
4278 buildPythonPackage rec {
4279 pname = "mansrht";
4280 - version = "0.15.12";
4281 + version = "0.15.22";
4282
4283 src = fetchFromSourcehut {
4284 owner = "~sircmpwn";
4285 repo = "man.sr.ht";
4286 rev = version;
4287 - sha256 = "sha256-MqH/8K9XRvEg6P7GHE6XXtWnhDP3wT8iGoNaFtYQbio=";
4288 + sha256 = "sha256-curouf+eNCKprDI23blGs4AzJMry6zlCLDt/+0j5c8A=";
4289 };
4290
4291 nativeBuildInputs = srht.nativeBuildInputs;
4292 @@ -29,10 +29,12 @@ buildPythonPackage rec {
4293 export SRHT_PATH=${srht}/${python.sitePackages}/srht
4294 '';
4295
4296 + pythonImportsCheck = [ "mansrht" ];
4297 +
4298 meta = with lib; {
4299 homepage = "https://git.sr.ht/~sircmpwn/man.sr.ht";
4300 description = "Wiki service for the sr.ht network";
4301 - license = licenses.agpl3;
4302 + license = licenses.agpl3Only;
4303 maintainers = with maintainers; [ eadwu ];
4304 };
4305 }
4306 diff --git a/pkgs/applications/version-management/sourcehut/meta.nix b/pkgs/applications/version-management/sourcehut/meta.nix
4307 index 86d293973d7..96ea83a4ef3 100644
4308 --- a/pkgs/applications/version-management/sourcehut/meta.nix
4309 +++ b/pkgs/applications/version-management/sourcehut/meta.nix
4310 @@ -18,19 +18,19 @@
4311 , python
4312 }:
4313 let
4314 - version = "0.53.14";
4315 + version = "0.56.19";
4316
4317 src = fetchFromSourcehut {
4318 owner = "~sircmpwn";
4319 repo = "meta.sr.ht";
4320 rev = version;
4321 - sha256 = "sha256-/+r/XLDkcSTW647xPMh5bcJmR2xZNNH74AJ5jemna2k=";
4322 + sha256 = "sha256-YVj+aehO+2cJGvti9KXexm3y/0dx3rZBjEf0tKFC+s4=";
4323 };
4324
4325 buildApi = src: buildGoModule {
4326 inherit src version;
4327 pname = "metasrht-api";
4328 - vendorSha256 = "sha256-eZyDrr2VcNMxI++18qUy7LA1Q1YDlWCoRtl00L8lfR4=";
4329 + vendorSha256 = "sha256-j++Z+QXwCC7H3OK0sfWZrFluOVdN+b2tGCpLnmjKjc4=";
4330 };
4331
4332 in
4333 @@ -66,10 +66,12 @@ buildPythonPackage rec {
4334 cp ${buildApi "${src}/api/"}/bin/api $out/bin/metasrht-api
4335 '';
4336
4337 + pythonImportsCheck = [ "metasrht" ];
4338 +
4339 meta = with lib; {
4340 homepage = "https://git.sr.ht/~sircmpwn/meta.sr.ht";
4341 description = "Account management service for the sr.ht network";
4342 - license = licenses.agpl3;
4343 + license = licenses.agpl3Only;
4344 maintainers = with maintainers; [ eadwu ];
4345 };
4346 }
4347 diff --git a/pkgs/applications/version-management/sourcehut/pages.nix b/pkgs/applications/version-management/sourcehut/pages.nix
4348 new file mode 100644
4349 index 00000000000..c49e49c2664
4350 --- /dev/null
4351 +++ b/pkgs/applications/version-management/sourcehut/pages.nix
4352 @@ -0,0 +1,30 @@
4353 +{ lib
4354 +, fetchFromSourcehut
4355 +, buildGoModule
4356 +}:
4357 +
4358 +buildGoModule rec {
4359 + pname = "pagessrht";
4360 + version = "0.5.1";
4361 +
4362 + src = fetchFromSourcehut {
4363 + owner = "~sircmpwn";
4364 + repo = "pages.sr.ht";
4365 + rev = version;
4366 + sha256 = "sha256-Cab8zx+9WHHAB1rBoyZACq7lx9JdRBGzI1H+Yu9qHfs=";
4367 + };
4368 +
4369 + vendorSha256 = "sha256-udr+1y5ApQCSPhs3yQTTi9QfzRbz0A9COYuFMjQGa74=";
4370 +
4371 + postInstall = ''
4372 + mkdir -p $out/share/sql/
4373 + cp -r -t $out/share/sql/ schema.sql migrations
4374 + '';
4375 +
4376 + meta = with lib; {
4377 + homepage = "https://git.sr.ht/~sircmpwn/pages.sr.ht";
4378 + description = "Web hosting service for the sr.ht network";
4379 + license = licenses.agpl3Only;
4380 + maintainers = with maintainers; [ eadwu ];
4381 + };
4382 +}
4383 diff --git a/pkgs/applications/version-management/sourcehut/paste.nix b/pkgs/applications/version-management/sourcehut/paste.nix
4384 index 0d8c9135493..c411f8e8c95 100644
4385 --- a/pkgs/applications/version-management/sourcehut/paste.nix
4386 +++ b/pkgs/applications/version-management/sourcehut/paste.nix
4387 @@ -8,13 +8,13 @@
4388
4389 buildPythonPackage rec {
4390 pname = "pastesrht";
4391 - version = "0.12.1";
4392 + version = "0.13.6";
4393
4394 src = fetchFromSourcehut {
4395 owner = "~sircmpwn";
4396 repo = "paste.sr.ht";
4397 rev = version;
4398 - sha256 = "sha256-QQhd2LeH9BLmlHilhsv+9fZ+RPNmEMSmOpFA3dsMBFc=";
4399 + sha256 = "sha256-Khcqk86iD9nxiKXN3+8mSLNoDau2qXNFOrLdkVu+rH8=";
4400 };
4401
4402 nativeBuildInputs = srht.nativeBuildInputs;
4403 @@ -29,10 +29,12 @@ buildPythonPackage rec {
4404 export SRHT_PATH=${srht}/${python.sitePackages}/srht
4405 '';
4406
4407 + pythonImportsCheck = [ "pastesrht" ];
4408 +
4409 meta = with lib; {
4410 homepage = "https://git.sr.ht/~sircmpwn/paste.sr.ht";
4411 description = "Ad-hoc text file hosting service for the sr.ht network";
4412 - license = licenses.agpl3;
4413 + license = licenses.agpl3Only;
4414 maintainers = with maintainers; [ eadwu ];
4415 };
4416 }
4417 diff --git a/pkgs/applications/version-management/sourcehut/scm.nix b/pkgs/applications/version-management/sourcehut/scm.nix
4418 index 1f385265360..53f899a3d7b 100644
4419 --- a/pkgs/applications/version-management/sourcehut/scm.nix
4420 +++ b/pkgs/applications/version-management/sourcehut/scm.nix
4421 @@ -5,18 +5,17 @@
4422 , redis
4423 , pyyaml
4424 , buildsrht
4425 -, writeText
4426 }:
4427
4428 buildPythonPackage rec {
4429 pname = "scmsrht";
4430 - version = "0.22.9";
4431 + version = "0.22.15";
4432
4433 src = fetchFromSourcehut {
4434 owner = "~sircmpwn";
4435 repo = "scm.sr.ht";
4436 rev = version;
4437 - sha256 = "sha256-327G6C8FW+iZx+167D7TQsFtV6FGc8MpMVo9L/cUUqU=";
4438 + sha256 = "sha256-h4akgDn78ctBF31TX8D8NwUVUVazClPVvCR9xWyQPBk=";
4439 };
4440
4441 nativeBuildInputs = srht.nativeBuildInputs;
4442 @@ -33,11 +32,12 @@ buildPythonPackage rec {
4443 '';
4444
4445 dontUseSetuptoolsCheck = true;
4446 + pythonImportsCheck = [ "scmsrht" ];
4447
4448 meta = with lib; {
4449 homepage = "https://git.sr.ht/~sircmpwn/git.sr.ht";
4450 description = "Shared support code for sr.ht source control services.";
4451 - license = licenses.agpl3;
4452 + license = licenses.agpl3Only;
4453 maintainers = with maintainers; [ eadwu ];
4454 };
4455 }
4456 diff --git a/pkgs/applications/version-management/sourcehut/todo.nix b/pkgs/applications/version-management/sourcehut/todo.nix
4457 index 85e1f5637b6..cdb1f8cf8be 100644
4458 --- a/pkgs/applications/version-management/sourcehut/todo.nix
4459 +++ b/pkgs/applications/version-management/sourcehut/todo.nix
4460 @@ -12,13 +12,13 @@
4461
4462 buildPythonPackage rec {
4463 pname = "todosrht";
4464 - version = "0.64.14";
4465 + version = "0.65.2";
4466
4467 src = fetchFromSourcehut {
4468 owner = "~sircmpwn";
4469 repo = "todo.sr.ht";
4470 rev = version;
4471 - sha256 = "sha256-huIAhn6h1F5w5ST4/yBwr82kAzyYwhLu+gpRuOQgnsE=";
4472 + sha256 = "sha256-46RkBwLuGpYYw2V4JBDR414W2EaYnz0rru/T9j/PrJs=";
4473 };
4474
4475 nativeBuildInputs = srht.nativeBuildInputs;
4476 @@ -42,11 +42,12 @@ buildPythonPackage rec {
4477 ];
4478
4479 dontUseSetuptoolsCheck = true;
4480 + pythonImportsCheck = [ "todosrht" ];
4481
4482 meta = with lib; {
4483 homepage = "https://todo.sr.ht/~sircmpwn/todo.sr.ht";
4484 description = "Ticket tracking service for the sr.ht network";
4485 - license = licenses.agpl3;
4486 + license = licenses.agpl3Only;
4487 maintainers = with maintainers; [ eadwu ];
4488 };
4489 }
4490 diff --git a/pkgs/applications/version-management/sourcehut/update.sh b/pkgs/applications/version-management/sourcehut/update.sh
4491 index 156d4cc35e4..104c33a5f4f 100755
4492 --- a/pkgs/applications/version-management/sourcehut/update.sh
4493 +++ b/pkgs/applications/version-management/sourcehut/update.sh
4494 @@ -1,8 +1,11 @@
4495 #! /usr/bin/env nix-shell
4496 #! nix-shell -i bash -p git mercurial common-updater-scripts
4497 +set -eux -o pipefail
4498
4499 -cd "$(dirname "${BASH_SOURCE[0]}")"
4500 +cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1
4501 root=../../../..
4502 +tmp=$(mktemp -d)
4503 +trap 'rm -rf "$tmp"' EXIT
4504
4505 default() {
4506 (cd "$root" && nix-instantiate --eval --strict -A "sourcehut.python.pkgs.$1.meta.position" | sed -re 's/^"(.*):[0-9]+"$/\1/')
4507 @@ -13,42 +16,55 @@ version() {
4508 }
4509
4510 src_url() {
4511 - (cd "$root" && nix-instantiate --eval --strict -A "sourcehut.python.pkgs.$1.src.drvAttrs.url" | tr -d '"')
4512 + nix-instantiate --eval --strict --expr " with import $root {}; let src = sourcehut.python.pkgs.$1.drvAttrs.src; in src.url or src.meta.homepage" | tr -d '"'
4513 }
4514
4515 get_latest_version() {
4516 src="$(src_url "$1")"
4517 - tmp=$(mktemp -d)
4518 -
4519 + rm -rf "$tmp"
4520 if [ "$1" = "hgsrht" ]; then
4521 - hg clone "$src" "$tmp" &> /dev/null
4522 + hg clone "$src" "$tmp" >/dev/null
4523 printf "%s" "$(cd "$tmp" && hg log --limit 1 --template '{latesttag}')"
4524 else
4525 - git clone "$src" "$tmp"
4526 - printf "%s" "$(cd "$tmp" && git describe $(git rev-list --tags --max-count=1))"
4527 + git clone "$src" "$tmp" >/dev/null
4528 + printf "%s" "$(cd "$tmp" && git describe "$(git rev-list --tags --max-count=1)")"
4529 fi
4530 }
4531
4532 update_version() {
4533 default_nix="$(default "$1")"
4534 - version_old="$(version "$1")"
4535 + oldVersion="$(version "$1")"
4536 version="$(get_latest_version "$1")"
4537
4538 (cd "$root" && update-source-version "sourcehut.python.pkgs.$1" "$version")
4539
4540 - git add "$default_nix"
4541 - git commit -m "$1: $version_old -> $version"
4542 + # Update vendorSha256 of Go modules
4543 + retry=true
4544 + while "$retry"; do
4545 + retry=false;
4546 + exec < <(exec nix -L build -f "$root" sourcehut.python.pkgs."$1" 2>&1)
4547 + while IFS=' :' read -r origin hash; do
4548 + case "$origin" in
4549 + (expected|specified) oldHash="$hash";;
4550 + (got) sed -i "s|$oldHash|$hash|" "$default_nix"; retry=true; break;;
4551 + (*) printf >&2 "%s\n" "$origin${hash:+:$hash}"
4552 + esac
4553 + done
4554 + done
4555 +
4556 + if [ "$oldVersion" != "$version" ]; then
4557 + git add "$default_nix"
4558 + git commit -m "sourcehut.$1: $oldVersion -> $version"
4559 + fi
4560 }
4561
4562 -services=( "srht" "buildsrht" "dispatchsrht" "gitsrht" "hgsrht" "hubsrht" "listssrht" "mansrht"
4563 - "metasrht" "pastesrht" "todosrht" "scmsrht" )
4564 -
4565 -# Whether or not a specific service is requested
4566 -if [ -n "$1" ]; then
4567 - version="$(get_latest_version "$1")"
4568 - (cd "$root" && update-source-version "sourcehut.python.pkgs.$1" "$version")
4569 +if [ $# -gt 0 ]; then
4570 + services=("$@")
4571 else
4572 - for service in "${services[@]}"; do
4573 - update_version "$service"
4574 - done
4575 + services=( "srht" "scmsrht" "buildsrht" "dispatchsrht" "gitsrht" "hgsrht" "hubsrht" "listssrht" "mansrht"
4576 + "metasrht" "pagessrht" "pastesrht" "todosrht" )
4577 fi
4578 +
4579 +for service in "${services[@]}"; do
4580 + update_version "$service"
4581 +done
4582 diff --git a/pkgs/development/go-modules/generic/default.nix b/pkgs/development/go-modules/generic/default.nix
4583 index 3b645f9ce8b..f00ca1984ec 100644
4584 --- a/pkgs/development/go-modules/generic/default.nix
4585 +++ b/pkgs/development/go-modules/generic/default.nix
4586 @@ -71,6 +71,7 @@ let
4587 inherit (go) GOOS GOARCH;
4588
4589 patches = args.patches or [];
4590 + patchFlags = args.patchFlags or [];
4591 preBuild = args.preBuild or "";
4592 sourceRoot = args.sourceRoot or "";
4593