summaryrefslogtreecommitdiff
path: root/gnu/services/forgejo.scm
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/services/forgejo.scm')
-rw-r--r--gnu/services/forgejo.scm913
1 files changed, 0 insertions, 913 deletions
diff --git a/gnu/services/forgejo.scm b/gnu/services/forgejo.scm
deleted file mode 100644
index 86db26775d2..00000000000
--- a/gnu/services/forgejo.scm
+++ /dev/null
@@ -1,913 +0,0 @@
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2026 Maxim Cournoyer <maxim@guixotic.coop>
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (gnu services forgejo)
20 #:use-module (guix gexp)
21 #:use-module (guix least-authority)
22 #:use-module (guix records)
23 #:use-module (gnu build linux-container)
24 #:use-module (gnu packages forgejo)
25 #:use-module (gnu services)
26 #:use-module (gnu services shepherd)
27 #:use-module (gnu services configuration)
28 #:use-module (gnu system file-systems)
29 #:use-module (gnu system shadow)
30 #:use-module (ice-9 match)
31 #:use-module (srfi srfi-1)
32 #:export (forgejo-service-type
33 forgejo-configuration
34 forgejo-configuration-forgejo
35 forgejo-configuration-application-name
36 forgejo-configuration-application-slogan
37 forgejo-configuration-run-mode
38 forgejo-configuration-configuration-wizard?
39 forgejo-configuration-domain
40 forgejo-configuration-port
41 forgejo-configuration-protocol
42 forgejo-configuration-root-url
43 forgejo-configuration-http-address
44 forgejo-configuration-acme?
45 forgejo-configuration-tls-cert-file
46 forgejo-configuration-tls-key-file
47 forgejo-configuration-ssh-address
48 forgejo-configuration-ssh-port
49 forgejo-configuration-actions?
50 forgejo-configuration-default-actions-url
51 forgejo-configuration-disable-registration?
52 forgejo-configuration-confirm-mail-on-registration?
53 forgejo-configuration-internal-token-file
54 forgejo-configuration-secret-key-file
55 forgejo-configuration-lfs?
56 forgejo-configuration-lfs-secret-file
57 forgejo-configuration-log-level
58 forgejo-configuration-oauth2-secret-file
59 forgejo-configuration-mail-notification?
60 forgejo-configuration-mail-notification-on-new-user-signin?
61 forgejo-configuration-mailer?
62 forgejo-configuration-mail-subject-prefix
63 forgejo-configuration-mail-protocol
64 forgejo-configuration-smtp-address
65 forgejo-configuration-smtp-port
66 forgejo-configuration-smtp-user
67 forgejo-configuration-smtp-password-file
68 forgejo-configuration-mail-plain-text?
69 forgejo-configuration-mail-from
70 forgejo-configuration-sendmail-command
71 forgejo-configuration-sendmail-options
72 forgejo-configuration-sendmail-timeout
73 forgejo-configuration-sendmail-convert-crlf?
74 forgejo-configuration-openid-signin?
75 forgejo-configuration-metrics?
76 forgejo-configuration-metrics-token
77 forgejo-configuration-database-type
78 forgejo-configuration-database-host
79 forgejo-configuration-database-name
80 forgejo-configuration-database-user
81 forgejo-configuration-database-password-file
82 forgejo-configuration-database-tls?
83 forgejo-configuration-user-push-to-create?
84 forgejo-configuration-organization-push-to-create?
85 forgejo-configuration-default-push-to-create-private?
86 forgejo-configuration-require-signin-to-view?
87 forgejo-configuration-default-merge-style
88 forgejo-configuration-default-trust-model
89 forgejo-configuration-default-keep-email-private?
90 forgejo-configuration-offline-mode?
91 forgejo-configuration-user
92 forgejo-configuration-group
93 forgejo-configuration-custom-configuration-file
94 forgejo-configuration-shepherd-provision
95 forgejo-configuration-work-directory))
96
97;;; Copied from (gnu services base)
98(define (ipv6-address? str)
99 "Return true if STR denotes an IPv6 address."
100 (false-if-exception (->bool (inet-pton AF_INET6 str))))
101
102(define (database-type? x)
103 (member x '("sqlite3" "mysql" "postgres")))
104
105(define (port? x)
106 (and (number? x)
107 (and (>= x 1) (<= x 65535))))
108
109(define (privileged-port? x)
110 (and (number? x)
111 (and (>= x 1) (<= x 1023))))
112
113(define (unprivileged-port? x)
114 (and (number? x)
115 (and (>= x 1024) (<= x 65535))))
116
117(define (port-or-false? x)
118 (or (not x)
119 (port? x)))
120
121(define (log-level? x)
122 (member x '("trace" "debug" "info" "warn" "error" "none")))
123
124(define (merge-style? x)
125 (member x '("merge" "rebase" "rebase-merge" "squash" "fast-forward-only")))
126
127(define (protocol? x)
128 (member x '("http" "https" "fcgi" "http+unix" "fcgi+unix")))
129
130(define (run-mode? x)
131 (member x '("dev" "prod")))
132
133(define (trust-model? x)
134 (member x '("collaborator" "committer" "collaboratorcommitter")))
135
136(define (ssl-mode? x)
137 (member x '(#t #f 'skip-verify)))
138
139(define (serialize-database-tls type value)
140 (match type
141 ("sqlite3" "disable") ;does not apply
142 ("mysql" (match value
143 (#t "true")
144 (#f "false")
145 ('skip-verify "skip-verify")))
146 ("postgres" (match value
147 (#t "verify-full")
148 (#f "disable")
149 ('skip-verify "require")))))
150
151(define-maybe file-like)
152
153(define-maybe/no-serialization string)
154
155(define (mail-protocol? x)
156 (member x '("" "dummy" "sendmail"
157 "smtp" "smtps" "smtp+starttls" "smtp+unix")))
158
159(define-configuration/no-serialization forgejo-configuration
160 (forgejo
161 (file-like forgejo)
162 "The forgejo package.")
163
164 (work-directory
165 (string "/var/lib/forgejo")
166 "The directory where Forgejo keeps its state, including its database when
167using @code{\"sqlite3\"} as the @code{database-type}.")
168
169 (shepherd-provision
170 (list-of-symbols '(forgejo))
171 "The name(s) of the service.")
172
173 (configuration-wizard?
174 (boolean #f)
175 "Whether to use the initial configuration page (wizard) to setup Forgejo.
176Setting this to @code{#t} means that none of the declarative configuration
177values will be honored. The Forgejo-generated configuration file will be kept
178under @file{custom/conf/app.ini}, relative to the value of the
179@code{work-directory}.")
180
181 (user
182 (string "forgejo")
183 "The name of the user under which Forgejo will be executed.")
184
185 (group
186 (string "forgejo")
187 "The name of the group under which Forgejo will be executed.")
188
189 (custom-configuration-file
190 maybe-file-like
191 "File-like object to provide a complete Forgejo configuration file as an
192escape hatch, overriding every other fields of this configuration.")
193
194 ;; Global settings (no section).
195 (application-name
196 (string "Forgejo")
197 "The application name that shows in every page title.")
198
199 (application-slogan
200 (string "")
201 "Slogan to show near the application name in every page title.")
202
203 (run-mode
204 (run-mode "prod")
205 "The run mode to use; either @code{\"dev\"} or @code{\"prod\"}.")
206
207 ;; [server] settings.
208 (domain
209 (string "localhost")
210 "The domain name or host address of the server.")
211
212 (offline-mode?
213 (boolean #f)
214 "Set to @code{#t} to run Forgejo in offline mode, meaning it won't reach
215out to any external services such as Gravatar, CDNs, etc. and serve everything
216locally instead.")
217
218 (port
219 (unprivileged-port 3000)
220 "The port Forgejo will listen on. The port should be an unprivileged port;
221to serve Forgejo on a privileged port like @code{80}, configure a reverse
222proxy. It is ignored when using a Unix-domain socket.")
223
224 (protocol
225 (protocol "http")
226 "Listen protocol. Either @code{http}, @code{https}, @code{fcgi},
227@code{\"http+unix\"} or @code{\"fcgi+unix\"}. @samp{+unix} means @emph{via} a
228Unix-domain socket, in which case @code{http-address} must be the file name of
229the socket to use.")
230
231 (root-url
232 maybe-string
233 "Overwrite the automatically generated public URL,
234which implicitly defaults to @samp{@var{protocol}://@var{domain}:@var{port}},
235which is necessary using a reverse proxy.")
236
237 (http-address
238 (string "0.0.0.0")
239 "The address to listen to. It can be an absolute file name when using a
240Unix-domain socket, for example @file{/run/forgejo.sock}, or a file name
241relative to the @code{work-directory}, for example @file{forgejo.sock}.")
242
243 (ssh-address
244 (string "0.0.0.0")
245 "The IP address to listen on for the SSH server.")
246
247 (lfs?
248 (boolean #t)
249 "Whether to enable Git LFS support.")
250
251 (lfs-secret-file
252 (string "/etc/forgejo/lfs_jwt_secret")
253 "The file name of the LFS JWT (JSON Web Token) secret, necessary for
254proper Git LFS operation, which can be generated with @samp{forgejo generate
255secret JWT_SECRET}. Automatically generated the first time Forgejo runs if it
256does not yet exist.")
257
258 (mail-notification?
259 (boolean #f)
260 "Whether to enable email notifications.")
261
262 (acme?
263 (boolean #f)
264 "Whether to enable @acronym{ACME, Automatic Certificate Management
265Environment}, to automatically provision TLS certificates, which can be useful
266if you use the @code{https} or @code{https+unix} protocols.")
267
268 (tls-cert-file
269 (string "https/cert.pem")
270 "File name of a TLS certificate file. Ignored if @code{acme?} is enabled.
271A relative file name is located under @file{@var{work-directory}/custom}.")
272
273 (tls-key-file
274 (string "https/key.pem")
275 "File name of a TLS key. Ignored if @code{acme?} is enabled. A relative
276file name is located under @file{@var{work-directory}/custom}.")
277
278 (ssh-port
279 (port-or-false 22)
280 "The SSH port Forgejo will use; if you already run an OpenSSH as the SSH
281server, specify the port used by the SSH daemon. Otherwise, you need to use
282an unprivileged port (greater or equal to 1024) which Forgejo will use with
283its built-in SSH server. Set to @code{#f} to disable SSH support.")
284
285 ;; [actions] settings.
286 (actions?
287 (boolean #t)
288 "Whether to enable actions capabilities.")
289
290 (default-actions-url
291 (string "https://code.forgejo.org")
292 "The default address to fetch action plugins from.")
293
294 ;; [security] settings.
295 (internal-token-file
296 (string "/etc/forgejo/internal_token")
297 "The file name of the internal token used to validate communication within
298Forgejo, which can be generated with @samp{forgejo generate
299secret INTERNAL_TOKEN}")
300
301 (secret-key-file
302 (string "/etc/forgejo/secret_key")
303 "The file name containing the global secret key used for encrypting data
304like 2FA secrets; it is thus very important to back it up somewhere safe to
305avoid losing access to encrypted data.")
306
307 ;; [oauth2] settings.
308 (oauth2-jwt-secret-file
309 (string "/etc/forgejo/oauth2_jwt_secret")
310 "The file name of the OAuth2 JWT secret, which Forgejo expects to exist.")
311
312 ;; [admin] settings.
313 (mail-notification-on-new-user-signin?
314 (boolean #f)
315 "Whether to notify administrators by email when a new user signs in for the
316first time.")
317
318 ;; [mailer] settings.
319 (mailer?
320 (boolean #f)
321 "Whether to enable the mail server integration, used for sending email
322notifications for example.")
323
324 (mail-from
325 (string "")
326 "The FROM email address used for sent emails, per the RFC 5322
327specification. This can be for example just an email address, or something
328like @samp{\"Name\" <email@@example.com>}. This field @emph{must} be specified
329if @code{mailer?} is set to @code{#t}.")
330
331 (mail-subject-prefix
332 (string "")
333 "The prefix displayed before the subject in emails.")
334
335 (mail-protocol
336 (mail-protocol "")
337 "The mail server protocol. One of @code{\"smtp\"}, @code{\"smtps\"},
338@code{\"smtp+starttls\"}, @code{\"smtp+unix\"}, @code{\"sendmail\"},
339@code{\"dummy\"} or @code{\"\"}. The empty string defaults means the protocol
340is inferred from the value of the @code{smtp-port} value, meaning its
341effective implicit value is @code{\"smtps\"}. @code{\"dummy\"} causes emails
342to be sent to the log, useful for testing. When @code{\"sendmail\"} is used,
343it is expected to be fully and correctly externally configured, with the other
344@samp{smtp-} prefixed options of this configuration ignored.")
345
346 (smtp-address
347 (string "")
348 "The mail server address, for example @code{\"smtp.example.com\"} or an
349absolute file name, if using @code{\"smtp+unix\"} for the
350@code{mail-protocol}.")
351
352 (smtp-port
353 (port 465) ;implies smtps protocol
354 "The mail server port. This field affects the implicit value of the
355@code{mail-protocol}.")
356
357 (smtp-user
358 (string "")
359 "The SMTP server user name, if required ")
360
361 (smtp-password-file
362 (string "")
363 "An absolute file name containing the password of the SMTP server.")
364
365 (mail-plain-text?
366 (boolean #f)
367 "Send emails only in plain text, without HTML alternative.")
368
369 (sendmail-command
370 (string "sendmail")
371 "The file name of the @command{sendmail} command to use, which can be
372either an absolute file name or a command name to be looked from
373@env{\"PATH\"}.")
374
375 (sendmail-options
376 (list-of-strings '())
377 "Optional options to pass to the @command{sendmail} command. If your
378@code{sendmail} program, like that of Postfix, interprets options, you should
379provide @code{\"--\"} as the first option.")
380
381 (sendmail-timeout
382 (string "5m")
383 "The timeout for Sendmail, provided as a
384@url{https://pkg.go.dev/time#ParseDuration, Go @code{time.Duration} string},
385for example @code{\"300ms\"}, @code{\"1.5h\"} or @code{\"2h45m\"}.")
386
387 (sendmail-convert-crlf?
388 (boolean #t)
389 "Whether to convert @samp{\\r\\n} to @samp{\\n} for Sendmail.")
390
391 ;; [log] settings.
392 (log-level
393 (log-level "info")
394 "The log level to use. Either @code{\"trace\"}, @code{\"debug\"},
395@code{\"info\"}, @code{\"warn\"}, @code{\"error\"} or @code{\"none\"}.")
396
397 ;; [openid] settings.
398 (openid-signin?
399 (boolean #t)
400 "Whether to allow signing in using OpenID.")
401
402 ;; [service] settings.
403 (disable-registration?
404 (boolean #f)
405 "Disallow registration, only allowing administrators to create accounts.")
406
407 (confirm-mail-on-registration?
408 (boolean #f)
409 "Whether to require email confirmation when new users register.")
410
411 (require-signin-to-view?
412 (boolean #f)
413 "Whether users must sign in before they can view the explore pages.")
414
415 (default-keep-email-private?
416 (boolean #f)
417 "Whether by default a user email is displayed on their profile.")
418
419 (user-push-to-create?
420 (boolean #f)
421 "Allow users to push local repositories to Forgejo and have them
422automatically created for a user.")
423
424 (organization-push-to-create?
425 (boolean #f)
426 "Allow users to push local repositories to Forgejo and have them
427automatically created for an organization.")
428
429 (default-push-to-create-private?
430 (boolean #t)
431 "Whether to make newly created repositories private by default when they
432were created via push.")
433
434 ;; [repository.pull-request] settings.
435 (default-merge-style
436 (merge-style "merge")
437 "The default merge style to use. Either @code{\"merge\"},
438@code{\"rebase\"}, @code{\"rebase-merge\"}, @code{\"squash\"} or
439@code{\"fast-forward-only\"}.")
440
441 ;; [repository.signing] settings.
442 (default-trust-model
443 (trust-model "committer")
444 "The default trust model for repositories. Either
445@code{\"collaborator\"}, @code{\"committer\"} or
446@code{\"collaboratorcommitter\"}")
447
448 ;; [metrics] settings.
449 (metrics?
450 (boolean #f)
451 "Whether to enable the metrics endpoint.")
452
453 (metrics-token
454 (string "")
455 "A secret to use, if authorization to the metrics endpoint is desired.")
456
457 ;; [database] settings.
458 (database-type
459 (database-type "sqlite3")
460 "The database type to use. Either @code{\"sqlite3\"}, @code{\"mysql\"} or
461@code{\"postgres\"}. The sqlite3 type is the simplest to use, but a
462full-fledged database like PostgreSQL may scale better for multiple users. If
463you change this, make sure to adjust the other @samp{database-*} values
464accordingly. Also note that currently, the PostgreSQL and MySQL databases
465must be manually created: refer to the official Forgejo documentation for
466@url{https://forgejo.org/docs/latest/admin/installation/database-preparation,
467database preparation}.")
468
469 (database-host
470 (string "/var/run/postgresql")
471 "This is the @samp{@var{host}:@var{port}} tuple for remote database
472MySQL/PostgreSQL hosts, or a local Unix-domain socket file. If using MySQL,
473this should be adjusted to @file{/run/mysqld/mysqld.sock}")
474
475 (database-name
476 ;; Keep the default database name in sync with the database user name, so
477 ;; that it can be easily provisioned via a the
478 ;; postgresql-role-service-type, which assumes both match.
479 (string "forgejo")
480 "The MySQL/PostgreSQL database name to use.")
481
482 (database-user
483 (string "forgejo")
484 "The MySQL/PostgreSQL database user to use.")
485
486 (database-password-file
487 maybe-string
488 "A file name containing the password to access the MySQL/PostgreSQL
489database, for example @file{\"/etc/forgejo/db_passwd\"}.")
490
491 (database-tls?
492 (ssl-mode #f)
493 "Whether the connection to a MySQL/PostgreSQL database should use TLS. Set
494to @code{#t} to enable TLS with full verification or to @code{'skip-verify} to
495enable TLS without verification."))
496
497;;; TODO: Allow enabling/configuring email integration
498;;; See custom/conf/app.example.ini for a documentation of the fields.
499(define forgejo-configuration->file
500 (match-record-lambda <forgejo-configuration>
501 ( application-name application-slogan work-directory
502 run-mode domain protocol http-address port root-url
503 acme? tls-cert-file tls-key-file
504 ssh-address ssh-port
505 default-keep-email-private?
506 default-merge-style default-trust-model
507 actions? default-actions-url
508 disable-registration? confirm-mail-on-registration?
509 require-signin-to-view? mail-notification?
510 internal-token-file secret-key-file
511 lfs? lfs-secret-file oauth2-jwt-secret-file
512 mail-notification-on-new-user-signin?
513 mailer? mail-subject-prefix mail-protocol
514 smtp-address smtp-port smtp-user smtp-password-file
515 mail-plain-text? mail-from
516 sendmail-command sendmail-options sendmail-timeout
517 sendmail-convert-crlf?
518 log-level offline-mode? openid-signin?
519 metrics? metrics-token
520 user-push-to-create? organization-push-to-create?
521 default-push-to-create-private?
522 user group
523 custom-configuration-file
524 database-type database-host database-name database-user
525 database-password-file database-tls?)
526 (if (maybe-value-set? custom-configuration-file)
527 custom-configuration-file
528 (mixed-text-file "forgejo.ini" "
529APP_NAME = " application-name "
530APP_SLOGAN = " application-slogan "
531RUN_USER = " user "
532WORK_PATH = " work-directory "
533RUN_MODE = " run-mode "
534
535[database]
536DB_TYPE = " database-type "
537HOST = " database-host "
538NAME = " database-name "
539USER = " database-user "\n"
540(if (maybe-value-set? database-password-file)
541 (string-append "PASSWD_URI = file://" database-password-file "\n")
542 "")
543"SSL_MODE = " (serialize-database-tls database-type database-tls?) "
544
545[repository]
546ROOT = " work-directory "/data/forgejo-repositories
547DEFAULT_PUSH_CREATE_PRIVATE = "
548(if default-push-to-create-private? "true" "false") "
549ENABLE_PUSH_CREATE_USER = " (if user-push-to-create? "true" "false") "
550ENABLE_PUSH_CREATE_ORG = " (if organization-push-to-create? "true" "false") "
551
552[server]
553SSH_DOMAIN = " domain "
554SSH_LISTEN_HOST = " ssh-address "
555PROTOCOL = " protocol "
556DOMAIN = " domain "
557HTTP_ADDR = " http-address "
558HTTP_PORT = " (number->string port) "\n"
559(if (maybe-value-set? root-url)
560 (string-append "ROOT_URL = " root-url "\n")
561 "")
562"ENABLE_ACME = " (if acme? "true" "false") "
563CERT_FILE = " tls-cert-file "
564KEY_FILE = " tls-key-file "
565APP_DATA_PATH = " work-directory "/data
566DISABLE_SSH = " (if (not ssh-port) "true" "false") "
567SSH_PORT = " (if (not ssh-port) "" (number->string ssh-port)) "
568LFS_START_SERVER = " (if lfs? "true" "false") "
569LFS_JWT_SECRET_URI = file://" lfs-secret-file "
570OFFLINE_MODE = " (if offline-mode? "true" "false") "
571
572[lfs]
573PATH = " work-directory "/data/lfs
574
575[service]
576REGISTER_EMAIL_CONFIRM = false
577ENABLE_NOTIFY_MAIL = " (if mail-notification? "true" "false") "
578DISABLE_REGISTRATION = " (if disable-registration? "true" "false") "
579REGISTER_EMAIL_CONFIRM = " (if confirm-mail-on-registration? "true" "false") "
580ENABLE_CAPTCHA = false
581REQUIRE_SIGNIN_VIEW = " (if require-signin-to-view? "true" "false") "
582DEFAULT_KEEP_EMAIL_PRIVATE = " (if default-keep-email-private? "true" "false") "
583
584[openid]
585ENABLE_OPENID_SIGNIN = " (if openid-signin? "true" "false") "
586
587[cron.update_checker]
588ENABLED = false
589
590[log]
591LEVEL = " log-level "
592ROOT_PATH = " work-directory "/log
593
594[repository.pull-request]
595DEFAULT_MERGE_STYLE = " default-merge-style "
596
597[repository.signing]
598DEFAULT_TRUST_MODEL = " default-trust-model "
599
600[security]
601INSTALL_LOCK = true
602SECRET_KEY = file://" secret-key-file "
603INTERNAL_TOKEN_URI = file://" internal-token-file "
604
605[oauth2]
606JWT_SECRET_URI = file://" oauth2-jwt-secret-file "
607
608[admin]
609SEND_NOTIFICATION_EMAIL_ON_NEW_USER = "
610(if mail-notification-on-new-user-signin? "true" "false") "
611
612[mailer]
613ENABLED = " (if mailer? "true" "false") "
614SUBJECT_PREFIX = " mail-subject-prefix "
615PROTOCOL = " mail-protocol "
616SMTP_ADDR = " smtp-address "
617SMTP_PORT = " (number->string smtp-port) "
618USER = " smtp-user "
619PASSWD_URI = " (if (string-null? smtp-password-file)
620 ""
621 (string-append "file://" smtp-password-file)) "
622SEND_AS_PLAIN_TEXT = " (if mail-plain-text? "true" "false") "
623FROM = " mail-from "
624SENDMAIL_PATH = " sendmail-command "
625SENDMAIL_ARGS = " (string-join sendmail-options " ") "
626SENDMAIL_TIMEOUT = " sendmail-timeout "
627SENDMAIL_CONVERT_CRLF = " (if sendmail-convert-crlf? "true" "false") "
628
629[metrics]
630ENABLED = " (if metrics? "true" "false") "
631TOKEN = " metrics-token "
632
633[actions]
634ENABLED = " (if actions? "true" "false") "
635DEFAULT_ACTIONS_URL = " default-actions-url "
636"))))
637
638(define (forgejo-shepherd-service config)
639 (match-record config <forgejo-configuration>
640 ( forgejo work-directory user group port
641 http-address protocol ssh-port
642 database-type database-host
643 configuration-wizard?
644 internal-token-file lfs-secret-file
645 smtp-password-file
646 tls-cert-file tls-key-file
647 oauth2-jwt-secret-file secret-key-file
648 shepherd-provision)
649 (let* ((mappings (append
650 %network-file-mappings
651 (list %store-mapping ;XXX: coarse-grained
652 (file-system-mapping
653 (source "/etc/ssl/certs")
654 (target source))
655 (file-system-mapping
656 (source work-directory)
657 (target source)
658 (writable? #t))
659 (file-system-mapping
660 (source internal-token-file)
661 (target source))
662 (file-system-mapping
663 (source lfs-secret-file)
664 (target source))
665 (file-system-mapping
666 (source oauth2-jwt-secret-file)
667 (target source))
668 (file-system-mapping
669 (source secret-key-file)
670 (target source)))
671 (if (absolute-file-name? smtp-password-file)
672 (list (file-system-mapping
673 (source smtp-password-file)
674 (target source)))
675 '())
676 (if (absolute-file-name? tls-cert-file)
677 (list (file-system-mapping
678 (source tls-cert-file)
679 (target source)))
680 '())
681 (if (absolute-file-name? tls-key-file)
682 (list (file-system-mapping
683 (source tls-key-file)
684 (target source)))
685 '())
686 (if (and (not (string=? "sqlite3" database-type))
687 (absolute-file-name? database-host)) ;socket
688 (list (file-system-mapping
689 (source database-host)
690 (target source)
691 (writable? #t)))
692 '())))
693 (forgejo (file-append forgejo "/bin/forgejo"))
694 (forgejo-wrapper (least-authority-wrapper
695 forgejo
696 #:name "forgejo-pola-wrapper"
697 #:user user #:group group
698 #:namespaces
699 ;; The wrapper needs access to the forgejo user
700 ;; to be able to mount
701 ;; /etc/forgejo/internal_token for example.
702 (fold delq %namespaces '(net user))
703 #:mappings mappings
704 #:preserved-environment-variables
705 (cons* "FORGEJO_WORK_DIR"
706 "SSL_CERT_DIR"
707 %default-preserved-environment-variables)))
708 (config (forgejo-configuration->file config))
709 (init-provision (list (symbol-append (first shepherd-provision)
710 '-init))))
711 (list (shepherd-service
712 (documentation "Run Forgejo pre-start actions.")
713 (requirement '(networking user-processes))
714 (provision init-provision)
715 (one-shot? #t)
716 (modules (cons '(srfi srfi-19) %default-modules))
717 (start
718 #~(lambda _
719 (chdir #$work-directory)
720
721 (define app.ini "custom/conf/app.ini")
722
723 (define (symlink? x)
724 (eq? (stat:type (stat x)) 'symlink))
725
726 (define (delete-file/backup x)
727 (let* ((timestamp (date->string
728 (current-date) "~4"))
729 (bak (string-append
730 x "-" timestamp ".bak")))
731 (copy-file x bak)
732 (delete-file x)))
733
734 (define (fork+exec-forgejo . options)
735 ;; We can't change the environment in a one-shot
736 ;; service/Shepherd (is this a bug?), so use this wrapper
737 ;; of fork+exec-command that passes the needed
738 ;; environment variables and raises on unclean exit.
739 (let ((pid
740 (apply fork+exec-command
741 (cons #$forgejo-wrapper options)
742 (list #:environment-variables
743 (list #$(string-append
744 "FORGEJO_WORK_DIR="
745 work-directory)
746 #$(string-append
747 "HOME=" work-directory))))))
748 (unless (zero? (cdr (waitpid pid)))
749 (error "failed to run forgejo command"
750 #$forgejo-wrapper options))))
751
752 (unless #$configuration-wizard?
753 ;; When users transition from configuration-wizard? #t to
754 ;; #f, the stale wizard configuration is backed up.
755 (when (file-exists? app.ini)
756 (if (symlink? app.ini)
757 (delete-file app.ini)
758 (delete-file/backup app.ini)))
759 (symlink #$config app.ini))
760
761 (when (file-exists? app.ini)
762 ;; Initialize Forgejo's database.
763 (fork+exec-forgejo "migrate")
764 ;; Refresh binary file names captured in git hooks.
765 (fork+exec-forgejo "admin" "regenerate" "hooks")
766 ;; Update command option in 'authorized_keys' file.
767 (fork+exec-forgejo "admin" "regenerate" "keys")
768 ;; Sanity check configuration/environment.
769 (fork+exec-forgejo "doctor" "check" "--all")))))
770 (shepherd-service
771 (documentation "Run the Forgejo Git forge")
772 (requirement (append init-provision
773 '(networking
774 user-processes)
775 (if (privileged-port? ssh-port)
776 '(ssh-daemon)
777 '())
778 (if (string=? "postgres" database-type)
779 '(postgres)
780 '())
781 (if (string=? "mysql" database-type)
782 '(mysql)
783 '())))
784 (provision shepherd-provision)
785 (actions
786 (list (shepherd-configuration-action
787 (if configuration-wizard?
788 (string-append work-directory "/custom/conf/app.ini")
789 config))))
790 (start
791 #~(make-systemd-constructor
792 (list #$forgejo-wrapper "web"
793 #$@(if configuration-wizard?
794 '()
795 #~("--config" #$config)))
796 (cond
797 (#$(string-suffix? "+unix" protocol)
798 (list (endpoint (make-socket-address AF_UNIX
799 #$http-address)
800 #:socket-owner #$user
801 #:socket-group #$group)))
802 (#$(string=? "0.0.0.0" http-address)
803 (list (endpoint (make-socket-address AF_INET
804 INADDR_ANY #$port))
805 (endpoint (make-socket-address AF_INET6
806 INADDR_ANY #$port))))
807 (#$(ipv6-address? http-address)
808 (list (endpoint (make-socket-address
809 AF_INET6
810 (inet-pton AF_INET6 #$http-address)
811 #$port))))
812 (else ;assumed ipv4
813 (list (endpoint (make-socket-address
814 AF_INET
815 (inet-pton AF_INET #$http-address)
816 #$port)))))
817 #:log-file "/var/log/forgejo.log"
818 #:environment-variables
819 (list (string-append "HOME=" #$work-directory)
820 "SSL_CERT_DIR=/etc/ssl/certs")))
821 (stop #~(make-systemd-destructor)))))))
822
823(define %forgejo-accounts
824 (match-record-lambda <forgejo-configuration>
825 (user group work-directory)
826 (list (user-group (name user)
827 (system? #t))
828 (user-account
829 (name user)
830 (group group)
831 (system? #t)
832 (comment "Forgejo User")
833 (home-directory work-directory)))))
834
835(define %forgejo-activation
836 (match-record-lambda <forgejo-configuration>
837 ( forgejo user configuration-wizard? work-directory
838 protocol http-address
839 secret-key-file internal-token-file lfs-secret-file
840 oauth2-jwt-secret-file)
841 #~(begin
842 (use-modules (gnu build activation)
843 (guix build utils)
844 (ice-9 match))
845
846 (define secret-files `(("SECRET_KEY" . ,#$secret-key-file)
847 ("INTERNAL_TOKEN" . ,#$internal-token-file)
848 ("LFS_JWT_SECRET" . ,#$lfs-secret-file)
849 ("JWT_SECRET" . ,#$oauth2-jwt-secret-file)))
850
851 (define forgejo #$(file-append forgejo "/bin/forgejo"))
852
853 (define (generate-secret type file)
854 (with-exception-handler
855 (lambda (e)
856 (false-if-exception (delete-file file))
857 (raise-exception e))
858 (lambda ()
859 (call-with-output-file file
860 (lambda (port)
861 (let ((pid (spawn forgejo
862 (list forgejo "generate" "secret" type)
863 #:output port)))
864 (match (waitpid pid)
865 ((_ . status)
866 (unless (zero? status)
867 (error "failed to generate forgejo secret" type))))))))))
868
869 (let* ((owner (getpw #$user))
870 (uid (passwd:uid owner))
871 (gid (passwd:gid owner)))
872 ;; Top-level directories of secrets; needed even when not using the
873 ;; wizard.
874 (for-each (match-lambda
875 ((_ . file)
876 (mkdir-p/perms (dirname file) owner #o750)))
877 secret-files)
878 (when (and (string-suffix? "-unix" #$protocol)
879 (absolute-file-name? http-address))
880 (mkdir-p/perms (dirname http-address) owner #o750))
881 ;; To please 'forgejo doctor check --all' out of the box.
882 (mkdir-p/perms #$work-directory owner #o750)
883 (with-directory-excursion #$work-directory
884 (mkdir-p/perms ".ssh" owner #o700)
885 (mkdir-p/perms "custom" owner #o750)
886 (mkdir-p/perms "custom/conf" owner #o750)
887 (mkdir-p/perms "data" owner #o750)
888 (mkdir-p/perms "data/forgejo-repositories" owner #o750)
889 (mkdir-p/perms "log" owner #o750))
890 (unless #$configuration-wizard?
891 ;; Bootstrap secret files.
892 (for-each (match-lambda
893 ((type . file)
894 (unless (file-exists? file)
895 (generate-secret type file))
896 (chown file uid gid)
897 (chmod file #o400)))
898 secret-files))))))
899
900(define forgejo-service-type
901 (service-type
902 (name 'forgejo)
903 (extensions
904 (list (service-extension shepherd-root-service-type
905 forgejo-shepherd-service)
906 (service-extension account-service-type
907 %forgejo-accounts)
908 (service-extension activation-service-type
909 %forgejo-activation)
910 (service-extension profile-service-type
911 (compose list forgejo-configuration-forgejo))))
912 (default-value (forgejo-configuration))
913 (description "Run Forgejo.")))