Subject: [bug#72803] [PATCH v9] services: restic-backup: Drop restic-guix command.

From: Giacomo Leidi <[email-masked]>
Date: Tue, 29 Apr 2025 02:00:16 +0200

restic-guix has always been close to a reimplementation of a restic
command line.  https://github.com/restic/restic/issues/16 was then found
out, indicating a missing feature upstream.  To avoid the maintenance
cost we drop the restic-guix package.  Its functionality can be
implemented in a separate project or by using one of the different FOSS
projects implementing a configuration file for restic.

* gnu/services/backup.scm: Drop mcron obsolete export.
(restic-backup-job-program): Generalize to restic-program.
(lower-restic-backup-job): New procedure implementing a standard way to
lower restic-backup-job records into lists.
(restic-guix): Drop procedure;
(restic-program): Implement general way to run restic commands, for
example to initialize repositories.
(restic-backup): New procedure returning a gexp for the entrypoint of
the backup job.
(restic-job-logfile): Rename to restic-backup-log-file.
(restic-backup-job-command): New procedure returning the restic command
file for the Shepherd timer as a gexped list.
(restic-backup-job-requirement): New procedure returning the Shepherd
timer Shepherd dependencies.
(restic-backup-job-modules): New procedure returning the Guile modules
that will be loaded in the Shepherd timer.
(restic-backup-job->shepherd-service): Use the new procedures and use
the trigger action from (shepherd service timer).
(restic-guix-wrapper-package): Drop procedure.
(restic-backup-service-profile): Drop procedure.
(restic-backup-service-activation): Drop procedure as now the Shepherd
takes care of creating timers log file directories.
(restic-backup-service-type): Drop profile and activation services extensions.

Change-Id: Ib2b5d74bebc51e35f1ae6e1aa32cedee0da59697
---
 gnu/services/backup.scm | 178 ++++++++++++++++------------------------
 1 file changed, 71 insertions(+), 107 deletions(-)

diff --git a/gnu/services/backup.scm b/gnu/services/backup.scm
index 6e066bd3d66..4fff815e168 100644
--- a/gnu/services/backup.scm
+++ b/gnu/services/backup.scm
@@ -47,16 +47,19 @@ (define-module (gnu services backup)
             restic-backup-job-verbose?
             restic-backup-job-extra-flags
 
+            lower-restic-backup-job
+
             restic-backup-configuration
             restic-backup-configuration?
             restic-backup-configuration-fields
             restic-backup-configuration-jobs
 
-            restic-backup-job-program
-            restic-backup-job->mcron-job
-            restic-guix
-            restic-guix-wrapper-package
-            restic-backup-service-profile
+            restic-program
+            restic-backup-job-log-file
+            restic-backup-job-command
+            restic-backup-job-requirement
+            restic-backup-job-modules
+            restic-backup-job->shepherd-service
             restic-backup-service-type))
 
 (define (gexp-or-string? value)
@@ -139,110 +142,104 @@ (define-configuration/no-serialization restic-backup-configuration
    (list-of-restic-backup-jobs '())
    "The list of backup jobs for the current system."))
 
-(define (restic-backup-job-program config)
+(define (lower-restic-backup-job config)
   (let ((restic
          (file-append (restic-backup-job-restic config) "/bin/restic"))
+        (name
+         (restic-backup-job-name config))
+        (files
+         (restic-backup-job-files config))
         (repository
          (restic-backup-job-repository config))
         (password-file
          (restic-backup-job-password-file config))
-        (files
-         (restic-backup-job-files config))
         (extra-flags
          (restic-backup-job-extra-flags config))
-        (verbose
+        (verbose?
          (if (restic-backup-job-verbose? config)
              '("--verbose")
              '())))
-    (program-file
-     "restic-backup-job.scm"
-     #~(begin
-         (use-modules (ice-9 popen)
-                      (ice-9 rdelim))
-         (setenv "RESTIC_PASSWORD"
-                 (with-input-from-file #$password-file read-line))
+    #~(list (list #$@files) #$restic #$repository #$password-file
+            (list #$@verbose?) (list #$@extra-flags))))
 
-         (execlp #$restic #$restic #$@verbose
-                 "-r" #$repository
-                 #$@extra-flags
-                 "backup" #$@files)))))
-
-(define (restic-guix jobs)
-  (program-file
-   "restic-guix"
-   #~(begin
-       (use-modules (ice-9 match)
-                    (srfi srfi-1))
+(define restic-program
+  #~(lambda (action action-args job-restic repository password-file verbose? extra-flags)
+      (use-modules (ice-9 format))
+      ;; This can be extended later, i.e. to have a
+      ;; centrally defined restic package.
+      ;; See https://issues.guix.gnu.org/71639
+      (define restic job-restic)
 
-       (define names '#$(map restic-backup-job-name jobs))
-       (define programs '#$(map restic-backup-job-program jobs))
+      (define command
+        `(,restic ,@verbose?
+          "-r" ,repository
+          ,@extra-flags
+          ,action ,@action-args))
 
-       (define (get-program name)
-         (define idx
-           (list-index (lambda (n) (string=? n name)) names))
-         (unless idx
-           (error (string-append "Unknown job name " name "\n\n"
-                                 "Possible job names are: "
-                                 (string-join names " "))))
-         (list-ref programs idx))
+      (setenv "RESTIC_PASSWORD_FILE" password-file)
 
-       (define (backup args)
-         (define name (third args))
-         (define program (get-program name))
-         (execlp program program))
+      (when (> (length verbose?) 0)
+        (format #t "Running~{ ~a~}~%" command))
 
-       (define (validate-args args)
-         (when (not (>= (length args) 3))
-           (error (string-append "Usage: " (basename (car args))
-                                 " backup NAME"))))
+      (apply execlp `(,restic ,@command))))
 
-       (define (main args)
-         (validate-args args)
-         (define action (second args))
-         (match action
-           ("backup"
-            (backup args))
-           (_
-            (error (string-append "Unknown action: " action)))))
+(define (restic-backup config)
+  (program-file
+   "restic-backup"
+   #~(let ((restic-exec
+            #$restic-program)
+           (job #$(lower-restic-backup-job config)))
 
-       (main (command-line)))))
+       (apply restic-exec `("backup" ,@job)))))
 
-(define (restic-job-log-file job)
+(define (restic-backup-job-log-file job)
   (let ((name (restic-backup-job-name job))
         (log-file (restic-backup-job-log-file job)))
     (if (maybe-value-set? log-file)
         log-file
         (string-append "/var/log/restic-backup/" name ".log"))))
 
+(define (restic-backup-job-command config)
+  ;; We go through bash, instead of executing
+  ;; restic-guix directly, because the login shell
+  ;; gives us the correct user environment that some
+  ;; backends require, such as rclone.
+  #~(list
+     (string-append #$bash-minimal "/bin/bash")
+     "-l" "-c"
+     (string-join (list #$(restic-backup config))
+                  " ")))
+
+(define (restic-backup-job-requirement requirement)
+  (append '(user-processes file-systems) requirement))
+
+(define (restic-backup-job-modules)
+ `((shepherd service timer)))
+
 (define (restic-backup-job->shepherd-service config)
   (let ((schedule (restic-backup-job-schedule config))
         (name (restic-backup-job-name config))
+        (files (restic-backup-job-files config))
         (user (restic-backup-job-user config))
         (group (restic-backup-job-group config))
         (max-duration (restic-backup-job-max-duration config))
         (wait-for-termination? (restic-backup-job-wait-for-termination? config))
-        (log-file (restic-job-log-file config))
-        (requirement (restic-backup-job-requirement config)))
+        (log-file (restic-backup-job-log-file config))
+        (requirement
+         (restic-backup-job-requirement
+          (restic-backup-job-requirement config))))
     (shepherd-service (provision `(,(string->symbol name)))
-                      (requirement
-                       `(user-processes file-systems ,@requirement))
+                      (requirement requirement)
                       (documentation
-                       "Run @code{restic} backed backups on a regular basis.")
-                      (modules '((shepherd service timer)))
+                       "Run restic backed backups on a regular basis.")
+                      (modules (restic-backup-job-modules))
                       (start
                        #~(make-timer-constructor
                           (if (string? #$schedule)
                               (cron-string->calendar-event #$schedule)
                               #$schedule)
                           (command
-                           (list
-                            ;; We go through bash, instead of executing
-                            ;; restic-guix directly, because the login shell
-                            ;; gives us the correct user environment that some
-                            ;; backends require, such as rclone.
-                            (string-append #+bash-minimal "/bin/bash")
-                            "-l" "-c"
-                            (string-append "restic-guix backup " #$name))
+                           #$(restic-backup-job-command config)
                            #:user #$user
                            #:group #$group
                            #:environment-variables
@@ -255,49 +252,16 @@ (define (restic-backup-job->shepherd-service config)
                                                 max-duration)))
                       (stop
                        #~(make-timer-destructor))
-                      (actions (list shepherd-trigger-action)))))
-
-(define (restic-guix-wrapper-package jobs)
-  (package
-    (name "restic-backup-service-wrapper")
-    (version "0.0.0")
-    (source (restic-guix jobs))
-    (build-system copy-build-system)
-    (arguments
-     (list #:install-plan #~'(("./" "/bin"))))
-    (home-page "https://restic.net")
-    (synopsis
-     "Easily interact from the CLI with Guix configured backups")
-    (description
-     "This package provides a simple wrapper around @code{restic}, handled
-by the @code{restic-backup-service-type}.  It allows for easily interacting
-with Guix configured backup jobs, for example for manually triggering a backup
-without waiting for the scheduled job to run.")
-    (license license:gpl3+)))
-
-(define restic-backup-service-profile
-  (lambda (config)
-    (define jobs (restic-backup-configuration-jobs config))
-    (if (> (length jobs) 0)
-        (list
-         (restic-guix-wrapper-package jobs))
-        '())))
-
-(define (restic-backup-activation config)
-  #~(for-each
-     (lambda (log-file)
-       (mkdir-p (dirname log-file)))
-     (list #$@(map restic-job-log-file
-                   (restic-backup-configuration-jobs config)))))
+                      (actions (list (shepherd-action
+                                      (name 'trigger)
+                                      (documentation "Manually trigger a backup,
+without waiting for the scheduled time.")
+                                      (procedure #~trigger-timer)))))))
 
 (define restic-backup-service-type
   (service-type (name 'restic-backup)
                 (extensions
                  (list
-                  (service-extension activation-service-type
-                                     restic-backup-activation)
-                  (service-extension profile-service-type
-                                     restic-backup-service-profile)
                   (service-extension shepherd-root-service-type
                                      (lambda (config)
                                        (map restic-backup-job->shepherd-service

base-commit: 1710c0941db517453ac2b88c0e854e8348172603
-- 
2.49.0