X-Git-Url: https://git.hcoop.net/jackhill/guix/guix.git/blobdiff_plain/93b83eb31e35aedaafcc40cfbb9a8743e0f6352d..08b4a10fa6bc535cd99d65f0233dd027153878eb:/gnu/services/web.scm?ds=sidebyside diff --git a/gnu/services/web.scm b/gnu/services/web.scm index aae2f3db0d..56971238ab 100644 --- a/gnu/services/web.scm +++ b/gnu/services/web.scm @@ -1,12 +1,14 @@ ;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2015 David Thompson -;;; Copyright © 2015, 2016, 2017 Ludovic Courtès -;;; Copyright © 2016 Nils Gillmann +;;; Copyright © 2015, 2016, 2017, 2018 Ludovic Courtès +;;; Copyright © 2016 ng0 ;;; Copyright © 2016, 2017, 2018 Julien Lepiller ;;; Copyright © 2017 Christopher Baines ;;; Copyright © 2017 nee -;;; Copyright © 2017 Clément Lassieur +;;; Copyright © 2017, 2018 Clément Lassieur ;;; Copyright © 2018 Pierre-Antoine Rouby +;;; Copyright © 2017, 2018, 2019 Christopher Baines +;;; Copyright © 2018 Marius Bakke ;;; ;;; This file is part of GNU Guix. ;;; @@ -26,15 +28,26 @@ (define-module (gnu services web) #:use-module (gnu services) #:use-module (gnu services shepherd) + #:use-module (gnu services admin) + #:use-module (gnu services getmail) + #:use-module (gnu services mail) #:use-module (gnu system pam) #:use-module (gnu system shadow) #:use-module (gnu packages admin) + #:use-module (gnu packages databases) #:use-module (gnu packages web) + #:use-module (gnu packages patchutils) #:use-module (gnu packages php) + #:use-module (gnu packages python) + #:use-module (gnu packages gnupg) #:use-module (gnu packages guile) + #:use-module (gnu packages logging) + #:use-module (guix packages) #:use-module (guix records) #:use-module (guix modules) + #:use-module (guix utils) #:use-module (guix gexp) + #:use-module ((guix store) #:select (text-file)) #:use-module ((guix utils) #:select (version-major)) #:use-module ((guix packages) #:select (package-version)) #:use-module (srfi srfi-1) @@ -65,6 +78,11 @@ httpd-config-file-user httpd-config-file-group + + httpd-module + httpd-module? + %default-httpd-modules + httpd-service-type @@ -77,6 +95,7 @@ nginx-configuration-upstream-blocks nginx-configuration-server-names-hash-bucket-size nginx-configuration-server-names-hash-bucket-max-size + nginx-configuration-extra-content nginx-configuration-file @@ -131,6 +150,7 @@ php-fpm-configuration-log-file php-fpm-configuration-process-manager php-fpm-configuration-display-errors + php-fpm-configuration-timezone php-fpm-configuration-workers-log-file php-fpm-configuration-file @@ -163,7 +183,78 @@ hpcguix-web-configuration hpcguix-web-configuration? - hpcguix-web-service-type)) + hpcguix-web-service-type + + + tailon-configuration-file + tailon-configuration-file? + tailon-configuration-file-files + tailon-configuration-file-bind + tailon-configuration-file-relative-root + tailon-configuration-file-allow-transfers? + tailon-configuration-file-follow-names? + tailon-configuration-file-tail-lines + tailon-configuration-file-allowed-commands + tailon-configuration-file-debug? + tailon-configuration-file-http-auth + tailon-configuration-file-users + + + tailon-configuration + tailon-configuration? + tailon-configuration-config-file + tailon-configuration-package + + tailon-service-type + + + varnish-configuration + varnish-configuration? + varnish-configuration-package + varnish-configuration-name + varnish-configuration-backend + varnish-configuration-vcl + varnish-configuration-listen + varnish-configuration-storage + varnish-configuration-parameters + varnish-configuration-extra-options + + varnish-service-type + + + patchwork-database-configuration + patchwork-database-configuration? + patchwork-database-configuration-engine + patchwork-database-configuration-name + patchwork-database-configuration-user + patchwork-database-configuration-password + patchwork-database-configuration-host + patchwork-database-configuration-port + + + patchwork-settings-module + patchwork-settings-module? + patchwork-settings-module-database-configuration + patchwork-settings-module-secret-key + patchwork-settings-module-allowed-hosts + patchwork-settings-module-default-from-email + patchwork-settings-module-static-url + patchwork-settings-module-admins + patchwork-settings-module-debug? + patchwork-settings-module-enable-rest-api? + patchwork-settings-module-enable-xmlrpc? + patchwork-settings-module-force-https-links? + patchwork-settings-module-extra-settings + + + patchwork-configuration + patchwork-configuration? + patchwork-configuration-patchwork + patchwork-configuration-settings-module + patchwork-configuration-domain + + patchwork-virtualhost + patchwork-service-type)) ;;; Commentary: ;;; @@ -431,6 +522,8 @@ (default #f)) (server-names-hash-bucket-max-size nginx-configuration-server-names-hash-bucket-max-size (default #f)) + (extra-content nginx-configuration-extra-content + (default "")) (file nginx-configuration-file ;#f | string | file-like (default #f))) @@ -521,7 +614,8 @@ of index files." (nginx log-directory run-directory server-blocks upstream-blocks server-names-hash-bucket-size - server-names-hash-bucket-max-size) + server-names-hash-bucket-max-size + extra-content) (apply mixed-text-file "nginx.conf" (flatten "user nginx nginx;\n" @@ -550,7 +644,8 @@ of index files." "\n" (map emit-nginx-upstream-config upstream-blocks) (map emit-nginx-server-config server-blocks) - "}\n" + extra-content + "\n}\n" "events {}\n")))) (define %nginx-accounts @@ -587,27 +682,39 @@ of index files." (system* (string-append #$nginx "/sbin/nginx") "-c" #$(or file (default-nginx-config config)) - "-t")))) + "-p" #$run-directory + "-t")))) (define (nginx-shepherd-service config) (match-record config (nginx file run-directory) (let* ((nginx-binary (file-append nginx "/sbin/nginx")) + (pid-file (in-vicinity run-directory "pid")) (nginx-action (lambda args #~(lambda _ - (zero? - (system* #$nginx-binary "-c" - #$(or file - (default-nginx-config config)) - #$@args)))))) + (invoke #$nginx-binary "-c" + #$(or file + (default-nginx-config config)) + #$@args) + (match '#$args + (("-s" . _) #f) + (_ + ;; When FILE is true, we cannot be sure that PID-FILE will + ;; be created, so assume it won't show up. When FILE is + ;; false, read PID-FILE. + #$(if file + #~#t + #~(read-pid-file #$pid-file)))))))) ;; TODO: Add 'reload' action. (list (shepherd-service (provision '(nginx)) (documentation "Run the nginx daemon.") (requirement '(user-processes loopback)) + (modules `((ice-9 match) + ,@%default-modules)) (start (nginx-action "-p" run-directory)) (stop (nginx-action "-s" "stop"))))))) @@ -711,6 +818,8 @@ of index files." (default (php-fpm-dynamic-process-manager-configuration))) (display-errors php-fpm-configuration-display-errors (default #f)) + (timezone php-fpm-configuration-timezone + (default #f)) (workers-log-file php-fpm-configuration-workers-log-file (default (string-append "/var/log/php" (version-major (package-version php)) @@ -765,7 +874,7 @@ of index files." (shell (file-append shadow "/sbin/nologin"))))))) (define (default-php-fpm-config socket user group socket-user socket-group - pid-file log-file pm display-errors workers-log-file) + pid-file log-file pm display-errors timezone workers-log-file) (apply mixed-text-file "php-fpm.conf" (flatten "[global]\n" @@ -778,6 +887,10 @@ of index files." "listen.owner =" socket-user "\n" "listen.group =" socket-group "\n" + (if timezone + (string-append "php_admin_value[date.timezone] = \"" timezone "\"\n") + "") + (match pm (($ pm.max-children @@ -817,7 +930,8 @@ of index files." (define php-fpm-shepherd-service (match-lambda (($ php socket user group socket-user socket-group - pid-file log-file pm display-errors workers-log-file file) + pid-file log-file pm display-errors + timezone workers-log-file file) (list (shepherd-service (provision '(php-fpm)) (documentation "Run the php-fpm daemon.") @@ -828,27 +942,27 @@ of index files." #$(or file (default-php-fpm-config socket user group socket-user socket-group pid-file log-file - pm display-errors workers-log-file))) + pm display-errors timezone workers-log-file))) #:pid-file #$pid-file)) (stop #~(make-kill-destructor))))))) -(define php-fpm-activation - (match-lambda - (($ _ _ user _ _ _ _ log-file _ _ workers-log-file _) - #~(begin - (use-modules (guix build utils)) - (let* ((user (getpwnam #$user)) - (touch (lambda (file-name) - (call-with-output-file file-name (const #t)))) - (init-log-file - (lambda (file-name) - (when #$workers-log-file - (when (not (file-exists? file-name)) - (touch file-name)) - (chown file-name (passwd:uid user) (passwd:gid user)) - (chmod file-name #o660))))) - (init-log-file #$log-file) - (init-log-file #$workers-log-file)))))) +(define (php-fpm-activation config) + #~(begin + (use-modules (guix build utils)) + (let* ((user (getpwnam #$(php-fpm-configuration-user config))) + (touch (lambda (file-name) + (call-with-output-file file-name (const #t)))) + (workers-log-file + #$(php-fpm-configuration-workers-log-file config)) + (init-log-file + (lambda (file-name) + (when workers-log-file + (when (not (file-exists? file-name)) + (touch file-name)) + (chown file-name (passwd:uid user) (passwd:gid user)) + (chmod file-name #o660))))) + (init-log-file #$(php-fpm-configuration-log-file config)) + (init-log-file workers-log-file)))) (define php-fpm-service-type @@ -933,6 +1047,14 @@ a webserver.") (chown home-dir (passwd:uid user) (passwd:gid user)) (chmod home-dir #o755)))) +(define %hpcguix-web-log-file + "/var/log/hpcguix-web.log") + +(define %hpcguix-web-log-rotations + (list (log-rotation + (files (list %hpcguix-web-log-file)) + (frequency 'weekly)))) + (define (hpcguix-web-shepherd-service config) (let ((specs (hpcguix-web-configuration-specs config)) (hpcguix-web (hpcguix-web-package config))) @@ -949,7 +1071,9 @@ a webserver.") #:user "hpcguix-web" #:group "hpcguix-web" #:environment-variables - (list "XDG_CACHE_HOME=/var/cache"))) + (list "XDG_CACHE_HOME=/var/cache" + "SSL_CERT_DIR=/etc/ssl/certs") + #:log-file #$%hpcguix-web-log-file)) (stop #~(make-kill-destructor)))))) (define hpcguix-web-service-type @@ -961,5 +1085,551 @@ a webserver.") (const %hpcguix-web-accounts)) (service-extension activation-service-type (const %hpcguix-web-activation)) + (service-extension rottlog-service-type + (const %hpcguix-web-log-rotations)) (service-extension shepherd-root-service-type (compose list hpcguix-web-shepherd-service)))))) + + +;;; +;;; Tailon +;;; + +(define-record-type* + tailon-configuration-file make-tailon-configuration-file + tailon-configuration-file? + (files tailon-configuration-file-files + (default '("/var/log"))) + (bind tailon-configuration-file-bind + (default "localhost:8080")) + (relative-root tailon-configuration-file-relative-root + (default #f)) + (allow-transfers? tailon-configuration-file-allow-transfers? + (default #t)) + (follow-names? tailon-configuration-file-follow-names? + (default #t)) + (tail-lines tailon-configuration-file-tail-lines + (default 200)) + (allowed-commands tailon-configuration-file-allowed-commands + (default '("tail" "grep" "awk"))) + (debug? tailon-configuration-file-debug? + (default #f)) + (wrap-lines tailon-configuration-file-wrap-lines + (default #t)) + (http-auth tailon-configuration-file-http-auth + (default #f)) + (users tailon-configuration-file-users + (default #f))) + +(define (tailon-configuration-files-string files) + (string-append + "\n" + (string-join + (map + (lambda (x) + (string-append + " - " + (cond + ((string? x) + (simple-format #f "'~A'" x)) + ((list? x) + (string-join + (cons (simple-format #f "'~A':" (car x)) + (map + (lambda (x) (simple-format #f " - '~A'" x)) + (cdr x))) + "\n")) + (else (error x))))) + files) + "\n"))) + +(define-gexp-compiler (tailon-configuration-file-compiler + (file ) system target) + (match file + (($ files bind relative-root + allow-transfers? follow-names? + tail-lines allowed-commands debug? + wrap-lines http-auth users) + (text-file + "tailon-config.yaml" + (string-concatenate + (filter-map + (match-lambda + ((key . #f) #f) + ((key . value) (string-append key ": " value "\n"))) + + `(("files" . ,(tailon-configuration-files-string files)) + ("bind" . ,bind) + ("relative-root" . ,relative-root) + ("allow-transfers" . ,(if allow-transfers? "true" "false")) + ("follow-names" . ,(if follow-names? "true" "false")) + ("tail-lines" . ,(number->string tail-lines)) + ("commands" . ,(string-append "[" + (string-join allowed-commands ", ") + "]")) + ("debug" . ,(if debug? "true" #f)) + ("wrap-lines" . ,(if wrap-lines "true" "false")) + ("http-auth" . ,http-auth) + ("users" . ,(if users + (string-concatenate + (cons "\n" + (map (match-lambda + ((user . pass) + (string-append + " " user ":" pass))) + users))) + #f))))))))) + +(define-record-type* + tailon-configuration make-tailon-configuration + tailon-configuration? + (config-file tailon-configuration-config-file + (default (tailon-configuration-file))) + (package tailon-configuration-package + (default tailon))) + +(define tailon-shepherd-service + (match-lambda + (($ config-file package) + (list (shepherd-service + (provision '(tailon)) + (documentation "Run the tailon daemon.") + (start #~(make-forkexec-constructor + `(,(string-append #$package "/bin/tailon") + "-c" ,#$config-file) + #:user "tailon" + #:group "tailon")) + (stop #~(make-kill-destructor))))))) + +(define %tailon-accounts + (list (user-group (name "tailon") (system? #t)) + (user-account + (name "tailon") + (group "tailon") + (system? #t) + (comment "tailon") + (home-directory "/var/empty") + (shell (file-append shadow "/sbin/nologin"))))) + +(define tailon-service-type + (service-type + (name 'tailon) + (description + "Run Tailon, a Web application for monitoring, viewing, and searching log +files.") + (extensions + (list (service-extension shepherd-root-service-type + tailon-shepherd-service) + (service-extension account-service-type + (const %tailon-accounts)))) + (compose concatenate) + (extend (lambda (parameter files) + (tailon-configuration + (inherit parameter) + (config-file + (let ((old-config-file + (tailon-configuration-config-file parameter))) + (tailon-configuration-file + (inherit old-config-file) + (files (append (tailon-configuration-file-files old-config-file) + files)))))))) + (default-value (tailon-configuration)))) + + +;;; +;;; Varnish +;;; + +(define-record-type* + varnish-configuration make-varnish-configuration + varnish-configuration? + (package varnish-configuration-package ; + (default varnish)) + (name varnish-configuration-name ;string + (default "default")) + (backend varnish-configuration-backend ;string + (default "localhost:8080")) + (vcl varnish-configuration-vcl ;#f | + (default #f)) + (listen varnish-configuration-listen ;list of strings + (default '("localhost:80"))) + (storage varnish-configuration-storage ;list of strings + (default '("malloc,128m"))) + (parameters varnish-configuration-parameters ;list of string pairs + (default '())) + (extra-options varnish-configuration-extra-options ;list of strings + (default '()))) + +(define %varnish-accounts + (list (user-group + (name "varnish") + (system? #t)) + (user-account + (name "varnish") + (group "varnish") + (system? #t) + (comment "Varnish Cache User") + (home-directory "/var/varnish") + (shell (file-append shadow "/sbin/nologin"))))) + +(define varnish-shepherd-service + (match-lambda + (($ package name backend vcl listen storage + parameters extra-options) + (list (shepherd-service + (provision (list (symbol-append 'varnish- (string->symbol name)))) + (documentation (string-append "The Varnish Web Accelerator" + " (" name ")")) + (requirement '(networking)) + (start #~(make-forkexec-constructor + (list #$(file-append package "/sbin/varnishd") + "-n" #$name + #$@(if vcl + #~("-f" #$vcl) + #~("-b" #$backend)) + #$@(append-map (lambda (a) (list "-a" a)) listen) + #$@(append-map (lambda (s) (list "-s" s)) storage) + #$@(append-map (lambda (p) + (list "-p" (format #f "~a=~a" + (car p) (cdr p)))) + parameters) + #$@extra-options) + ;; Varnish will drop privileges to the "varnish" user when + ;; it exists. Not passing #:user here allows the service + ;; to bind to ports < 1024. + #:pid-file (if (string-prefix? "/" #$name) + (string-append #$name "/_.pid") + (string-append "/var/varnish/" #$name "/_.pid")))) + (stop #~(make-kill-destructor))))))) + +(define varnish-service-type + (service-type + (name 'varnish) + (description "Run the Varnish cache server.") + (extensions + (list (service-extension account-service-type + (const %varnish-accounts)) + (service-extension shepherd-root-service-type + varnish-shepherd-service))) + (default-value + (varnish-configuration)))) + + +;;; +;;; Patchwork +;;; + +(define-record-type* + patchwork-database-configuration make-patchwork-database-configuration + patchwork-database-configuration? + (engine patchwork-database-configuration-engine + (default "django.db.backends.postgresql_psycopg2")) + (name patchwork-database-configuration-name + (default "patchwork")) + (user patchwork-database-configuration-user + (default "httpd")) + (password patchwork-database-configuration-password + (default "")) + (host patchwork-database-configuration-host + (default "")) + (port patchwork-database-configuration-port + (default ""))) + +(define-record-type* + patchwork-settings-module make-patchwork-settings-module + patchwork-settings-module? + (database-configuration patchwork-settings-module-database-configuration + (default (patchwork-database-configuration))) + (secret-key-file patchwork-settings-module-secret-key-file + (default "/etc/patchwork/django-secret-key")) + (allowed-hosts patchwork-settings-module-allowed-hosts) + (default-from-email patchwork-settings-module-default-from-email) + (static-url patchwork-settings-module-static-url + (default "/static/")) + (admins patchwork-settings-module-admins + (default '())) + (debug? patchwork-settings-module-debug? + (default #f)) + (enable-rest-api? patchwork-settings-module-enable-rest-api? + (default #t)) + (enable-xmlrpc? patchwork-settings-module-enable-xmlrpc? + (default #t)) + (force-https-links? patchwork-settings-module-force-https-links? + (default #t)) + (extra-settings patchwork-settings-module-extra-settings + (default ""))) + +(define-record-type* + patchwork-configuration make-patchwork-configuration + patchwork-configuration? + (patchwork patchwork-configuration-patchwork + (default patchwork)) + (domain patchwork-configuration-domain) + (settings-module patchwork-configuration-settings-module) + (static-path patchwork-configuration-static-url + (default "/static/")) + (getmail-retriever-config getmail-retriever-config)) + +;; Django uses a Python module for configuration, so this compiler generates a +;; Python module from the configuration record. +(define-gexp-compiler (patchwork-settings-module-compiler + (file ) system target) + (match file + (($ database-configuration secret-key-file + allowed-hosts default-from-email + static-url admins debug? enable-rest-api? + enable-xmlrpc? force-https-links? + extra-configuration) + (gexp->derivation + "patchwork-settings" + (with-imported-modules '((guix build utils)) + #~(let ((output #$output)) + (define (create-__init__.py filename) + (call-with-output-file filename + (lambda (port) (display "" port)))) + + (use-modules (guix build utils) + (srfi srfi-1)) + + (mkdir-p (string-append output "/guix/patchwork")) + (create-__init__.py + (string-append output "/guix/__init__.py")) + (create-__init__.py + (string-append output "/guix/patchwork/__init__.py")) + + (call-with-output-file + (string-append output "/guix/patchwork/settings.py") + (lambda (port) + (display + (string-append "from patchwork.settings.base import * + +# Configuration from Guix +with open('" #$secret-key-file "') as f: + SECRET_KEY = f.read().strip() + +ALLOWED_HOSTS = [ +" #$(string-concatenate + (map (lambda (allowed-host) + (string-append " '" allowed-host "'\n")) + allowed-hosts)) +"] + +ADMINS = [ +" #$(string-concatenate + (map (match-lambda + ((name email-address) + (string-append + "('" name "','" email-address "'),"))) + admins)) +"] + +DEBUG = " #$(if debug? "True" "False") " + +ENABLE_REST_API = " #$(if enable-xmlrpc? "True" "False") " +ENABLE_XMLRPC = " #$(if enable-xmlrpc? "True" "False") " + +FORCE_HTTPS_LINKS = " #$(if force-https-links? "True" "False") " + +DATABASES = { + 'default': { +" #$(match database-configuration + (($ + engine name user password host port) + (string-append + " 'ENGINE': '" engine "',\n" + " 'NAME': '" name "',\n" + " 'USER': '" user "',\n" + " 'PASSWORD': '" password "',\n" + " 'HOST': '" host "',\n" + " 'PORT': '" port "',\n"))) " + }, +} + +" #$(if debug? + #~(string-append "STATIC_ROOT = '" + #$(file-append patchwork "/share/patchwork/htdocs") + "'") + #~(string-append "STATIC_URL = '" #$static-url "'")) " + +STATICFILES_STORAGE = ( + 'django.contrib.staticfiles.storage.StaticFilesStorage' +) + +# Guix Extra Configuration +" #$extra-configuration " +") port))) + #t)) + #:local-build? #t)))) + +(define patchwork-virtualhost + (match-lambda + (($ patchwork domain + settings-module static-path + getmail-retriever-config) + (define wsgi.py + (file-append patchwork + (string-append + "/lib/python" + (version-major+minor + (package-version python)) + "/site-packages/patchwork/wsgi.py"))) + + (httpd-virtualhost + "*:8080" + `("ServerAdmin admin@example.com` +ServerName " ,domain " + +LogFormat \"%v %h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\"\" customformat +LogLevel info +CustomLog \"/var/log/httpd/" ,domain "-access_log\" customformat + +ErrorLog /var/log/httpd/error.log + +WSGIScriptAlias / " ,wsgi.py " +WSGIDaemonProcess " ,(package-name patchwork) " user=httpd group=httpd processes=1 threads=2 display-name=%{GROUP} lang='en_US.UTF-8' locale='en_US.UTF-8' python-path=" ,settings-module " +WSGIProcessGroup " ,(package-name patchwork) " +WSGIPassAuthorization On + + + Require all granted + + +" ,@(if static-path + `("Alias " ,static-path " " ,patchwork "/share/patchwork/htdocs/") + '()) +" + + AllowOverride None + Options MultiViews Indexes SymlinksIfOwnerMatch IncludesNoExec + Require method GET POST OPTIONS +"))))) + +(define (patchwork-httpd-configuration patchwork-configuration) + (list "WSGISocketPrefix /var/run/mod_wsgi" + (list "LoadModule wsgi_module " + (file-append mod-wsgi "/modules/mod_wsgi.so")) + (patchwork-virtualhost patchwork-configuration))) + +(define (patchwork-django-admin-gexp patchwork settings-module) + #~(lambda command + (let ((pid (primitive-fork)) + (user (getpwnam "httpd"))) + (if (eq? pid 0) + (dynamic-wind + (const #t) + (lambda () + (setgid (passwd:gid user)) + (setuid (passwd:uid user)) + + (setenv "DJANGO_SETTINGS_MODULE" "guix.patchwork.settings") + (setenv "PYTHONPATH" #$settings-module) + (primitive-exit + (if (zero? + (apply system* + #$(file-append patchwork "/bin/patchwork-admin") + command)) + 0 + 1))) + (lambda () + (primitive-exit 1))) + (zero? (cdr (waitpid pid))))))) + +(define (patchwork-django-admin-action patchwork settings-module) + (shepherd-action + (name 'django-admin) + (documentation + "Run a django admin command for patchwork") + (procedure (patchwork-django-admin-gexp patchwork settings-module)))) + +(define patchwork-shepherd-services + (match-lambda + (($ patchwork domain + settings-module static-path + getmail-retriever-config) + (define secret-key-file-creation-gexp + (if (patchwork-settings-module? settings-module) + (with-extensions (list guile-gcrypt) + #~(let ((secret-key-file + #$(patchwork-settings-module-secret-key-file + settings-module))) + (use-modules (guix build utils) + (gcrypt random)) + + (unless (file-exists? secret-key-file) + (mkdir-p (dirname secret-key-file)) + (call-with-output-file secret-key-file + (lambda (port) + (display (random-token 30 'very-strong) port))) + (let* ((pw (getpwnam "httpd")) + (uid (passwd:uid pw)) + (gid (passwd:gid pw))) + (chown secret-key-file uid gid) + (chmod secret-key-file #o400))))) + #~())) + + (list (shepherd-service + (requirement '(postgres)) + (provision (list (string->symbol + (string-append (package-name patchwork) + "-setup")))) + (start + #~(lambda () + (define run-django-admin-command + #$(patchwork-django-admin-gexp patchwork + settings-module)) + + #$secret-key-file-creation-gexp + + (run-django-admin-command "migrate"))) + (stop #~(const #f)) + (actions + (list (patchwork-django-admin-action patchwork + settings-module))) + (respawn? #f) + (documentation "Setup Patchwork.")))))) + +(define patchwork-getmail-configs + (match-lambda + (($ patchwork domain + settings-module static-path + getmail-retriever-config) + (list + (getmail-configuration + (name (string->symbol (package-name patchwork))) + (user "httpd") + (directory (string-append + "/var/lib/getmail/" (package-name patchwork))) + (rcfile + (getmail-configuration-file + (retriever getmail-retriever-config) + (destination + (getmail-destination-configuration + (type "MDA_external") + (path (file-append patchwork "/bin/patchwork-admin")) + (extra-parameters + '((arguments . ("parsemail")))))) + (options + (getmail-options-configuration + (read-all #f) + (delivered-to #f) + (received #f))))) + (idle (assq-ref + (getmail-retriever-configuration-extra-parameters + getmail-retriever-config) + 'mailboxes)) + (environment-variables + (list "DJANGO_SETTINGS_MODULE=guix.patchwork.settings" + #~(string-append "PYTHONPATH=" #$settings-module)))))))) + +(define patchwork-service-type + (service-type + (name 'patchwork-setup) + (extensions + (list (service-extension httpd-service-type + patchwork-httpd-configuration) + (service-extension shepherd-root-service-type + patchwork-shepherd-services) + (service-extension getmail-service-type + patchwork-getmail-configs))) + (description + "Patchwork patch tracking system.")))