about summary refs log tree commit diff
path: root/genenetwork-development.scm
diff options
context:
space:
mode:
Diffstat (limited to 'genenetwork-development.scm')
-rw-r--r--genenetwork-development.scm383
1 files changed, 270 insertions, 113 deletions
diff --git a/genenetwork-development.scm b/genenetwork-development.scm
index f841255..8e4e1e8 100644
--- a/genenetwork-development.scm
+++ b/genenetwork-development.scm
@@ -21,19 +21,20 @@
 ;;; <https://www.gnu.org/licenses/>.
 
 (use-modules (gnu)
-             ((gn packages genenetwork) #:select (genenetwork2 genenetwork3 gn-auth gn-libs))
+             ((gn-machines genenetwork) #:select (genenetwork2 genenetwork3 gn-auth))
              (gn services databases)
              ((gn packages guile) #:select (gn-guile))
              (gnu build linux-container)
              ((gnu packages admin) #:select (shepherd shadow))
-             ((gnu packages base) #:select (gnu-make tar))
+             ((gnu packages base) #:select (gnu-make tar coreutils-minimal))
              ((gnu packages bash) #:select (bash))
-             ((gnu packages bioinformatics) #:select (ccwl) #:prefix guix:)
-             ((gnu packages certs) #:select (nss-certs))
+             ((gnu packages nss) #:select (nss-certs))
              ((gnu packages check) #:select (python-pylint))
+             ((gnu packages curl) #:select (curl))
              ((gnu packages ci) #:select (laminar))
              ((gnu packages compression) #:select (gzip))
-             ((gnu packages databases) #:select (mariadb virtuoso-ose))
+             ((gnu packages databases) #:select (mariadb redis))
+             ((gnu packages databases) #:select (virtuoso-ose))
              ((gnu packages gnupg) #:select (guile-gcrypt))
              ((gnu packages graphviz) #:select (graphviz))
              ((gnu packages guile) #:select (guile-3.0 guile-git guile-zlib))
@@ -84,10 +85,6 @@
 (define %guix-daemon-uri
   "/var/host-guix/daemon-socket/socket")
 
-(define %default-guix-channel-with-substitutes
-  (channel-with-substitutes-available %default-guix-channel
-                                      "https://ci.guix.gnu.org"))
-
 ;; We cannot refer to sudo in the store since that sudo does not have
 ;; the setuid bit set. See "(guix) Setuid Programs".
 (define sudo
@@ -122,9 +119,11 @@ be imported into G-expressions."
   (gn3-repository genenetwork-configuration-gn3-repository
                   (default "https://github.com/genenetwork/genenetwork3"))
   (gn-auth-repository genenetwork-configuration-gn-auth-repository
-                      (default "/home/git/public/gn-auth"))
+                      (default "https://git.genenetwork.org/gn-auth"))
   (gn-libs-repository genenetwork-configuration-gn-libs-repository
-                      (default "/home/git/public/gn-libs"))
+                      (default "https://git.genenetwork.org/gn-libs"))
+  (gn-guile-repository genenetwork-configuration-gn-libs-repository
+                      (default "https://git.genenetwork.org/gn-guile"))
   (gn2-port genenetwork-configuration-gn2-port
             (default 8082))
   (gn3-port genenetwork-configuration-gn3-port
@@ -149,8 +148,12 @@ be imported into G-expressions."
                 (default "/export/data/genenetwork-sqlite/auth.db"))
   (llm-db-path genenetwork-llm-db-path
                (default "/export/data/genenetwork-sqlite/llm.db"))
+  (lmdb-data-path genenetwork-lmdb-data-path
+                  (default "/export/data/lmdb"))
   (gn-guile-port genenetwork-configuration-gn-guile-port
                  (default 8091))
+  (repositories genenetwork-configuration-repositories
+                 (default "/export/data/repositories"))
   (gn-doc-git-checkout genenetwork-configuration-gn-doc-git-checkout
                        (default "/export/data/gn-docs")))
 
@@ -192,7 +195,7 @@ executed."
   (match-record config <genenetwork-configuration>
     (gn2-repository gn3-repository gn-libs-repository gn3-port genotype-files)
     (with-imported-modules '((guix build utils))
-      (with-packages (list bash coreutils git-minimal nss-certs)
+      (with-packages (list bash coreutils git-minimal nss-certs genenetwork2)
         #~(begin
             (use-modules (guix build utils)
                          (srfi srfi-26))
@@ -224,10 +227,6 @@ executed."
             (invoke "git" "clone" "--depth" "1" #$gn2-repository)
             (with-directory-excursion "genenetwork2"
               (show-head-commit))
-            ;; This is a dummy SERVER_PORT to placate
-            ;; bin/genenetwork2. TODO: Fix bin/genenetwork2 so that
-            ;; this is not needed.
-            (setenv "SERVER_PORT" "8080")
             ;; Use a profile with all dependencies except
             ;; genenetwork3.
             (setenv "GN2_PROFILE"
@@ -241,7 +240,30 @@ executed."
             (setenv "GN3_LOCAL_URL" (string-append "http://localhost:" (number->string #$gn3-port)))
             (setenv "GENENETWORK_FILES" #$genotype-files)
             (setenv "HOME" "/tmp")
-            (setenv "SQL_URI" "mysql://webqtlout:webqtlout@localhost/db_webqtl?unix_socket=/run/mysqld/mysqld.sock")
+	    ;; This file is cosmetic
+	    (setenv
+             "GN2_SETTINGS"
+             #$(mixed-text-file "gn2.conf"
+		 "GN2_SECRETS=\"/tmp/secret.py\"\n"
+                 "AI_SEARCH_ENABLED=True\n"
+		 "TEST_FEATURE_SWITCH=True\n"
+		 "GN3_LOCAL_URL=\"http://localhost:120\"\n"
+		 "GN3_GUILE_SERVER_URL=\"http://localhost:120\"\n"
+                 "GN_SERVER_URL=\"https://cd.genenetwork.org/api3/\"\n"
+                 "AUTH_SERVER_URL=\"https://auth-cd.genenetwork.org/\"\n"
+                 "SQL_URI=\"mysql://webqtlout:webqtlout@localhost/db_webqtl?unix_socket=/run/mysqld/mysqld.sock\"\n"
+                 "SSL_PRIVATE_KEY=\"/tmp/ssl-private.pem\"\n"
+                 "AUTH_SERVER_SSL_PUBLIC_KEY=\"/tmp/gn-auth-ssl-public-key.pem\"\n"))
+            (setenv "SQL_URI" "mysql://webqtlout:webqtlout@localhost/db_webqtl?unix_socket=/run/mysqld/mysqld.sock&charset=utf8")
+	    (setenv "PATH" (string-append (getenv "GN2_PROFILE") "/bin:$PATH"))
+	    (setenv "R_LIBS_SITE" (string-append (getenv "GN2_PROFILE") "/site-library"))
+	    (setenv "JS_GUIX_PATH" (string-append (getenv "GN2_PROFILE") "/share/genenetwork2/javascript"))
+	    (setenv "GUIX_GENENETWORK_FILES" (string-append (getenv "GN2_PROFILE") "/share/genenetwork2"))
+	    (setenv "GENENETWORK_FILES" "/export/data/genenetwork/genotype_files")
+	    (setenv "PLINK_COMMAND" (string-append (getenv "GN2_PROFILE") "/bin/plink2"))
+	    (setenv "GEMMA_COMMAND" (string-append (getenv "GN2_PROFILE") "/bin/gemma"))
+	    (setenv "GEMMA_WRAPPER_COMMAND" (string-append (getenv "GN2_PROFILE") "/bin/gemma-wrapper"))
+	    (setenv "HOME" "/tmp")
             (chdir "genenetwork2")
             ;; XXXX: FIXME: R/Qtl tests fail because files are generated in
             ;; the "/tmp" directory.  Currently, "/tmp" is mapped by gn2/gn3
@@ -277,7 +299,7 @@ genenetwork3 source from the latest commit of @var{project}."
                 (setenv "PYTHONPATH" (getcwd))
                 (invoke "./scripts/index-genenetwork" "create-xapian-index"
                         xapian-build-directory
-                        "mysql://webqtlout:webqtlout@localhost/db_webqtl?unix_socket=/run/mysqld/mysqld.sock"
+                        "mysql://webqtlout:webqtlout@localhost/db_webqtl?unix_socket=/run/mysqld/mysqld.sock&charset=utf8"
                         "http://localhost:9082/sparql")
                 ;; Stop genenetwork3, replace old xapian index and
                 ;; start genenetwork3.
@@ -332,16 +354,13 @@ genenetwork3 source from the latest commit of @var{project}."
   "Return forge projects for genenetwork described by CONFIG, a
 <genenetwork-configuration> object."
   (match-record config <genenetwork-configuration>
-    (gn2-repository gn3-repository gn-auth-repository gn-libs-repository gn2-port)
+    (gn2-repository gn3-repository gn-auth-repository gn-libs-repository gn2-port gn-guile-port gn-guile-repository)
     (list (forge-project
            (name "genenetwork2")
            (repository gn2-repository)
            (ci-jobs (list (forge-laminar-job
                            (name "genenetwork2")
-                           (run (genenetwork2-tests
-                                 config
-                                 (list "sh" "bin/genenetwork2" "./gn2/default_settings.py"
-                                       "-c" "-m" "pytest")))
+                           (run (genenetwork2-tests config (list "python3" "-m" "pytest")))
                            ;; If unit tests pass, redeploy genenetwork2 and
                            ;; trigger Mechanical Rob.
                            (after (with-imported-modules '((guix build utils))
@@ -357,8 +376,7 @@ genenetwork3 source from the latest commit of @var{project}."
                            (name "genenetwork2-mechanical-rob")
                            (run (genenetwork2-tests
                                  config
-                                 (list "sh" "bin/genenetwork2" "./gn2/default_settings.py"
-                                       "-c" "test/requests/test-website.py"
+                                 (list "python3" "test/requests/test-website.py"
                                        "--all" (string-append "http://localhost:" (number->string gn2-port)))))
                            (trigger? #f))))
            (ci-jobs-trigger 'webhook))
@@ -417,7 +435,20 @@ genenetwork3 source from the latest commit of @var{project}."
                                  #:variables (list (variable-specification
                                                     (module '(gn-libs))
                                                     (name 'gn-libs)))
-                                 #:guix-daemon-uri %guix-daemon-uri))))))
+                                 #:guix-daemon-uri %guix-daemon-uri)))))
+           (ci-jobs-trigger 'webhook))
+          (forge-project
+           (name "gn-guile")
+           (repository gn-guile-repository)
+           (ci-jobs (list (forge-laminar-job
+                           (name "gn-guile")
+                           (run (with-imported-modules '((guix build utils))
+                                  #~(begin
+                                      (use-modules (guix build utils))
+                                      (invoke #$sudo
+                                                #$(file-append shepherd "/bin/herd")
+                                                "restart" "gn-guile")))))))
+           (ci-jobs-trigger 'webhook))
           (forge-project
            (name "gn-auth")
            (repository gn-auth-repository)
@@ -453,13 +484,14 @@ genenetwork3 source from the latest commit of @var{project}."
                                  #:variables (list (variable-specification
                                                     (module '(gn-auth))
                                                     (name 'gn-auth-all-tests)))
-                                 #:guix-daemon-uri %guix-daemon-uri)))))))))
+                                 #:guix-daemon-uri %guix-daemon-uri)))))
+           (ci-jobs-trigger 'webhook)))))
 
 (define (genenetwork2-cd-gexp config)
   "Return a G-expression that runs the latest genenetwork2 development
 server described by CONFIG, a <genenetwork-configuration> object."
   (match-record config <genenetwork-configuration>
-    (gn2-repository gn3-repository gn2-port gn3-port gn2-secrets genotype-files)
+    (gn2-repository gn3-repository gn2-port gn3-port gn2-secrets genotype-files gn-guile-port)
     (with-packages (list coreutils git-minimal gunicorn nss-certs)
       (with-imported-modules '((guix build utils))
         #~(begin
@@ -476,33 +508,52 @@ server described by CONFIG, a <genenetwork-configuration> object."
               (hline)
               (invoke "git" "log" "--max-count" "1")
               (hline))
-
-            ;; Clone the latest genenetwork2 and genenetwork3
+	    (setenv "GIT_PAGER" #$(file-append coreutils-minimal "/bin/cat"))
+	    (setenv "TERM" "xterm-256color")
+            ;; Clone the latest genenetwork2
             ;; repositories.
-            (invoke "git" "clone" "--depth" "1" #$gn2-repository)
-            (with-directory-excursion "genenetwork2"
-              (show-head-commit))
-            (invoke "git" "clone" "--depth" "1" #$gn3-repository)
-            (with-directory-excursion "genenetwork3"
-              (show-head-commit))
+	    (with-directory-excursion
+	     "/home/genenetwork"
+	     (when (file-exists? "/home/genenetwork/genenetwork2")
+	       (delete-file-recursively "/home/genenetwork/genenetwork2"))
+	     (invoke "git" "clone" "--depth" "1" #$gn2-repository))
 
             ;; Override the genenetwork3 used by genenetwork2.
-            (setenv "GN3_PYTHONPATH"
-                    (string-append (getcwd) "/genenetwork3"))
+            (setenv "GN3_PYTHONPATH" "/home/genenetwork/genenetwork3")
             ;; Set other environment variables required by
             ;; genenetwork2.
             (setenv "GN2_PROFILE" #$(profile
                                      (content (package->development-manifest genenetwork2))
                                      (allow-collisions? #t)))
+            (setenv "REQUESTS_CA_BUNDLE" (string-append
+                                          (getenv "GN2_PROFILE")
+                                          "/etc/ssl/certs/ca-certificates.crt"))
+	    (setenv "PYTHONPATH" (string-append
+				  (getenv "GN2_PROFILE")
+				  "/lib/python3.11/site-packages"))
+	    (setenv "PATH" (string-append (getenv "GN2_PROFILE") "/bin:$PATH"))
+	    (setenv "R_LIBS_SITE" (string-append (getenv "GN2_PROFILE") "/site-library"))
+	    (setenv "JS_GUIX_PATH" (string-append (getenv "GN2_PROFILE") "/share/genenetwork2/javascript"))
+	    (setenv "GUIX_GENENETWORK_FILES" (string-append (getenv "GN2_PROFILE") "/share/genenetwork2"))
+	    (setenv "GENENETWORK_FILES" #$genotype-files)
+	    (setenv "PLINK_COMMAND" (string-append (getenv "GN2_PROFILE") "/bin/plink2"))
+	    (setenv "GEMMA_COMMAND" (string-append (getenv "GN2_PROFILE") "/bin/gemma"))
+	    (setenv "GEMMA_WRAPPER_COMMAND" (string-append (getenv "GN2_PROFILE") "/bin/gemma-wrapper"))
+	    (setenv "HOME" "/home/genenetwork")
             (setenv
              "GN2_SETTINGS"
              #$(mixed-text-file "gn2.conf"
                                 "GN2_SECRETS=\"" gn2-secrets "/gn2-secrets.py\"\n"
                                 "AI_SEARCH_ENABLED=True\n"
+				"TEST_FEATURE_SWITCH=True\n"
                                 "GN3_LOCAL_URL=\""
                                 (string-append "http://localhost:"
                                                (number->string gn3-port))
                                 "\"\n"
+                                "GN_GUILE_SERVER_URL=\""
+                                (string-append "http://localhost:"
+                                               (number->string gn-guile-port))
+                                "\"\n"
                                 "GN_SERVER_URL=\"https://cd.genenetwork.org/api3/\"\n"
                                 "AUTH_SERVER_URL=\"https://auth-cd.genenetwork.org/\"\n"
                                 "SQL_URI=\"mysql://webqtlout:webqtlout@localhost/db_webqtl?unix_socket=/run/mysqld/mysqld.sock\"\n"
@@ -510,15 +561,23 @@ server described by CONFIG, a <genenetwork-configuration> object."
                                 "AUTH_SERVER_SSL_PUBLIC_KEY=\"" gn2-secrets "/gn-auth-ssl-public-key.pem\"\n"))
 
             ;; Start genenetwork2.
-            (with-directory-excursion "genenetwork2"
-              (invoke #$(file-append bash "/bin/sh")
-                      "bin/genenetwork2" "gn2/default_settings.py" "-gunicorn-prod")))))))
+            (with-directory-excursion
+	     "/home/genenetwork/genenetwork2"
+	     (show-head-commit)
+	     (invoke #$(file-append gunicorn "/bin/gunicorn")
+		     "--bind" (string-append "0.0.0.0:" (number->string #$gn2-port))
+		     "--workers" "20"
+		     "--keep-alive" "6000"
+		     "--max-requests" "100"
+		     "--max-requests-jitter" "30"
+		     "--timeout" "1200"
+		     "gn2.wsgi")))))))
 
 (define (genenetwork3-cd-gexp config)
   "Return a G-expression that runs the latest genenetwork3 development
 server described by CONFIG, a <genenetwork-configuration> object."
   (match-record config <genenetwork-configuration>
-    (gn3-repository gn3-port gn3-secrets sparql-endpoint data-directory xapian-db-path auth-db-path llm-db-path)
+    (gn3-repository gn3-port gn3-secrets sparql-endpoint data-directory xapian-db-path auth-db-path llm-db-path lmdb-data-path)
     (with-manifest (package->development-manifest genenetwork3)
       (with-packages (list git-minimal nss-certs)
         (with-imported-modules '((guix build utils))
@@ -537,35 +596,48 @@ server described by CONFIG, a <genenetwork-configuration> object."
                 (invoke "git" "log" "--max-count" "1")
                 (hline))
 
-              ;; Clone the latest genenetwork3 repository.
-              (invoke "git" "clone" "--depth" "1" #$gn3-repository)
+	      (setenv "GIT_PAGER" #$(file-append coreutils-minimal "/bin/cat"))
+	      (setenv "GN3_PROFILE" #$(profile
+                                       (content (package->development-manifest genenetwork3))
+                                       (allow-collisions? #t)))
+              (setenv "REQUESTS_CA_BUNDLE" (string-append
+                                            (getenv "GN3_PROFILE")
+                                            "/etc/ssl/certs/ca-certificates.crt"))
               ;; Configure genenetwork3.
               (setenv "GN3_CONF"
                       #$(mixed-text-file "gn3.conf"
                                          "SPARQL_ENDPOINT=\"" sparql-endpoint "\"\n"
                                          "DATA_DIR=\"" data-directory "\"\n"
+                                         "LMDB_DATA_PATH=\"" lmdb-data-path "\"\n"
                                          "AUTH_SERVER_URL=\"https://auth-cd.genenetwork.org/\"\n"
                                          "XAPIAN_DB_PATH=\"" xapian-db-path "\"\n"
                                          "AUTH_DB=\"" auth-db-path "\"\n"
                                          "LLM_DB_PATH=\"" llm-db-path "\"\n"))
               (setenv "HOME" "/tmp")
               (setenv "GN3_SECRETS" #$gn3-secrets)
-              (setenv "RSCRIPT" #$(file-append
-                                   (profile
-                                    (content (package->development-manifest genenetwork3))
-                                    (allow-collisions? #t))
-                                   "/bin/Rscript"))
-              ;; Run genenetwork3.
-              (with-directory-excursion "genenetwork3"
-                (show-head-commit)
-                (invoke #$(file-append gunicorn "/bin/gunicorn")
-                        "-b" #$(string-append "localhost:" (number->string gn3-port))
-                        "--workers" "8"
-                        "gn3.app:create_app()"
-                        ;; gunicorn's default 30 second timeout is
-                        ;; insufficient for Fahamu AI endpoints and
-                        ;; results in worker timeout errors.
-                        "--timeout" "1200"))))))))
+              (setenv "RSCRIPT" (string-append
+                                 (getenv "GN3_PROFILE")
+                                 "/bin/Rscript"))
+
+	      (with-directory-excursion
+	       "/home/genenetwork"
+	       ;; Clone the latest genenetwork3 repository.
+	       (when (file-exists? "/home/genenetwork/genenetwork3")
+		 (delete-file-recursively "/home/genenetwork/genenetwork3"))
+	       (invoke "git" "clone" "--depth" "1" #$gn3-repository))
+
+	      (with-directory-excursion
+	       "/home/genenetwork/genenetwork3"
+	       (show-head-commit)
+	       ;; Run genenetwork3.
+	       (invoke #$(file-append gunicorn "/bin/gunicorn")
+		       "-b" #$(string-append "localhost:" (number->string gn3-port))
+		       "--workers" "8"
+		       "gn3.app:create_app()"
+		       ;; gunicorn's default 30 second timeout is
+		       ;; insufficient for Fahamu AI endpoints and
+		       ;; results in worker timeout errors.
+		       "--timeout" "1200"))))))))
 
 (define (gn-auth-cd-gexp config)
   "Return a G-expression that runs the latest gn-auth development
@@ -590,9 +662,23 @@ server described by CONFIG, a <genenetwork-configuration> object."
                 (invoke "git" "log" "--max-count" "1")
                 (hline))
 
-              ;; Clone the latest gn-auth repository.
-              (invoke "git" "clone" "--depth" "1" #$gn-auth-repository)
+	      (setenv "GIT_PAGER" #$(file-append coreutils-minimal "/bin/cat"))
+
+	      (with-directory-excursion
+	       "/home/genenetwork/"
+	       ;; Clone the latest gn-auth repository.
+	       (when (file-exists? "/home/genenetwork/gn-auth")
+		 (delete-file-recursively "/home/genenetwork/gn-auth"))
+               (invoke "git" "clone" "--depth" "1" #$gn-auth-repository))
+
               ;; Configure gn-auth.
+              (setenv "GN_AUTH_PROFILE" #$(profile
+                                           (content (package->development-manifest gn-auth))
+                                           (allow-collisions? #t)))
+              (setenv "REQUESTS_CA_BUNDLE" (string-append
+                                            (getenv "GN_AUTH_PROFILE")
+                                            "/etc/ssl/certs/ca-certificates.crt"))
+
               (setenv "GN_AUTH_CONF"
                       #$(mixed-text-file "gn-auth.conf"
                                          "AUTH_DB=\"" auth-db-path "\"\n"
@@ -602,7 +688,7 @@ server described by CONFIG, a <genenetwork-configuration> object."
               (setenv "HOME" "/tmp")
               (setenv "AUTHLIB_INSECURE_TRANSPORT" "true")
               ;; Run gn-auth.
-              (with-directory-excursion "gn-auth"
+              (with-directory-excursion "/home/genenetwork/gn-auth"
                 (show-head-commit)
                 (invoke #$(file-append gunicorn "/bin/gunicorn")
                         "-b" #$(string-append "localhost:" (number->string gn-auth-port))
@@ -610,23 +696,70 @@ server described by CONFIG, a <genenetwork-configuration> object."
                         "gn_auth.wsgi:app"))))))))
 
 (define (gn-guile-gexp gn-guile-port)
-  (with-imported-modules '((guix build utils))
-    #~(begin
-        (use-modules (guix build utils))
-        (let ((current-repo-path (string-append (getcwd) "/gn-docs")))
-          (when (file-exists? current-repo-path)
-            (delete-file-recursively current-repo-path))
-          (setenv "CURRENT_REPO_PATH" current-repo-path)
-          (invoke #$(file-append git-minimal "/bin/git")
-                  "clone" "--depth" "1" (getenv "CGIT_REPO_PATH")))
-        (invoke #$(file-append gn-guile "/bin/gn-guile")
-                (number->string #$gn-guile-port)))))
+  (with-packages
+   (list coreutils git-minimal nss-certs)
+   (with-imported-modules '((guix build utils))
+     #~(begin
+         (use-modules (guix build utils))
+
+         (define (hline)
+           "Print a horizontal line 50 '=' characters long."
+           (display (make-string 50 #\=))
+           (newline)
+           (force-output))
+
+         (define (show-head-commit)
+           (hline)
+           (invoke "git" "log" "--max-count" "1")
+           (hline))
+         ;; KLUDGE: Here we set all the certificates properly.  In gn-guile,
+         ;; we make request to external services.  Here's an example:
+         ;; curl http://localhost:8091/gene/aliases/Shh
+         ;;
+         ;; Without certs, we run into:
+         ;; 2025-07-22 08:27:11 GET /gene/aliases/Shh
+         ;; [...]
+         ;; 2025-07-22 08:27:19   signer-not-found invalid
+         (setenv "GN_GUILE_PROFILE" #$(profile
+                                       (content (package->development-manifest gn-guile))
+                                       (allow-collisions? #t)))
+         (setenv "SSL_CERT_DIR" (string-append
+                                 (getenv "GN_GUILE_PROFILE")
+                                 "/etc/ssl/certs"))
+         (setenv "SSL_CERT_FILE" (string-append
+                                  (getenv "GN_GUILE_PROFILE")
+                                  "/etc/ssl/certs/ca-certificates.crt"))
+         (setenv "GIT_SSL_CAINFO" (getenv "SSL_CERT_FILE"))
+         (setenv "CURL_CA_BUNDLE" (getenv "SSL_CERT_FILE"))
+         (setenv "REQUESTS_CA_BUNDLE" (getenv "SSL_CERT_FILE"))
+	 (setenv "GIT_PAGER" #$(file-append coreutils-minimal "/bin/cat"))
+         (let ((current-repo-path "/home/genenetwork/gn-docs"))
+	   (setenv "CURRENT_REPO_PATH" current-repo-path)
+	   (with-directory-excursion
+	    "/home/genenetwork"
+	    ;; All edits go to the current-repo-path; so we need it to
+	    ;; be persistent.
+	    (unless (file-exists? current-repo-path)
+              (invoke #$(file-append git-minimal "/bin/git")
+                      "clone" "--depth" "1" (getenv "CGIT_REPO_PATH") current-repo-path))
+
+	    (when (file-exists? "gn-guile")
+              (delete-file-recursively "gn-guile"))
+	    (invoke "git" "clone" "--depth" "1" "https://git.genenetwork.org/gn-guile")
+	    ;; We have a gn-guile-dev wrapper script that sets a "./" in the
+            ;; GN_GUILE_LOAD_PATH hence allowing this to be run from the gn-guile
+            ;; directory.  This allows gn-guile to be run from the latest
+            ;; upstream commits without pinning to guix.
+            (with-directory-excursion "gn-guile"
+				      (show-head-commit)
+				      (invoke #$(file-append gn-guile "/bin/gn-guile-dev")
+					      (number->string #$gn-guile-port)))))))))
 
 (define (genenetwork-shepherd-services config)
   "Return shepherd services to run the genenetwork development server
 described by CONFIG, a <genenetwork-configuration> object."
   (match-record config <genenetwork-configuration>
-    (gn2-port gn3-port gn-auth-port genotype-files data-directory xapian-db-path gn2-secrets auth-db-path gn-auth-secrets llm-db-path gn-doc-git-checkout gn-guile-port)
+    (gn2-port gn3-port gn-auth-port genotype-files data-directory xapian-db-path gn2-secrets auth-db-path gn-auth-secrets llm-db-path lmdb-data-path gn-doc-git-checkout gn-guile-port)
     (list (shepherd-service
            (documentation "Run gn-guile server.")
            (provision '(gn-guile))
@@ -649,6 +782,10 @@ described by CONFIG, a <genenetwork-configuration> object."
                           #:mappings (list (file-system-mapping
                                             (source gn-doc-git-checkout)
                                             (target source)
+                                            (writable? #t))
+					   (file-system-mapping
+                                            (source "/home/genenetwork")
+                                            (target source)
                                             (writable? #t)))
                           #:namespaces (delq 'net %namespaces))
                        "127.0.0.1" #$(number->string gn-guile-port))
@@ -664,11 +801,13 @@ described by CONFIG, a <genenetwork-configuration> object."
           (shepherd-service
            (documentation "Run GeneNetwork 2 development server.")
            (provision '(genenetwork2))
-           ;; FIXME: The genenetwork2 service should depend on redis.
-           (requirement '(networking genenetwork3))
+           (requirement '(networking redis))
            (modules '((guix search-paths)
                       (ice-9 match)
                       (srfi srfi-1)))
+	   ;; KLUDGE: The default value of 0.5 is too low, and causes
+	   ;; gn2 to be disabled when it is respawned "too fast."
+	   (respawn-delay 5)
            (start
             (let* ((gn2-manifest (packages->manifest (list genenetwork2)))
                    (gn2-profile (profile
@@ -698,6 +837,10 @@ described by CONFIG, a <genenetwork-configuration> object."
                             #:mappings (list (file-system-mapping
                                               (source genotype-files)
                                               (target source))
+					     (file-system-mapping
+                                              (source "/home/genenetwork")
+                                              (target source)
+                                              (writable? #t))
                                              (file-system-mapping
                                               (source "/run/mysqld")
                                               (target source)
@@ -743,6 +886,9 @@ described by CONFIG, a <genenetwork-configuration> object."
            (documentation "Run GeneNetwork 3 development server.")
            (provision '(genenetwork3))
            (requirement '(networking))
+	   ;; KLUDGE: The default value of 0.5 is too low, and causes
+	   ;; gn3 to be disabled when it is respawned "too fast."
+	   (respawn-delay 5)
            (start #~(make-forkexec-constructor
                      (list #$(least-authority-wrapper
                               (program-file "genenetwork3"
@@ -755,6 +901,14 @@ described by CONFIG, a <genenetwork-configuration> object."
                                                 (source "/run/mysqld")
                                                 (target source)
                                                 (writable? #t))
+					       (file-system-mapping
+						(source "/home/genenetwork")
+						(target source)
+						(writable? #t))
+                                               (file-system-mapping
+                                                (source lmdb-data-path)
+                                                (target source)
+                                                (writable? #t))
                                                ;; XXXX: FIXME: R/Qtl generates
                                                ;; files in "/tmp" and
                                                ;; "/tmp/gn2".  These files are
@@ -775,11 +929,11 @@ described by CONFIG, a <genenetwork-configuration> object."
                                                 (target source)
                                                 (writable? #t))
                                                (file-system-mapping
-                                                (source auth-db-path)
+                                                (source (dirname auth-db-path))
                                                 (target source)
                                                 (writable? #t))
-                                               (file-system-mapping
-                                                (source llm-db-path)
+					       (file-system-mapping
+                                                (source (dirname llm-db-path))
                                                 (target source)
                                                 (writable? #t)))
                               #:namespaces (delq 'net %namespaces))
@@ -792,6 +946,9 @@ described by CONFIG, a <genenetwork-configuration> object."
            (documentation "Run gn-auth development server.")
            (provision '(gn-auth))
            (requirement '(networking))
+	   ;; KLUDGE: The default value of 0.5 is too low, and causes
+	   ;; gn-auth to be disabled when it is respawned "too fast."
+	   (respawn-delay 5)
            (start #~(make-forkexec-constructor
                      (list #$(least-authority-wrapper
                               (program-file "gn-auth"
@@ -804,11 +961,15 @@ described by CONFIG, a <genenetwork-configuration> object."
                                                 (source "/run/mysqld")
                                                 (target source)
                                                 (writable? #t))
+					       (file-system-mapping
+						(source "/home/genenetwork")
+						(target source)
+						(writable? #t))
                                                (file-system-mapping
                                                 (source data-directory)
                                                 (target source))
                                                (file-system-mapping
-                                                (source auth-db-path)
+                                                (source (dirname auth-db-path))
                                                 (target source)
                                                 (writable? #t))
                                                (file-system-mapping
@@ -831,15 +992,29 @@ described by CONFIG, a <genenetwork-configuration> object."
          (group "genenetwork")
          (system? #t)
          (comment "GeneNetwork user")
-         (home-directory "/var/empty")
+         (home-directory "/home/genenetwork")
          (shell (file-append shadow "/sbin/nologin")))))
 
 (define (genenetwork-activation config)
   (match-record config <genenetwork-configuration>
-    (gn2-secrets gn3-secrets auth-db-path gn-auth-secrets)
+    (gn2-secrets gn3-secrets auth-db-path gn-auth-secrets gn-doc-git-checkout)
     (with-imported-modules '((guix build utils))
       #~(begin
-          (use-modules (guix build utils))
+          (use-modules (guix build utils)
+		       (ice-9 ftw))
+	  ;; KLUDGE: Guix now stores inferior profiles under
+	  ;; /var/guix/profiles/per-user (commit
+	  ;; d12c4452a49b355369636de1dfc766b5bad6437b).  The 'laminar'
+	  ;; user’s directory is not created automatically in our
+	  ;; pinned Guix revision, which causes CI jobs using
+	  ;; inferiors to fail with permission errors.
+	  ;; XXXX: FIXME: Explicitly create the directory for
+	  ;; now. Remove this once we update the pinned Guix commit.
+	  (unless (file-exists? "/var/guix/profiles/per-user/laminar")
+	    (mkdir-p "/var/guix/profiles/per-user/laminar")
+	    (chown "/var/guix/profiles/per-user/laminar"
+		   (passwd:uid (getpw "laminar"))
+		   (passwd:gid (getpw "laminar"))))
 
           ;; Set ownership of files.
           (for-each (lambda (file)
@@ -849,12 +1024,13 @@ described by CONFIG, a <genenetwork-configuration> object."
                     (cons* #$gn3-secrets
                            (append (find-files #$gn2-secrets
                                                #:directories? #t)
-                                   (find-files "/export/data/gn-docs"
+                                   (find-files gn-doc-git-checkout
                                                #:directories? #t)
                                    (find-files #$(dirname auth-db-path)
                                                #:directories? #t)
                                    (find-files #$gn-auth-secrets
                                                #:directories? #t))))
+
           ;; Prevent other users from reading secret files.
           (for-each (lambda (file)
                       (chmod file #o600))
@@ -884,27 +1060,6 @@ described by CONFIG, a <genenetwork-configuration> object."
 ;;; transform-genenetwork-database
 ;;;
 
-
-;; Unreleased version of ccwl that is required by
-;; transform-genenetwork-database for its graphql library.
-(define ccwl
-  (let ((commit "02677a508b407779f5991a230341e016deb7f69b")
-        (revision "0"))
-    (package
-      (inherit guix:ccwl)
-      (name "ccwl")
-      (version (git-version (package-version guix:ccwl) revision commit))
-      (source
-       (origin
-         (method git-fetch)
-         (uri (git-reference
-               (url "https://github.com/arunisaac/ccwl")
-               (commit commit)))
-         (file-name (git-file-name name version))
-         (sha256
-          (base32
-           "1kxry8y0pibl0x789jrzqkkh2s59ajyinfvrgvd00gkbqldih82r")))))))
-
 ;; guile-sparql tests are broken. Disable them temporarily. The issue
 ;; has been reported upstream at
 ;; https://github.com/roelj/guile-sparql/issues/6
@@ -949,7 +1104,7 @@ described by CONFIG, a <genenetwork-configuration> object."
 
 (define (transform-genenetwork-database-gexp connection-settings virtuoso-data-dir repository)
   (with-imported-modules '((guix build utils))
-    (with-packages (list ccwl git-minimal gnu-make guile-3.0 guile-dbd-mysql
+    (with-packages (list git-minimal gnu-make guile-3.0 guile-dbd-mysql
                          guile-dbi guile-hashing guile-libyaml guile-sparql
                          guile-zlib nss-certs virtuoso-ose raptor2)
      #~(begin
@@ -995,7 +1150,7 @@ described by CONFIG, a <genenetwork-configuration> object."
                                 (name 'transform-genenetwork-database)
                                 (url (forge-project-repository this-forge-project))
                                 (branch "master"))
-                               %default-guix-channel-with-substitutes)
+                               %default-guix-channel)
                          #:guix-daemon-uri %guix-daemon-uri)))
                   (forge-laminar-job
                    (name "transform-genenetwork-database")
@@ -1041,7 +1196,7 @@ described by CONFIG, a <genenetwork-configuration> object."
                                 (name 'guile-lmdb)
                                 (url (forge-project-repository this-forge-project))
                                 (branch "master"))
-                               %default-guix-channel-with-substitutes)
+                               %default-guix-channel)
                          #:guix-daemon-uri %guix-daemon-uri)))))
    (ci-jobs-trigger 'webhook)))
 
@@ -1061,7 +1216,7 @@ described by CONFIG, a <genenetwork-configuration> object."
                                 (name 'guile-gsl)
                                 (url (forge-project-repository this-forge-project))
                                 (branch "master"))
-                               %default-guix-channel-with-substitutes)
+                               %default-guix-channel)
                          #:guix-daemon-uri %guix-daemon-uri)))))
    (ci-jobs-trigger 'webhook)))
 
@@ -1081,7 +1236,7 @@ described by CONFIG, a <genenetwork-configuration> object."
                                 (name 'guile-lapack)
                                 (url (forge-project-repository this-forge-project))
                                 (branch "master"))
-                               %default-guix-channel-with-substitutes)
+                               %default-guix-channel)
                          #:guix-daemon-uri %guix-daemon-uri)))))
    (ci-jobs-trigger 'webhook)))
 
@@ -1308,13 +1463,14 @@ gn-auth."
                (targets (list "/dev/sdX"))))
   (file-systems %base-file-systems)
   (users %base-user-accounts)
-  (packages %base-packages)
+  (packages (cons* curl coreutils-minimal %base-packages))
   (sudoers-file
    (mixed-text-file "sudoers"
                     "@include " %sudoers-specification
                     ;; Permit the laminar user to restart genenetwork2
                     ;; and genenetwork3.
                     "\nlaminar ALL = NOPASSWD: "
+                    (file-append shepherd "/bin/herd") " restart gn-guile, "
                     (file-append shepherd "/bin/herd") " restart genenetwork2, "
                     (file-append shepherd "/bin/herd") " start genenetwork3, "
                     (file-append shepherd "/bin/herd") " stop genenetwork3, "
@@ -1418,7 +1574,8 @@ gn-auth."
                                      %genenetwork2-port %genenetwork3-port)
                                     (laminar-reverse-proxy-server-block
                                      "localhost:9089" %webhook-port
-                                     (list 'gn-bioinformatics))
+                                     (list 'gn-bioinformatics
+                                           'guix-bioinformatics))
                                     (tissue-reverse-proxy-server-block)
                                     (gn-auth-reverse-proxy-server-block)))))
                    (service acme-service-type