From: Nikita Karetnikov <nikita@karetnikov.org>
To: "Ludovic Courtès" <ludo@gnu.org>
Cc: guix-devel@gnu.org
Subject: [PATCH] guix package: Add '--delete-generations'.
Date: Sun, 22 Sep 2013 23:19:14 +0400 [thread overview]
Message-ID: <87li2oslzh.fsf_-_@karetnikov.org> (raw)
In-Reply-To: <87hae81uvo.fsf@gnu.org> ("Ludovic Courtès"'s message of "Thu, 29 Aug 2013 15:36:43 +0200")
[-- Attachment #1.1: Type: text/plain, Size: 266 bytes --]
Can I push this patch to ‘master’? Do you see any problems?
I had noticed that ‘--roll-back’ doesn’t output anything with
‘--dry-run’, so I implemented ‘--delete-generations’ similarly. Maybe
it would be better to print something. WDYT?
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: 0001-guix-package-Add-delete-generations.patch --]
[-- Type: text/x-diff, Size: 20291 bytes --]
From ede983c90bd4cdece708820e1d52a2d1894a51c8 Mon Sep 17 00:00:00 2001
From: Nikita Karetnikov <nikita@karetnikov.org>
Date: Sun, 22 Sep 2013 18:50:06 +0000
Subject: [PATCH] guix package: Add '--delete-generations'.
* guix/scripts/package.scm (link-to-empty-environment)
(switch-to-previous-generation): New functions.
(roll-back): Replace internal functions with the new ones.
(show-help): Add '--delete-generations'.
(%options): Likewise.
(guix-package): Add 'apply-to-generations'.
(guix-package)[process-actions]: Add support for '--delete-generations'.
(guix-package)[process-query]: Replace 'cond' with 'apply-to-generations'.
* tests/guix-package.sh: Test '--delete-generations'.
* doc/guix.texi (Invoking guix-package): Document '--delete-generations'.
---
doc/guix.texi | 7 ++
guix/scripts/package.scm | 271 ++++++++++++++++++++++++++++------------------
tests/guix-package.sh | 7 ++
3 files changed, 178 insertions(+), 107 deletions(-)
diff --git a/doc/guix.texi b/doc/guix.texi
index fdddcc5..3d61630 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -639,6 +639,13 @@ or months by passing an integer along with the first letter of the
duration, e.g., @code{--list-generations=20d}.
@end itemize
+@item --delete-generations[=@var{pattern}]
+@itemx -d [@var{pattern}]
+Delete generations.
+
+When @var{pattern} is specified, delete only the matching generations.
+This command accepts the same patterns as @option{--list-generations}.
+
@item --profile=@var{profile}
@itemx -p @var{profile}
Use @var{profile} instead of the user's default profile.
diff --git a/guix/scripts/package.scm b/guix/scripts/package.scm
index c0cedcd..c72b56e 100644
--- a/guix/scripts/package.scm
+++ b/guix/scripts/package.scm
@@ -214,6 +214,25 @@ all of PACKAGES, a list of name/version/output/path/deps tuples."
(compose string->number (cut match:substring <> 1)))
0))
+(define (link-to-empty-environment generation)
+ "Link GENERATION, a string, to the empty environment."
+ (let* ((drv (profile-derivation (%store) '()))
+ (prof (derivation->output-path drv "out")))
+ (when (not (build-derivations (%store) (list drv)))
+ (leave (_ "failed to build the empty profile~%")))
+
+ (switch-symlinks generation prof)))
+
+(define (switch-to-previous-generation profile)
+ "Atomically switch PROFILE to the previous generation."
+ (let* ((number (generation-number profile))
+ (previous-number (previous-generation-number profile number))
+ (previous-generation (format #f "~a-~a-link"
+ profile previous-number)))
+ (format #t (_ "switching from generation ~a to ~a~%")
+ number previous-number)
+ (switch-symlinks profile previous-generation)))
+
(define (roll-back profile)
"Roll back to the previous generation of PROFILE."
(let* ((number (generation-number profile))
@@ -222,28 +241,18 @@ all of PACKAGES, a list of name/version/output/path/deps tuples."
profile previous-number))
(manifest (string-append previous-generation "/manifest")))
- (define (switch-link)
- ;; Atomically switch PROFILE to the previous generation.
- (format #t (_ "switching from generation ~a to ~a~%")
- number previous-number)
- (switch-symlinks profile previous-generation))
-
- (cond ((not (file-exists? profile)) ; invalid profile
+ (cond ((not (file-exists? profile)) ; invalid profile
(leave (_ "profile `~a' does not exist~%")
profile))
- ((zero? number) ; empty profile
+ ((zero? number) ; empty profile
(format (current-error-port)
(_ "nothing to do: already at the empty profile~%")))
- ((or (zero? previous-number) ; going to emptiness
+ ((or (zero? previous-number) ; going to emptiness
(not (file-exists? previous-generation)))
- (let* ((drv (profile-derivation (%store) '()))
- (prof (derivation->output-path drv "out")))
- (when (not (build-derivations (%store) (list drv)))
- (leave (_ "failed to build the empty profile~%")))
-
- (switch-symlinks previous-generation prof)
- (switch-link)))
- (else (switch-link))))) ; anything else
+ (begin (link-to-empty-environment previous-generation)
+ (switch-to-previous-generation profile)))
+ (else
+ (switch-to-previous-generation profile))))) ; anything else
(define (generation-time profile number)
"Return the creation time of a generation in the UTC format."
@@ -511,6 +520,9 @@ Install, remove, or upgrade PACKAGES in a single transaction.\n"))
(display (_ "
-l, --list-generations[=PATTERN]
list generations matching PATTERN"))
+ (display (_ "
+ -d, --delete-generations[=PATTERN]
+ delete generations matching PATTERN"))
(newline)
(display (_ "
-p, --profile=PROFILE use PROFILE instead of the user's default profile"))
@@ -574,6 +586,10 @@ Install, remove, or upgrade PACKAGES in a single transaction.\n"))
(lambda (opt name arg result)
(cons `(query list-generations ,(or arg ""))
result)))
+ (option '(#\d "delete-generations") #f #t
+ (lambda (opt name arg result)
+ (alist-cons 'delete-generations (or arg "")
+ result)))
(option '("search-paths") #f #f
(lambda (opt name arg result)
(cons `(query search-paths) result)))
@@ -742,6 +758,20 @@ more information.~%"))
%profile-directory (or (getenv "USER") (getuid)))
(rtfm))))
+ (define (apply-to-generations function profile pattern)
+ (cond ((not (file-exists? profile)) ; XXX: race condition
+ (leave (_ "profile '~a' does not exist~%")
+ profile))
+ ((string-null? pattern)
+ (for-each function
+ (generation-numbers profile)))
+ ((matching-generations pattern profile)
+ =>
+ (cut for-each function <>))
+ (else
+ (leave (_ "invalid syntax: ~a~%")
+ pattern))))
+
(define (process-actions opts)
;; Process any install/remove/upgrade action from OPTS.
@@ -824,85 +854,123 @@ more information.~%"))
install))))
(_ #f)))
+ (define (delete-generation number)
+ (define (display-and-delete generation)
+ (begin (format #t "deleting ~a~%" generation)
+ (delete-file generation)))
+
+ (define (current-generation? profile generation)
+ (string=? (readlink profile) generation))
+
+ (let* ((generation (format #f "~a-~a-link" profile number))
+ (previous-number (previous-generation-number profile number))
+ (previous-generation (format #f "~a-~a-link"
+ profile previous-number)))
+ (cond ((zero? number)) ; do not delete generation 0
+ ((and (current-generation? profile generation)
+ (not (file-exists? previous-generation)))
+ (begin (link-to-empty-environment previous-generation)
+ (switch-to-previous-generation profile)
+ (display-and-delete generation)))
+ ((current-generation? profile generation)
+ (begin (roll-back profile)
+ (display-and-delete generation)))
+ (else
+ (display-and-delete generation)))))
+
;; First roll back if asked to.
- (if (and (assoc-ref opts 'roll-back?) (not dry-run?))
- (begin
- (roll-back profile)
- (process-actions (alist-delete 'roll-back? opts)))
- (let* ((installed (manifest-packages (profile-manifest profile)))
- (upgrade-regexps (filter-map (match-lambda
- (('upgrade . regexp)
- (make-regexp (or regexp "")))
- (_ #f))
- opts))
- (upgrade (if (null? upgrade-regexps)
- '()
- (let ((newest (find-newest-available-packages)))
- (filter-map (match-lambda
- ((name version output path _)
- (and (any (cut regexp-exec <> name)
- upgrade-regexps)
- (upgradeable? name version path)
- (find-package name
- (or output "out"))))
- (_ #f))
- installed))))
- (install (append
- upgrade
- (filter-map (match-lambda
- (('install . (? package? p))
- (package->tuple p))
- (('install . (? store-path?))
- #f)
- (('install . package)
- (find-package package))
- (_ #f))
- opts)))
- (drv (filter-map (match-lambda
- ((name version sub-drv
- (? package? package)
- (deps ...))
- (check-package-freshness package)
- (package-derivation (%store) package))
- (_ #f))
- install))
- (install* (append
- (filter-map (match-lambda
- (('install . (? package? p))
- #f)
- (('install . (? store-path? path))
- (let-values (((name version)
- (package-name->name+version
- (store-path-package-name
- path))))
- `(,name ,version #f ,path ())))
+ (cond ((and (assoc-ref opts 'roll-back?) (not dry-run?))
+ (begin
+ (roll-back profile)
+ (process-actions (alist-delete 'roll-back? opts))))
+ ((and (assoc-ref opts 'delete-generations)
+ (not dry-run?))
+ (filter-map (match-lambda
+ (('delete-generations . pattern)
+ (begin (apply-to-generations delete-generation
+ profile pattern)
+ (process-actions
+ (alist-delete 'delete-generations opts))))
+ (_ #f))
+ opts))
+ (else
+ (let* ((installed (manifest-packages (profile-manifest profile)))
+ (upgrade-regexps (filter-map (match-lambda
+ (('upgrade . regexp)
+ (make-regexp (or regexp "")))
+ (_ #f))
+ opts))
+ (upgrade (if (null? upgrade-regexps)
+ '()
+ (let ((newest (find-newest-available-packages)))
+ (filter-map
+ (match-lambda
+ ((name version output path _)
+ (and (any (cut regexp-exec <> name)
+ upgrade-regexps)
+ (upgradeable? name version path)
+ (find-package name
+ (or output "out"))))
+ (_ #f))
+ installed))))
+ (install (append
+ upgrade
+ (filter-map (match-lambda
+ (('install . (? package? p))
+ (package->tuple p))
+ (('install . (? store-path?))
+ #f)
+ (('install . package)
+ (find-package package))
+ (_ #f))
+ opts)))
+ (drv (filter-map (match-lambda
+ ((name version sub-drv
+ (? package? package)
+ (deps ...))
+ (check-package-freshness package)
+ (package-derivation (%store) package))
+ (_ #f))
+ install))
+ (install*
+ (append
+ (filter-map (match-lambda
+ (('install . (? package? p))
+ #f)
+ (('install . (? store-path? path))
+ (let-values (((name version)
+ (package-name->name+version
+ (store-path-package-name
+ path))))
+ `(,name ,version #f ,path ())))
+ (_ #f))
+ opts)
+ (map (lambda (tuple drv)
+ (match tuple
+ ((name version sub-drv _ (deps ...))
+ (let ((output-path
+ (derivation->output-path
+ drv sub-drv)))
+ `(,name ,version ,sub-drv ,output-path
+ ,(canonicalize-deps deps))))))
+ install drv)))
+ (remove (filter-map (match-lambda
+ (('remove . package)
+ package)
(_ #f))
- opts)
- (map (lambda (tuple drv)
- (match tuple
- ((name version sub-drv _ (deps ...))
- (let ((output-path
- (derivation->output-path
- drv sub-drv)))
- `(,name ,version ,sub-drv ,output-path
- ,(canonicalize-deps deps))))))
- install drv)))
- (remove (filter-map (match-lambda
- (('remove . package)
- package)
- (_ #f))
- opts))
- (remove* (filter-map (cut assoc <> installed) remove))
- (packages (append install*
- (fold (lambda (package result)
- (match package
- ((name _ out _ ...)
- (filter (negate
- (cut same-package? <>
- name out))
- result))))
- (fold alist-delete installed remove)
- install*))))
+ opts))
+ (remove* (filter-map (cut assoc <> installed) remove))
+ (packages
+ (append install*
+ (fold (lambda (package result)
+ (match package
+ ((name _ out _ ...)
+ (filter (negate
+ (cut same-package? <>
+ name out))
+ result))))
+ (fold alist-delete installed remove)
+ install*))))
(when (equal? profile %current-profile)
(ensure-default-profile))
@@ -946,7 +1014,7 @@ more information.~%"))
count)
count)
(display-search-paths packages
- profile))))))))))
+ profile)))))))))))
(define (process-query opts)
;; Process any query specified by OPTS. Return #t when a query was
@@ -970,18 +1038,7 @@ more information.~%"))
(format #f "~a-~a-link" profile number))))
(newline)))
- (cond ((not (file-exists? profile)) ; XXX: race condition
- (leave (_ "profile '~a' does not exist~%")
- profile))
- ((string-null? pattern)
- (for-each list-generation
- (generation-numbers profile)))
- ((matching-generations pattern profile)
- =>
- (cut for-each list-generation <>))
- (else
- (leave (_ "invalid syntax: ~a~%")
- pattern)))
+ (apply-to-generations list-generation profile pattern)
#t)
(('list-installed regexp)
diff --git a/tests/guix-package.sh b/tests/guix-package.sh
index b09a9c0..65bc94c 100644
--- a/tests/guix-package.sh
+++ b/tests/guix-package.sh
@@ -132,6 +132,13 @@ then
# Make sure LIBRARY_PATH gets listed by `--search-paths'.
guix package --bootstrap -p "$profile" -i guile-bootstrap -i gcc-bootstrap
guix package --search-paths -p "$profile" | grep LIBRARY_PATH
+
+ # Delete the third generation and check that it was actually deleted.
+ guix package -p "$profile" --delete-generations=3
+ test -z "`guix package -p "$profile" -l 3`"
+
+ # Do not output anything when such a generation does not exist.
+ test -z "`guix package -p "$profile" --delete-generations=42`"
fi
# Make sure the `:' syntax works.
--
1.7.9.5
[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]
next prev parent reply other threads:[~2013-09-22 19:14 UTC|newest]
Thread overview: 132+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-08-29 12:34 Goals for 0.4 Ludovic Courtès
2013-08-29 13:16 ` Nikita Karetnikov
2013-08-29 13:36 ` Ludovic Courtès
2013-08-30 17:55 ` Nikita Karetnikov
2013-08-30 18:31 ` Ludovic Courtès
2013-08-31 16:40 ` Nikita Karetnikov
2013-08-31 18:05 ` Ludovic Courtès
2013-08-31 20:34 ` Jose E. Marchesi
2013-08-31 21:07 ` Ludovic Courtès
2013-09-01 23:16 ` New ‘--list-generations’ and ‘--delete-generations’ options (was: Goals for 0.4) Nikita Karetnikov
2013-09-02 9:08 ` New ‘--list-generations’ and ‘--delete-generations’ options Ludovic Courtès
2013-09-05 1:30 ` Nikita Karetnikov
2013-09-05 20:00 ` Ludovic Courtès
2013-09-05 21:14 ` Nikita Karetnikov
2013-09-07 19:34 ` Ludovic Courtès
2013-09-08 10:59 ` Nikita Karetnikov
2013-09-08 20:22 ` Ludovic Courtès
2013-09-09 9:17 ` Nikita Karetnikov
2013-09-09 16:55 ` Ludovic Courtès
2013-09-11 5:16 ` Nikita Karetnikov
2013-09-11 21:25 ` Ludovic Courtès
2013-09-12 9:17 ` Nikita Karetnikov
2013-09-12 12:26 ` Ludovic Courtès
2013-09-13 14:44 ` Nikita Karetnikov
2013-09-13 21:29 ` Ludovic Courtès
2013-09-16 11:12 ` Nikita Karetnikov
2013-09-16 12:16 ` Ludovic Courtès
2013-09-18 0:43 ` PRELIMINARY: [PATCH] guix package: Add '--list-generations' Nikita Karetnikov
2013-09-18 17:35 ` Nikita Karetnikov
2013-09-18 21:32 ` Ludovic Courtès
2013-09-19 0:49 ` Nikita Karetnikov
2013-09-19 9:39 ` Ludovic Courtès
2013-09-19 11:48 ` Nikita Karetnikov
2013-09-19 12:13 ` Ludovic Courtès
2013-09-21 20:39 ` Ludovic Courtès
2013-09-18 21:35 ` PRELIMINARY: " Ludovic Courtès
2013-09-22 19:19 ` Nikita Karetnikov [this message]
2013-09-22 20:15 ` Generation 0 (was: [PATCH] guix package: Add '--delete-generations'.) Nikita Karetnikov
2013-09-22 21:15 ` Generation 0 Ludovic Courtès
2013-09-23 10:14 ` Nikita Karetnikov
2013-09-23 15:42 ` Ludovic Courtès
2013-09-24 0:54 ` Nikita Karetnikov
2013-09-24 5:56 ` [PATCH] guix package: Show which generation is the current one. (was: Generation 0) Nikita Karetnikov
2013-09-24 12:45 ` [PATCH] guix package: Show which generation is the current one Ludovic Courtès
2013-09-24 13:55 ` Nikita Karetnikov
2013-09-24 14:16 ` Ludovic Courtès
2013-09-25 2:10 ` Nikita Karetnikov
2013-09-25 12:51 ` Ludovic Courtès
2013-09-24 12:43 ` Generation 0 Ludovic Courtès
2013-09-24 22:29 ` Nikita Karetnikov
2013-09-25 12:50 ` Ludovic Courtès
2013-09-25 18:07 ` Nikita Karetnikov
2013-09-25 19:24 ` Ludovic Courtès
2013-09-26 2:18 ` Nikita Karetnikov
2013-09-26 9:44 ` Ludovic Courtès
2013-09-22 20:55 ` [PATCH] guix package: Add '--delete-generations' Ludovic Courtès
2013-09-23 10:11 ` Nikita Karetnikov
2013-09-23 15:41 ` Ludovic Courtès
2013-09-24 7:21 ` Nikita Karetnikov
2013-09-24 12:50 ` Ludovic Courtès
2013-09-24 13:57 ` Nikita Karetnikov
2013-09-25 4:21 ` Nikita Karetnikov
2013-09-25 13:05 ` Ludovic Courtès
2013-09-26 2:47 ` Nikita Karetnikov
2013-09-26 9:49 ` Ludovic Courtès
2013-09-27 19:04 ` Ludovic Courtès
2013-09-03 19:21 ` MIPS64/N64 support (was: Goals for 0.4) Nikita Karetnikov
2013-09-03 20:45 ` MIPS64/N64 support Ludovic Courtès
2013-09-04 0:35 ` Nikita Karetnikov
2013-09-04 12:18 ` Ludovic Courtès
2013-09-06 8:35 ` Nikita Karetnikov
2013-09-06 9:46 ` Ludovic Courtès
2013-09-07 2:45 ` Nikita Karetnikov
2013-09-07 12:57 ` Ludovic Courtès
2013-09-08 14:21 ` Nikita Karetnikov
2013-09-08 19:54 ` Ludovic Courtès
2013-09-09 5:38 ` Nikita Karetnikov
2013-09-09 16:47 ` Ludovic Courtès
2013-09-27 2:16 ` Nikita Karetnikov
2013-09-27 19:00 ` Ludovic Courtès
2013-09-29 13:27 ` Nikita Karetnikov
2013-09-29 13:31 ` Ludovic Courtès
2013-09-29 23:18 ` Nikita Karetnikov
2013-09-30 11:32 ` Nikita Karetnikov
2013-09-30 16:26 ` Ludovic Courtès
2013-09-30 21:51 ` Nikita Karetnikov
2013-10-01 7:09 ` Lluís Batlle i Rossell
2013-10-01 7:48 ` Nikita Karetnikov
2013-10-01 8:03 ` Lluís Batlle i Rossell
2013-10-01 8:55 ` Nikita Karetnikov
2013-10-01 8:59 ` Nikita Karetnikov
2013-10-01 9:30 ` Lluís Batlle i Rossell
2013-10-01 10:06 ` Nikita Karetnikov
2013-10-01 10:04 ` Lluís Batlle i Rossell
2013-10-01 11:25 ` Ludovic Courtès
2013-10-01 11:56 ` Lluís Batlle i Rossell
2013-10-07 18:47 ` Mark H Weaver
2013-10-07 19:39 ` Ludovic Courtès
2013-10-08 23:03 ` Mark H Weaver
2013-10-09 6:53 ` Mark H Weaver
2013-10-09 10:42 ` Ludovic Courtès
2013-10-09 10:39 ` Ludovic Courtès
2013-10-10 4:08 ` Mark H Weaver
2013-09-30 16:09 ` Ludovic Courtès
2013-08-29 15:49 ` Goals for 0.4 Amirouche Boubekki
2013-08-29 20:04 ` Ludovic Courtès
2013-08-30 16:09 ` Cyprien Nicolas
2013-08-30 17:40 ` Amirouche Boubekki
2013-08-30 19:31 ` Overlays Ludovic Courtès
2013-08-30 20:42 ` Overlays Nikita Karetnikov
2013-08-30 21:21 ` Overlays Ludovic Courtès
2013-08-31 10:56 ` Overlays Amirouche Boubekki
2013-08-31 15:57 ` Overlays Ludovic Courtès
2013-08-29 20:42 ` Goals for 0.4 Andreas Enge
2013-08-29 21:32 ` Ludovic Courtès
2013-09-25 8:43 ` Andreas Enge
2013-09-25 13:13 ` Ludovic Courtès
2013-09-26 11:35 ` Andreas Enge
2013-09-28 13:25 ` Ludovic Courtès
2013-09-29 21:29 ` Alex Sassmannshausen
2013-09-02 17:33 ` Cyril Roelandt
2013-09-02 19:38 ` Ludovic Courtès
2013-09-02 19:40 ` Cyril Roelandt
2013-09-02 21:35 ` Ludovic Courtès
2013-09-06 9:19 ` ‘--no-substitutes’ is ignored on i686 (was: Goals for 0.4) Nikita Karetnikov
2013-09-06 9:59 ` ‘--no-substitutes’ is ignored on i686 Ludovic Courtès
2013-09-07 8:43 ` Nikita Karetnikov
2013-09-07 13:00 ` Ludovic Courtès
2013-09-08 11:53 ` Nikita Karetnikov
2013-09-08 11:51 ` Cyril Roelandt
2013-09-08 13:22 ` Nikita Karetnikov
2013-09-24 21:59 ` Goals for 0.4 Ludovic Courtès
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://guix.gnu.org/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87li2oslzh.fsf_-_@karetnikov.org \
--to=nikita@karetnikov.org \
--cc=guix-devel@gnu.org \
--cc=ludo@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/guix.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).