From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp0 ([2001:41d0:2:bcc0::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms0.migadu.com with LMTPS id 4LodGoxEC2FuFAEAgWs5BA (envelope-from ) for ; Thu, 05 Aug 2021 03:53:16 +0200 Received: from aspmx1.migadu.com ([2001:41d0:2:bcc0::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp0 with LMTPS id GmHdFYxEC2GlDgAA1q6Kng (envelope-from ) for ; Thu, 05 Aug 2021 01:53:16 +0000 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 860E018474 for ; Thu, 5 Aug 2021 03:53:15 +0200 (CEST) Received: from localhost ([::1]:53626 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mBSZO-0006U6-KD for larch@yhetil.org; Wed, 04 Aug 2021 21:53:14 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:39776) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mBSZC-0006Tw-Pc for guix-patches@gnu.org; Wed, 04 Aug 2021 21:53:02 -0400 Received: from debbugs.gnu.org ([209.51.188.43]:33927) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1mBSZC-0002kn-It for guix-patches@gnu.org; Wed, 04 Aug 2021 21:53:02 -0400 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1mBSZC-0000dm-CZ for guix-patches@gnu.org; Wed, 04 Aug 2021 21:53:02 -0400 X-Loop: help-debbugs@gnu.org Subject: [bug#49886] [PATCH] Add gitile and gitile service Resent-From: Julien Lepiller Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Thu, 05 Aug 2021 01:53:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 49886 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: 49886@debbugs.gnu.org X-Debbugs-Original-To: guix-patches@gnu.org Received: via spool by submit@debbugs.gnu.org id=B.16281283482415 (code B ref -1); Thu, 05 Aug 2021 01:53:02 +0000 Received: (at submit) by debbugs.gnu.org; 5 Aug 2021 01:52:28 +0000 Received: from localhost ([127.0.0.1]:45473 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1mBSYX-0000cl-6Z for submit@debbugs.gnu.org; Wed, 04 Aug 2021 21:52:28 -0400 Received: from lists.gnu.org ([209.51.188.17]:41130) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1mBSYV-0000ce-Dr for submit@debbugs.gnu.org; Wed, 04 Aug 2021 21:52:20 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:39746) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mBSYV-0006RT-4C for guix-patches@gnu.org; Wed, 04 Aug 2021 21:52:19 -0400 Received: from lepiller.eu ([89.234.186.109]:52256) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mBSYS-0002FV-03 for guix-patches@gnu.org; Wed, 04 Aug 2021 21:52:18 -0400 Received: from lepiller.eu (localhost [127.0.0.1]) by lepiller.eu (OpenSMTPD) with ESMTP id 7482929c for ; Thu, 5 Aug 2021 01:52:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed; d=lepiller.eu; h=date:from :to:subject:message-id:mime-version:content-type; s=dkim; bh=Yju Tq5ZyPt+NohoX3d8r701EJONu08/17PgD669YJbs=; b=Qm1Mx3dKnJicpWeIQUe bdTRO4leXv7VmJxjTuSMVm55+RzfAW7yed/H367bSJHZefC/wf6E3purWt8zqbFB FfwXC2+Op/ql+m6Uh4+xHR8VFuL1QtdUJEJk5gs0kAlp9LU75VvxAk+7Yy3eUr/y JG/1hTHWv6C6+wJBs1SNBblrAGNL0vr1W7qWR1xX2cdUw3DxZ5Z06CZBxYgoR+Rc BOrCpDX4kdWEQA818TkD83btoPIzf70QUF1FJ4P1TjfjA6UU+V8hnQjlHwFKUFpj 2gzoBGiRfvVaIzdMfXtIrOcDfFHeGF2GyMRujWsRPVUuonjmGs9DOZOt799H+Dh5 NvQ== Received: by lepiller.eu (OpenSMTPD) with ESMTPSA id 0c4fef2b (TLSv1.3:AEAD-AES256-GCM-SHA384:256:NO) for ; Thu, 5 Aug 2021 01:52:10 +0000 (UTC) Date: Thu, 5 Aug 2021 03:51:59 +0200 From: Julien Lepiller Message-ID: <20210805035159.3959e01e@tachikoma.lepiller.eu> X-Mailer: Claws Mail 3.18.0 (GTK+ 2.24.32; x86_64-pc-linux-gnu) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="MP_/9V5R=VpoWcy+VCTvn/d+M75" Received-SPF: pass client-ip=89.234.186.109; envelope-from=julien@lepiller.eu; helo=lepiller.eu X-Spam_score_int: 0 X-Spam_score: -0.1 X-Spam_bar: / X-Spam_report: (-0.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, PDS_OTHER_BAD_TLD=1.997, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+larch=yhetil.org@gnu.org Sender: "Guix-patches" X-Migadu-Flow: FLOW_IN ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1628128395; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:mime-version:mime-version: content-type:content-type:resent-cc:resent-from:resent-sender: resent-message-id:list-id:list-help:list-unsubscribe:list-subscribe: list-post:dkim-signature; bh=RfTYRsD7BUtwVNH6BrEdzYGVKlmzBhZSk58OfeMvgPI=; b=on3mvu+gWS+xfOqXQi+6z9kMAA5Jcbx1uTR8t7K5lEwxiBh7JFtKjL2NjuNDsElIUWFlvM jZ1mJUEX+PuZ+U/nJCfVPzpHsfvv9O0y6mqvXQbc7LdFmA/qlPMY+N6UqJqyH46loEiD+S iFeixSxgBMs78/CfMUDYEogObmmjKiqXSljx8A/sKVfp5LuWxKEGdAZ663oFYfQhW4yS2T PEZTLC21wjkbo3BGSTVA5oexpFy8QSSgR5LtQMdmrhErx1CXHuxlGBHDv+zcCWJUAXuKJw BU5cHJbHpK3T1fHmSIWC7xsFtiA0iroQeFj+/iSh55gTeJyCaQq9GotYVro8FQ== ARC-Seal: i=1; s=key1; d=yhetil.org; t=1628128395; a=rsa-sha256; cv=none; b=FiqXHQInwB2dD6ZW6g63J+mri7LCqclN87HeOlLZGiwMAYQnZHugqODp24r4J898++iW77 W/MI/eM4+xzm4f+XJ6m0cT/8Ik7ZonPa3gHs5MfpZsJW1kT9SFQKLiODo7QamOgmZr36m4 P0kiaffvRlX1iR7fUrM1mlqHSAopbuBJFeAqKzVB1R/x1sabyYHomyBM+f8+oKx7Ydls+c xGWUj/f8nTLdaOO8SrdNiy7H3tCH7bK4CqXrwqfW6aRT2IJ7qWBKGJKpH3n71P2TFZJk93 28RmivyPZIJdbBuF9L58K7FGxK8ThTyrAj11WhlKTUOCZq8W8DllXeND7MDp6A== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=fail ("headers rsa verify failed") header.d=lepiller.eu header.s=dkim header.b=Qm1Mx3dK; dmarc=fail reason="SPF not aligned (relaxed)" header.from=lepiller.eu (policy=none); spf=pass (aspmx1.migadu.com: domain of guix-patches-bounces@gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=guix-patches-bounces@gnu.org X-Migadu-Spam-Score: -1.31 Authentication-Results: aspmx1.migadu.com; dkim=fail ("headers rsa verify failed") header.d=lepiller.eu header.s=dkim header.b=Qm1Mx3dK; dmarc=fail reason="SPF not aligned (relaxed)" header.from=lepiller.eu (policy=none); spf=pass (aspmx1.migadu.com: domain of guix-patches-bounces@gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=guix-patches-bounces@gnu.org X-Migadu-Queue-Id: 860E018474 X-Spam-Score: -1.31 X-Migadu-Scanner: scn0.migadu.com X-TUID: r4YcGFMhpTNO --MP_/9V5R=VpoWcy+VCTvn/d+M75 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Content-Disposition: inline Hi guix! Attached is a small patch series that adds gitile and the associated service type. Gitile is a small git forge project I have, and some of you expressed their interest, so here it is :) --MP_/9V5R=VpoWcy+VCTvn/d+M75 Content-Type: text/x-patch Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename=0001-gnu-Add-gitile.patch =46rom 38773e575b313caedfc788d4e28fd219265b4254 Mon Sep 17 00:00:00 2001 From: Julien Lepiller Date: Thu, 5 Aug 2021 02:57:32 +0200 Subject: [PATCH 1/2] gnu: Add gitile. * gnu/packages/version-control.scm (gitile): New variable. --- gnu/packages/version-control.scm | 109 +++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/gnu/packages/version-control.scm b/gnu/packages/version-contro= l.scm index ac61b97f59..29dc86be8c 100644 --- a/gnu/packages/version-control.scm +++ b/gnu/packages/version-control.scm @@ -39,6 +39,7 @@ ;;; Copyright =C2=A9 2021 LibreMiami ;;; Copyright =C2=A9 2021 Xinglu Chen ;;; Copyright =C2=A9 2021 Fran=C3=A7ois J. +;;; Copyright =C2=A9 2021 Julien Lepiller ;;; ;;; This file is part of GNU Guix. ;;; @@ -93,6 +94,7 @@ #:use-module (gnu packages golang) #:use-module (gnu packages groff) #:use-module (gnu packages guile) + #:use-module (gnu packages guile-xyz) #:use-module (gnu packages image) #:use-module (gnu packages linux) #:use-module (gnu packages mail) @@ -114,6 +116,7 @@ #:use-module (gnu packages readline) #:use-module (gnu packages rsync) #:use-module (gnu packages sqlite) + #:use-module (gnu packages texinfo) #:use-module (gnu packages admin) #:use-module (gnu packages xml) #:use-module (gnu packages emacs) @@ -1517,6 +1520,112 @@ also walk each side of a merge and test those chang= es individually.") control to Git repositories.") (license license:gpl2))) =20 +;; gitile requires a more recent version than the latest release. +(define guile-syntax-highlight-for-gitile + (let ((commit "51727cbb7fc05ef743aab2d7b16314ea1ed790e4") + (revision "0")) + (package + (inherit guile-syntax-highlight) + (version (git-version "0.1" revision commit)) + (source (origin + (method git-fetch) + (uri (git-reference + (url "https://git.dthompson.us/guile-syntax-highlig= ht.git") + (commit commit))) + (file-name (git-file-name "guile-syntax-highlight" version= )) + (sha256 + (base32 + "1cvacy4y5qxajygb1qg8hvhjdf2xnnwnm5j918cabsi8wfwchig7"))= )) + (native-inputs + `(("autoconf" ,autoconf) + ("automake" ,automake) + ("texinfo" ,texinfo) + ,@(package-native-inputs guile-syntax-highlight)))))) + +(define-public gitile + (package + (name "gitile") + (version "0.1") + (source + (origin + (method git-fetch) + (uri (git-reference + (url "https://git.lepiller.eu/git/gitile") + (commit version))) + (file-name (git-file-name name version)) + (sha256 + (base32 "1ciwvqlc0nnzcxx3wjdsrm8i1h9965clv56nzcw7yyhml8ds42dk")))) + (build-system gnu-build-system) + (arguments + `(#:modules ((guix build utils) + (guix build gnu-build-system) + (ice-9 rdelim) + (ice-9 popen)) + #:make-flags (list "GUILE_AUTO_COMPILE=3D0") + #:phases + (modify-phases %standard-phases + (add-after 'install 'install-assets + (lambda* (#:key outputs #:allow-other-keys) + (let ((assets (string-append (assoc-ref outputs "out") + "/share/gitile/assets"))) + (mkdir-p assets) + (copy-recursively "assets" assets)))) + (add-after 'install 'install-bin + (lambda* (#:key outputs #:allow-other-keys) + (install-file "scripts/gitile" + (string-append (assoc-ref outputs "out") + "/bin")))) + (add-after 'install-bin 'wrap-program + (lambda* (#:key inputs outputs #:allow-other-keys) + ;; Wrap the 'cuirass' command to refer to the right modules. + (let* ((out (assoc-ref outputs "out")) + (commonmark (assoc-ref inputs "guile-commonmark")) + (git (assoc-ref inputs "guile-git")) + (bytes (assoc-ref inputs "guile-bytestructures")) + (fibers (assoc-ref inputs "guile-fibers")) + (gcrypt (assoc-ref inputs "guile-gcrypt")) + (syntax-highlight (assoc-ref inputs "guile-syntax-high= light")) + (deps (list out commonmark git bytes fibers gcrypt + syntax-highlight)) + (guile (assoc-ref %build-inputs "guile")) + (effective (read-line + (open-pipe* OPEN_READ + (string-append guile "/bin/gui= le") + "-c" "(display (effective-vers= ion))"))) + (mods (string-drop-right ;drop trailing colon + (string-join deps + (string-append "/share/guile/sit= e/" + effective ":") + 'suffix) + 1)) + (objs (string-drop-right + (string-join deps + (string-append "/lib/guile/" eff= ective + "/site-ccache:") + 'suffix) + 1))) + (wrap-program (string-append out "/bin/gitile") + `("GUILE_LOAD_PATH" ":" prefix (,mods)) + `("GUILE_LOAD_COMPILED_PATH" ":" prefix (,objs))))))))) + (native-inputs + `(("autoconf" ,autoconf) + ("automake" ,automake) + ("guile" ,guile-3.0) + ("pkg-config" ,pkg-config))) + (inputs + `(("guile" ,guile-3.0) + ("guile-commonmark" ,guile-commonmark) + ("guile-fibers" ,guile-fibers) + ("guile-gcrypt" ,guile-gcrypt) + ("guile-git" ,guile-git) + ("guile-syntax-highlight" ,guile-syntax-highlight) + ("gnutls" ,gnutls))) + (home-page "https://git.lepiller.eu/gitile") + (synopsis "Simple git forge written in Guile") + (description "Gitile is a git forge written in Guile that lets you +visualize your public Git repositories on a web interface.") + (license license:agpl3+))) + (define-public pre-commit (package (name "pre-commit") --=20 2.32.0 --MP_/9V5R=VpoWcy+VCTvn/d+M75 Content-Type: text/x-patch Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename=0002-gnu-version-control-Add-gitile-service.patch =46rom 3cd7ef0ae922d77ff5d29c831dbdf8d350210fb7 Mon Sep 17 00:00:00 2001 From: Julien Lepiller Date: Thu, 5 Aug 2021 03:46:40 +0200 Subject: [PATCH 2/2] gnu: version-control: Add gitile service. * gnu/services/version-control.scm (gitile-service-type): New variable. * doc/guix.texi (Version Control Services): Document it. --- doc/guix.texi | 79 ++++++++++++++++++++ gnu/services/version-control.scm | 124 ++++++++++++++++++++++++++++++- 2 files changed, 202 insertions(+), 1 deletion(-) diff --git a/doc/guix.texi b/doc/guix.texi index a826171f34..e5e989c223 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -31440,6 +31440,85 @@ This setting controls the commands and features to= enable within Gitolite. @end deftp =20 =20 +@subsubheading Gitile Service + +@cindex Gitile service +@cindex Git, forge +@uref{https://git.lepiller.eu/gitile, Gitile} is a Git forge for viewing +public git repository contents from a web browser. + +Gitile works best in collaboration with Gitolite, and will serve the public +repositories from Gitolite by default. + +The following example will configure Gitile to serve repositories from a +custom location, with some default messages for the home page and the +footers. + +@lisp +(service gitile-service-type + (gitile-configuration + (repositories "/srv/git") + (base-git-url "https://myweb.site/git") + (index-title "My git repositories") + (intro '((p "This is all my public work!"))) + (footer '((p "This is the end"))) + (nginx-server-block + (nginx-server-configuration + (ssl-certificate + "/etc/letsencrypt/live/myweb.site/fullchain.pem") + (ssl-certificate-key + "/etc/letsencrypt/live/myweb.site/privkey.pem") + (listen '("443 ssl http2" "[::]:443 ssl http2")) + (locations + (list + (git-http-nginx-location-configuration + (git-http-configuration + (uri-path "/git/") + (git-root "/var/lib/gitolite/repositories"))))))))) +@end lisp + +@deftp {Data Type} gitile-configuration +Data type representing the configuration for @code{gitile-service-type}. + +@table @asis +@item @code{package} (default: @var{gitile}) +Gitile package to use. + +@item @code{host} (default: @code{"localhost"}) +The host on which gitile is listening. + +@item @code{port} (default: @code{8080}) +The port on which gitile is listening. + +@item @code{database} (default: @code{"/var/lib/gitile/gitile-db.sql"}) +The location of the database. + +@item @code{repositories} (default: @code{"/var/lib/gitolite/repositories"= }) +The location of the repositories. Note that only public repositories will +be shown by Gitile. To make a repository public, add an empty +@file{git-daemon-export-ok} file at the root of that repository. + +@item @code{base-git-url} +The base git url that will be used to show clone commands. + +@item @code{index-title} (default: @code{"Index"}) +The page title for the index page that lists all the available repositorie= s. + +@item @code{intro} (default: @code{'()}) +The intro content, as a list of sxml expressions. This is shown above the= list +of repositories, on the index page. + +@item @code{footer} (default: @code{'()}) +The footer content, as a list of sxml expressions. This is shown on every +page served by Gitile. + +@item @code{nginx-server-block} +An nginx server block that will be extended and used as a reverse proxy by +Gitile to serve its pages, and as a normal web server to serve its assets. +@end table +@end deftp + + @node Game Services @subsection Game Services =20 diff --git a/gnu/services/version-control.scm b/gnu/services/version-contro= l.scm index 8cb5633165..f28206b239 100644 --- a/gnu/services/version-control.scm +++ b/gnu/services/version-control.scm @@ -4,6 +4,7 @@ ;;; Copyright =C2=A9 2017 Oleg Pykhalov ;;; Copyright =C2=A9 2017 Cl=C3=A9ment Lassieur ;;; Copyright =C2=A9 2018 Christopher Baines +;;; Copyright =C2=A9 2021 Julien Lepiller ;;; ;;; This file is part of GNU Guix. ;;; @@ -58,7 +59,22 @@ gitolite-rc-file-roles gitolite-rc-file-enable =20 - gitolite-service-type)) + gitolite-service-type + + + gitile-configuration + gitile-configuration-package + gitile-configuration-host + gitile-configuration-port + gitile-configuration-database + gitile-configuration-repositories + gitile-configuration-git-base-url + gitile-configuration-index-title + gitile-configuration-intro + gitile-configuration-footer + gitile-configuration-nginx-server-block + + gitile-service-type)) =20 ;;; Commentary: ;;; @@ -380,3 +396,109 @@ access to exported repositories under @file{/srv/git}= ." By default, the @code{git} user is used, but this is configurable. Additionally, Gitolite can integrate with with tools like gitweb or cgit to provide a web interface to view selected repositories."))) + +;;; +;;; Gitile +;;; + +(define-record-type* + gitile-configuration make-gitile-configuration gitile-configuration? + (package gitile-configuration-package + (default gitile)) + (host gitile-configuration-host + (default "localhost")) + (port gitile-configuration-port + (default 8080)) + (database gitile-configuration-database + (default "/var/lib/gitile/gitile-db.sql")) + (repositories gitile-configuration-repositories + (default "/var/lib/gitolite/repositories")) + (base-git-url gitile-configuration-base-git-url) + (index-title gitile-configuration-index-title + (default "Index")) + (intro gitile-configuration-intro + (default '())) + (footer gitile-configuration-footer + (default '())) + (nginx-server-block nginx-configuration-nginx-server-block)) + +(define (gitile-config-file host port database repositories base-git-url + index-title intro footer) + (define build + #~(write `(config + (port #$port) + (host #$host) + (database #$database) + (repositories #$repositories) + (base-git-url #$base-git-url) + (index-title #$index-title) + (intro #$intro) + (footer #$footer)) + (open-output-file #$output))) + + (computed-file "gitile.conf" build)) + +(define gitile-nginx-server-block + (match-lambda + (($ package host port database repositories + base-git-url index-title intro footer nginx-server-block) + (list (nginx-server-configuration + (inherit nginx-server-block) + (locations + (append + (list + (nginx-location-configuration + (uri "/") + (body + (list + #~(string-append "proxy_pass http://" #$ho= st + ":" (number->string #$por= t) + "/;"))))) + (map + (lambda (loc) + (nginx-location-configuration + (uri loc) + (body + (list + #~(string-append "root " package "/share/gitile= /assets;"))))) + '("/css" "/js" "/images")) + (nginx-server-configuration-locations nginx-server-block)= ))))))) + +(define gitile-shepherd-service + (match-lambda + (($ package host port database repositories + base-git-url index-title intro footer nginx-server-block) + (list (shepherd-service + (provision '(gitile)) + (requirement '(loopback)) + (documentation "gitile") + (start (let ((gitile (file-append package "/bin/gitile"))) + #~(make-forkexec-constructor + `(,#$gitile "-c" #$(gitile-config-file + host port database + repositories + base-git-url index-title + intro footer)) + #:user "gitile" + #:group "git"))) + (stop #~(make-kill-destructor))))))) + +(define %gitile-accounts + (list (user-account + (name "gitile") + (group "git") + (system? #t) + (comment "Gitile user") + (home-directory "/var/empty") + (shell (file-append shadow "/sbin/nologin"))))) + +(define gitile-service-type + (service-type + (name 'gitile) + (extensions + (list (service-extension account-service-type + (const %gitile-accounts)) + (service-extension shepherd-root-service-type + gitile-shepherd-service) + (service-extension nginx-service-type + gitile-nginx-server-block))))) --=20 2.32.0 --MP_/9V5R=VpoWcy+VCTvn/d+M75--