From e412a2d5a9014b58439f0580abbf8c0248271bb1 Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Thu, 17 Mar 2016 13:11:48 -0600 Subject: [PATCH] raco pkg {install,update,...}: add --dry-run --- pkgs/racket-doc/pkg/scribblings/lib.scrbl | 25 ++++--- pkgs/racket-doc/pkg/scribblings/pkg.scrbl | 18 +++-- pkgs/racket-test/tests/pkg/tests-install.rkt | 37 +++++++--- pkgs/racket-test/tests/pkg/tests-migrate.rkt | 2 + pkgs/racket-test/tests/pkg/tests-remove.rkt | 8 ++- pkgs/racket-test/tests/pkg/tests-update.rkt | 20 +++++- racket/collects/pkg/lib.rkt | 12 ++-- racket/collects/pkg/main.rkt | 13 +++- racket/collects/pkg/private/git.rkt | 7 +- racket/collects/pkg/private/install.rkt | 75 ++++++++++++-------- racket/collects/pkg/private/migrate.rkt | 6 +- racket/collects/pkg/private/print.rkt | 6 ++ racket/collects/pkg/private/remove.rkt | 68 ++++++++++-------- 13 files changed, 206 insertions(+), 91 deletions(-) diff --git a/pkgs/racket-doc/pkg/scribblings/lib.scrbl b/pkgs/racket-doc/pkg/scribblings/lib.scrbl index f582239550..d027899dc4 100644 --- a/pkgs/racket-doc/pkg/scribblings/lib.scrbl +++ b/pkgs/racket-doc/pkg/scribblings/lib.scrbl @@ -265,7 +265,8 @@ is true, error messages may suggest specific command-line flags for [#:force-strip? force-string? boolean? #f] [#:multi-clone-mode multi-clone-mode (or/c 'fail 'force 'convert 'ask) 'fail] [#:pull-mode pull-mode (or/c 'ff-only 'try 'rebase) 'ff-only] - [#:link-dirs? link-dirs? boolean? #f]) + [#:link-dirs? link-dirs? boolean? #f] + [#:dry-run? dry-run? boolean? #f]) (or/c 'skip #f (listof (or/c path-string? @@ -299,7 +300,8 @@ The package lock must be held; see @racket[with-pkg-lock]. @history[#:changed "6.1.1.5" @elem{Added the @racket[#:multi-clone-mode] and @racket[#:infer-clone-from-dir?] arguments.} #:changed "6.1.1.6" @elem{Added the @racket[#:use-trash?] argument.} - #:changed "6.1.1.8" @elem{Added the @racket[#:pull-mode] argument.}]} + #:changed "6.1.1.8" @elem{Added the @racket[#:pull-mode] argument.} + #:changed "6.4.0.14" @elem{Added the @racket[#:dry-run] argument.}]} @defproc[(pkg-update [sources (listof (or/c string? pkg-desc?))] @@ -322,7 +324,8 @@ The package lock must be held; see @racket[with-pkg-lock]. [#:multi-clone-mode multi-clone-mode (or/c 'fail 'force 'convert 'ask) 'fail] [#:pull-mode pull-mode (or/c 'ff-only 'try 'rebase) 'ff-only] [#:link-dirs? link-dirs? boolean? #f] - [#:infer-clone-from-dir? infer-clone-from-dir? boolean? #f]) + [#:infer-clone-from-dir? infer-clone-from-dir? boolean? #f] + [#:dry-run? dry-run? boolean? #f]) (or/c 'skip #f (listof (or/c path-string? @@ -357,7 +360,8 @@ The package lock must be held; see @racket[with-pkg-lock]. @history[#:changed "6.1.1.5" @elem{Added the @racket[#:multi-clone-mode] and @racket[#:infer-clone-from-dir?] arguments.} #:changed "6.1.1.6" @elem{Added the @racket[#:use-trash?] argument.} - #:changed "6.1.1.8" @elem{Added the @racket[#:skip-uninstalled?] and @racket[#:pull-mode] arguments.}]} + #:changed "6.1.1.8" @elem{Added the @racket[#:skip-uninstalled?] and @racket[#:pull-mode] arguments.} + #:changed "6.4.0.14" @elem{Added the @racket[#:dry-run] argument.}]} @defproc[(pkg-remove [names (listof string?)] @@ -366,7 +370,8 @@ The package lock must be held; see @racket[with-pkg-lock]. [#:force? force? boolean? #f] [#:quiet? quiet? boolean? #f] [#:use-trash? boolean? use-trash? #f] - [#:from-command-line? from-command-line? boolean? #f]) + [#:from-command-line? from-command-line? boolean? #f] + [#:dry-run? dry-run? boolean? #f]) (or/c 'skip #f (listof (or/c path-string? @@ -381,7 +386,8 @@ specific command-line flags for @command-ref{remove}. The package lock must be held; see @racket[with-pkg-lock]. -@history[#:changed "6.1.1.6" @elem{Added the @racket[#:use-trash?] argument.}]} +@history[#:changed "6.1.1.6" @elem{Added the @racket[#:use-trash?] argument.} + #:changed "6.4.0.14" @elem{Added the @racket[#:dry-run] argument.}]} @defproc[(pkg-new [name path-string?]) @@ -419,7 +425,8 @@ The package lock must be held to allow reads; see [#:quiet? quiet? boolean? #f] [#:from-command-line? from-command-line? boolean? #f] [#:strip strip (or/c #f 'source 'binary 'binary-lib) #f] - [#:force-strip? force-string? boolean? #f]) + [#:force-strip? force-string? boolean? #f] + [#:dry-run? dry-run? boolean? #f]) (or/c 'skip #f (listof (or/c path-string? @@ -431,7 +438,9 @@ Implements @racket[pkg-migrate-command]. The result is the same as for If @racket[from-command-line?] is true, error messages may suggest specific command-line flags for @command-ref{migrate}. -The package lock must be held; see @racket[with-pkg-lock].} +The package lock must be held; see @racket[with-pkg-lock]. + +@history[#:changed "6.4.0.14" @elem{Added the @racket[#:dry-run] argument.}]} @defproc[(pkg-catalog-show [names (listof string?)] diff --git a/pkgs/racket-doc/pkg/scribblings/pkg.scrbl b/pkgs/racket-doc/pkg/scribblings/pkg.scrbl index 82db0b2af1..058ac1b72b 100644 --- a/pkgs/racket-doc/pkg/scribblings/pkg.scrbl +++ b/pkgs/racket-doc/pkg/scribblings/pkg.scrbl @@ -596,6 +596,9 @@ sub-commands. ]} + @item{@DFlag{dry-run} --- Prevents changes to the current installation. All installation and update work is + staged and checked, but the final installation step is skipped.} + @item{@DFlag{no-setup} --- Does not run @exec{raco setup} after installation. This behavior is also the case if the environment variable @envvar{PLT_PKG_NOSETUP} is set to any non-empty value.} @@ -613,7 +616,8 @@ sub-commands. @DFlag{multi-clone} flags.} #:changed "6.1.1.6" @elem{Added the @DFlag{no-trash} flag, and changed the @DFlag{deps} default to depend only on interactive mode.} - #:changed "6.1.1.8" @elem{Added the @DFlag{pull} flag.}]} + #:changed "6.1.1.8" @elem{Added the @DFlag{pull} flag.} + #:changed "6.4.0.14" @elem{Added the @DFlag{dry-run} flag.}]} @subcommand{@command/toc{update} @nonterm{option} ... @nonterm{pkg-source} ... @@ -723,6 +727,7 @@ the given @nonterm{pkg-source}s. @item{@DFlag{pull} @nonterm{mode} --- Same as for @command-ref{install}} + @item{@DFlag{dry-run} --- Same as for @command-ref{install}.} @item{@DFlag{no-setup} --- Same as for @command-ref{install}.} @item{@DFlag{jobs} @nonterm{n} or @Flag{j} @nonterm{n} --- Same as for @command-ref{install}.} @item{@DFlag{batch} --- Same as for @command-ref{install}.} @@ -735,7 +740,8 @@ the given @nonterm{pkg-source}s. when no arguments are provided.} #:changed "6.1.1.6" @elem{Added the @DFlag{no-trash} flag, and changed the @DFlag{deps} default to depend only on interactive mode.} - #:changed "6.1.1.8" @elem{Added the @DFlag{skip-uninstalled} and @DFlag{pull} flags.}]} + #:changed "6.1.1.8" @elem{Added the @DFlag{skip-uninstalled} and @DFlag{pull} flags.}] + #:changed "6.4.0.14" @elem{Added the @DFlag{dry-run} flag.}} @subcommand{@command/toc{remove} @nonterm{option} ... @nonterm{pkg} ... --- Attempts to remove the given packages. By default, if a package is the dependency @@ -761,6 +767,7 @@ the given @nonterm{pkg}s. @item{@Flag{i} or @DFlag{installation} --- Shorthand for @exec{--scope installation}.} @item{@Flag{u} or @DFlag{user} --- Shorthand for @exec{--scope user}.} @item{@DFlag{scope-dir} @nonterm{dir} --- Selects @nonterm{dir} as the @tech{package scope}, the same as for @command-ref{install}.} + @item{@DFlag{dry-run} --- Same as for @command-ref{install}.} @item{@DFlag{no-setup} --- Same as for @command-ref{install}.} @item{@DFlag{jobs} @nonterm{n} or @Flag{j} @nonterm{n} --- Same as for @command-ref{install}.} @item{@DFlag{batch} --- Same as for @command-ref{install}.} @@ -768,7 +775,8 @@ the given @nonterm{pkg}s. ] @history[#:changed "6.1.1.5" @elem{Added the @DFlag{batch} flag.} - #:changed "6.1.1.6" @elem{Added the @DFlag{no-trash} flag.}]} + #:changed "6.1.1.6" @elem{Added the @DFlag{no-trash} flag.} + #:changed "6.4.0.14" @elem{Added the @DFlag{dry-run} flag.}]} @subcommand{@command/toc{new} @nonterm{pkg} --- @@ -848,10 +856,12 @@ package is created. @item{@DFlag{ignore-checksums} --- Same as for @command-ref{install}.} @item{@DFlag{strict-doc-conflicts} --- Same as for @command-ref{install}.} @item{@DFlag{no-cache} --- Same as for @command-ref{install}.} + @item{@DFlag{dry-run} --- Same as for @command-ref{install}.} @item{@DFlag{no-setup} --- Same as for @command-ref{install}.} @item{@DFlag{jobs} @nonterm{n} or @Flag{j} @nonterm{n} --- Same as for @command-ref{install}.} ] -} + +@history[#:changed "6.4.0.14" @elem{Added the @DFlag{dry-run} flag.}]} @subcommand{@command/toc{create} @nonterm{option} ... @nonterm{directory-or-package} --- Bundles a package into an archive. Bundling diff --git a/pkgs/racket-test/tests/pkg/tests-install.rkt b/pkgs/racket-test/tests/pkg/tests-install.rkt index 68f70a048d..c2cff03092 100644 --- a/pkgs/racket-test/tests/pkg/tests-install.rkt +++ b/pkgs/racket-test/tests/pkg/tests-install.rkt @@ -22,18 +22,35 @@ (shelly-begin (initialize-catalogs) + (define-syntax-rule (shelly-install-dry-run what src) + (shelly-case + (format "Test dry-run installation of ~a" what) + $ "racket -e '(require pkg-test1)'" =exit> 1 + $ (~a "raco pkg install --dry-run " src) + $ "racket -e '(require pkg-test1)'" =exit> 1)) + + (define-syntax-rule (shelly-install/d what src) + (begin + (shelly-install-dry-run what src) + (shelly-install what src))) + + (define-syntax-rule (shelly-install*/d what srcs pkgs) + (begin + (shelly-install-dry-run what srcs) + (shelly-install* what srcs pkgs))) + (shelly-case "raco pkg install tests" - (shelly-install "local package (tgz)" "test-pkgs/pkg-test1.tgz") - (shelly-install "local package (zip)" "test-pkgs/pkg-test1.zip") - (shelly-install "local package (file://zip)" (url->string (path->url (path->complete-path "test-pkgs/pkg-test1.zip")))) - (shelly-install "local package (plt)" "test-pkgs/pkg-test1.plt") - (shelly-install* "local package (zip, compiled)" "test-pkgs/pkg-test1b.zip" "pkg-test1b") - (shelly-install* "local package (zip, single-collection)" - "test-pkgs/pkg-test1.zip test-pkgs/pkg-test3.zip" - "pkg-test1 pkg-test3") - (shelly-install "local package (dir)" (url->string (path->url (path->complete-path "test-pkgs/pkg-test1")))) - (shelly-install "local package (file://dir)" (url->string (path->url (path->complete-path "test-pkgs/pkg-test1")))) + (shelly-install/d "local package (tgz)" "test-pkgs/pkg-test1.tgz") + (shelly-install/d "local package (zip)" "test-pkgs/pkg-test1.zip") + (shelly-install/d "local package (file://zip)" (url->string (path->url (path->complete-path "test-pkgs/pkg-test1.zip")))) + (shelly-install/d "local package (plt)" "test-pkgs/pkg-test1.plt") + (shelly-install*/d "local package (zip, compiled)" "test-pkgs/pkg-test1b.zip" "pkg-test1b") + (shelly-install*/d "local package (zip, single-collection)" + "test-pkgs/pkg-test1.zip test-pkgs/pkg-test3.zip" + "pkg-test1 pkg-test3") + (shelly-install/d "local package (dir)" (url->string (path->url (path->complete-path "test-pkgs/pkg-test1")))) + (shelly-install/d "local package (file://dir)" (url->string (path->url (path->complete-path "test-pkgs/pkg-test1")))) ;; Check ".zip" file with extra directory layer: (let ([dir (make-temporary-file "zip~a" 'directory)] diff --git a/pkgs/racket-test/tests/pkg/tests-migrate.rkt b/pkgs/racket-test/tests/pkg/tests-migrate.rkt index 43b92cc909..5d5a3e7088 100644 --- a/pkgs/racket-test/tests/pkg/tests-migrate.rkt +++ b/pkgs/racket-test/tests/pkg/tests-migrate.rkt @@ -29,5 +29,7 @@ " (build-path (find-system-path 'addon-dir) (symbol->string 'other)))\"") $ "raco pkg remove -u --auto pkg-b" $ "raco pkg show -l -u -a" =stdout> " [none]\n" + $ "raco pkg migrate --dry-run -u other" + $ "raco pkg show -l -u -a" =stdout> " [none]\n" $ "raco pkg migrate -u other" $ "raco pkg show -l -u -a" =stdout> #rx"Package\\[\\*=auto\\] +Checksum +Source\npkg-a\\* +[a-f0-9.]+ \\(catalog \"pkg-a\"\\)\npkg-b +[a-f0-9.]+ +\\(catalog \"pkg-b\"\\)\n"))) diff --git a/pkgs/racket-test/tests/pkg/tests-remove.rkt b/pkgs/racket-test/tests/pkg/tests-remove.rkt index e9e9a83e62..95c5bcac73 100644 --- a/pkgs/racket-test/tests/pkg/tests-remove.rkt +++ b/pkgs/racket-test/tests/pkg/tests-remove.rkt @@ -21,7 +21,8 @@ "remove and show" (shelly-case "remove of not installed package fails" $ "raco pkg show -l -u -a" =stdout> " [none]\n" - $ "raco pkg remove not-there" =exit> 1) + $ "raco pkg remove not-there" =exit> 1 + $ "raco pkg remove --dry-run not-there" =exit> 1) (shelly-case "remove of bad name" $ "raco pkg remove bad/" =exit> 1 =stderr> #rx"disallowed") @@ -39,12 +40,17 @@ $ "raco pkg install test-pkgs/pkg-test2.zip" $ "raco pkg show -l -u -a" =stdout> #rx"Package +Checksum +Source\npkg-test1 +[a-f0-9.]+ +\\(file .+/test-pkgs/pkg-test1.zip\"\\)\npkg-test2 +[a-f0-9.]+ +\\(file .+/test-pkgs/pkg-test2.zip\"\\)\n" $ "raco pkg remove pkg-test1" =exit> 1 =stderr> #rx"pkg-test1 \\(required by: \\(pkg-test2\\)\\)" + $ "raco pkg remove --dry-run pkg-test1" =exit> 1 =stderr> #rx"pkg-test1 \\(required by: \\(pkg-test2\\)\\)" + $ "raco pkg remove --dry-run pkg-test2" + $ "raco pkg show -l -u -a" =stdout> #rx"Package +Checksum +Source\npkg-test1 +[a-f0-9.]+ +\\(file .+/test-pkgs/pkg-test1.zip\"\\)\npkg-test2 +[a-f0-9.]+ +\\(file .+/test-pkgs/pkg-test2.zip\"\\)\n" $ "raco pkg remove pkg-test2" $ "raco pkg show -l -u -a" =stdout> #rx"Package +Checksum +Source\npkg-test1 +[a-f0-9.]+ +\\(file .+/test-pkgs/pkg-test1.zip\"\\)\n") (shelly-install "remove of dep can be forced" "test-pkgs/pkg-test1.zip" $ "raco pkg install test-pkgs/pkg-test2.zip" $ "racket -e '(require pkg-test2/contains-dep)'" =exit> 0 + $ "raco pkg remove --dry-run --force pkg-test1" + $ "racket -e '(require pkg-test2/contains-dep)'" =exit> 0 $ "raco pkg remove --force pkg-test1" $ "racket -e '(require pkg-test2/contains-dep)'" =exit> 1 $ "raco pkg install test-pkgs/pkg-test1.zip" diff --git a/pkgs/racket-test/tests/pkg/tests-update.rkt b/pkgs/racket-test/tests/pkg/tests-update.rkt index e1164b54f1..8f3969d310 100644 --- a/pkgs/racket-test/tests/pkg/tests-update.rkt +++ b/pkgs/racket-test/tests/pkg/tests-update.rkt @@ -45,6 +45,8 @@ "test-pkgs/pkg-test1.zip" "pkg-test1" $ "racket -e '(require pkg-test1/update)'" =exit> 42 + $ "raco pkg update --dry-run test-pkgs/update-test/pkg-test1.zip" + $ "racket -e '(require pkg-test1/update)'" =exit> 42 $ "raco pkg update test-pkgs/update-test/pkg-test1.zip" $ "racket -e '(require pkg-test1/update)'" =exit> 43) (finally @@ -52,6 +54,8 @@ (shelly-install "packages can be replaced with local packages (file + name)" "test-pkgs/pkg-test1.zip" $ "racket -e '(require pkg-test1/update)'" =exit> 42 + $ "raco pkg update --dry-run --name pkg-test1 test-pkgs/pkg-test1-v2.zip" + $ "racket -e '(require pkg-test1/update)'" =exit> 42 $ "raco pkg update --name pkg-test1 test-pkgs/pkg-test1-v2.zip" $ "racket -e '(require pkg-test1/update)'" =exit> 43) (define tmp2-dir (path->directory-path (make-temporary-file "pkg~a" 'directory))) @@ -60,6 +64,8 @@ (shelly-install "packages can be replaced with local packages (directory)" "test-pkgs/pkg-test1.zip" $ "racket -e '(require pkg-test1/update)'" =exit> 42 + $ (~a "raco pkg update --dry-run --name pkg-test1 "tmp2-dir"pkg-test1-v2") + $ "racket -e '(require pkg-test1/update)'" =exit> 42 $ (~a "raco pkg update --name pkg-test1 "tmp2-dir"pkg-test1-v2") $ "racket -e '(require pkg-test1/update)'" =exit> 43) (shelly-install "replacement checksum can be checked" @@ -68,6 +74,8 @@ (shelly-install "checksum can be supplied for local directory" "test-pkgs/pkg-test1.zip" $ "racket -e '(require pkg-test1/update)'" =exit> 42 + $ (~a "raco pkg update --dry-run --name pkg-test1 --checksum abcdef "tmp2-dir"pkg-test1-v2") + $ "racket -e '(require pkg-test1/update)'" =exit> 42 $ (~a "raco pkg update --name pkg-test1 --checksum abcdef "tmp2-dir"pkg-test1-v2") $ "racket -e '(require pkg-test1/update)'" =exit> 43 $ "raco pkg show" =stdout> #rx"abcdef" @@ -92,6 +100,8 @@ $ "racket -e '(require pkg-test1/update)'" =exit> 42 $ "cp -f test-pkgs/pkg-test1-v2.zip test-pkgs/update-test/pkg-test1.zip" $ "cp -f test-pkgs/pkg-test1-v2.zip.CHECKSUM test-pkgs/update-test/pkg-test1.zip.CHECKSUM" + $ "raco pkg update --dry-run pkg-test1" =exit> 0 + $ "racket -e '(require pkg-test1/update)'" =exit> 42 $ "raco pkg update pkg-test1" =exit> 0 $ "racket -e '(require pkg-test1/update)'" =exit> 43) (finally @@ -105,9 +115,12 @@ (shelly-install* "remote packages can be updated, single-collection to multi-collection" "test-pkgs/pkg-test1.zip http://localhost:9997/update-test/pkg-test3.zip" "pkg-test1 pkg-test3" + $ "racket -e '(require pkg-test3)'" =stdout> "pkg-test3/main loaded\n" $ "raco pkg update pkg-test3" =exit> 0 =stdout> "Downloading checksum for pkg-test3\nNo updates available\n" $ "cp -f test-pkgs/pkg-test3-v2.zip test-pkgs/update-test/pkg-test3.zip" $ "cp -f test-pkgs/pkg-test3-v2.zip.CHECKSUM test-pkgs/update-test/pkg-test3.zip.CHECKSUM" + $ "raco pkg update --dry-run pkg-test3" =exit> 0 + $ "racket -e '(require pkg-test3)'" =stdout> "pkg-test3/main loaded\n" $ "raco pkg update pkg-test3" =exit> 0 $ "racket -e '(require pkg-test3)'" =stdout> "pkg-test3/main version 2 loaded\n") (finally @@ -118,12 +131,15 @@ $ "mkdir -p test-pkgs/update-test" $ "cp -f test-pkgs/pkg-test3-v2.zip test-pkgs/update-test/pkg-test3.zip" $ "cp -f test-pkgs/pkg-test3-v2.zip.CHECKSUM test-pkgs/update-test/pkg-test3.zip.CHECKSUM" - (shelly-install* "remote packages can be updated, multi-colelction to single-collection" + (shelly-install* "remote packages can be updated, multi-collection to single-collection" "test-pkgs/pkg-test1.zip http://localhost:9997/update-test/pkg-test3.zip" "pkg-test1 pkg-test3" + $ "racket -e '(require pkg-test3)'" =stdout> "pkg-test3/main version 2 loaded\n" $ "raco pkg update pkg-test3" =exit> 0 =stdout> "Downloading checksum for pkg-test3\nNo updates available\n" $ "cp -f test-pkgs/pkg-test3.zip test-pkgs/update-test/pkg-test3.zip" $ "cp -f test-pkgs/pkg-test3.zip.CHECKSUM test-pkgs/update-test/pkg-test3.zip.CHECKSUM" + $ "raco pkg update --dry-run pkg-test3" =exit> 0 + $ "racket -e '(require pkg-test3)'" =stdout> "pkg-test3/main version 2 loaded\n" $ "raco pkg update pkg-test3" =exit> 0 $ "racket -e '(require pkg-test3)'" =stdout> "pkg-test3/main loaded\n") (finally @@ -145,6 +161,8 @@ $ "racket -e '(require pkg-test1/update)'" =exit> 42 $ "cp -f test-pkgs/pkg-test1-v2.zip test-pkgs/update-test/pkg-test1.zip" $ "cp -f test-pkgs/pkg-test1-v2.zip.CHECKSUM test-pkgs/update-test/pkg-test1.zip.CHECKSUM" + $ "raco pkg update --dry-run --update-deps pkg-test2" =exit> 0 + $ "racket -e '(require pkg-test1/update)'" =exit> 42 $ "raco pkg update --update-deps pkg-test2" =exit> 0 $ "racket -e '(require pkg-test1/update)'" =exit> 43 $ "raco pkg remove pkg-test2") diff --git a/racket/collects/pkg/lib.rkt b/racket/collects/pkg/lib.rkt index a4a51c8795..9b686a63ed 100644 --- a/racket/collects/pkg/lib.rkt +++ b/racket/collects/pkg/lib.rkt @@ -119,7 +119,8 @@ #:infer-clone-from-dir? boolean? #:lookup-for-clone? boolean? #:multi-clone-behavior (or/c 'fail 'force 'convert 'ask) - #:pull-behavior (or/c 'ff-only 'rebase 'try)) + #:pull-behavior (or/c 'ff-only 'rebase 'try) + #:dry-run? boolean?) (or/c #f 'skip (listof (or/c path-string? (non-empty-listof path-string?)))))] [pkg-remove (->* ((listof string?)) @@ -128,7 +129,8 @@ #:quiet? boolean? #:use-trash? boolean? #:from-command-line? boolean? - #:demote? boolean?) + #:demote? boolean? + #:dry-run? boolean?) (or/c #f 'skip (listof (or/c path-string? (non-empty-listof path-string?)))))] [pkg-show (->* (string? (or/c #f (listof string?))) @@ -156,7 +158,8 @@ #:force-strip? boolean? #:link-dirs? boolean? #:multi-clone-behavior (or/c 'fail 'force 'convert 'ask) - #:pull-behavior (or/c 'ff-only 'rebase 'try)) + #:pull-behavior (or/c 'ff-only 'rebase 'try) + #:dry-run? boolean?) (or/c #f 'skip (listof (or/c path-string? (non-empty-listof path-string?)))))] [pkg-migrate (->* (string?) @@ -169,7 +172,8 @@ #:quiet? boolean? #:from-command-line? boolean? #:strip (or/c #f 'source 'binary 'binary-lib) - #:force-strip? boolean?) + #:force-strip? boolean? + #:dry-run? boolean?) (or/c #f 'skip (listof (or/c path-string? (non-empty-listof path-string?)))))] [pkg-catalog-show (->* ((listof string?)) diff --git a/racket/collects/pkg/main.rkt b/racket/collects/pkg/main.rkt index fa015657ac..6091fbbe2b 100644 --- a/racket/collects/pkg/main.rkt +++ b/racket/collects/pkg/main.rkt @@ -140,6 +140,7 @@ (define-syntax (make-commands stx) (syntax-case stx () [(_ #:scope-flags (scope-flags ...) + #:dry-run-flags (dry-run-flags ...) #:job-flags (job-flags ...) #:trash-flags (trash-flags ...) #:catalog-flags (catalog-flags ...) @@ -181,6 +182,7 @@ [#:bool pkgs () ("Install only the specified packages, even when none are provided")] install-force-flags ... install-clone-flags ... + dry-run-flags ... job-flags ... trash-flags ... [#:bool fail-fast () ("Break `raco setup' when it discovers an error")] @@ -242,6 +244,7 @@ 'ask)) #:pull-behavior pull #:link-dirs? link-dirs? + #:dry-run? dry-run #:use-trash? (not no-trash) (for/list ([p (in-list sources)]) (pkg-desc p a-type* name checksum #f @@ -274,6 +277,7 @@ [#:bool skip-uninstalled () ("Skip a given if not installed")] install-force-flags ... install-clone-flags ... + dry-run-flags ... job-flags ... trash-flags ... #:args pkg-source @@ -345,6 +349,7 @@ #:pull-behavior pull #:link-dirs? link-dirs? #:infer-clone-from-dir? (not (or link static-link copy)) + #:dry-run? dry-run #:use-trash? (not no-trash))))) (setup "updated" no-setup #f setup-collects jobs))))] ;; ---------------------------------------- @@ -357,6 +362,7 @@ #:once-any scope-flags ... #:once-each + dry-run-flags ... job-flags ... trash-flags ... #:args pkg @@ -371,6 +377,7 @@ #:demote? demote #:auto? auto #:force? force + #:dry-run? dry-run #:use-trash? (not no-trash)))) (setup "removed" no-setup #f setup-collects jobs)))] ;; ---------------------------------------- @@ -445,6 +452,7 @@ #:once-each catalog-flags ... install-force-flags ... + dry-run-flags ... job-flags ... #:args (from-version) (call-with-package-scope @@ -466,7 +474,8 @@ #:strip (or (and source 'source) (and binary 'binary) (and binary-lib 'binary-lib)) - #:force-strip? force)))) + #:force-strip? force + #:dry-run? dry-run)))) (setup "migrated" no-setup #f setup-collects jobs)))] ;; ---------------------------------------- [create @@ -654,6 +663,8 @@ [#:bool installation ("-i") "Shorthand for `--scope installation'"] [#:bool user ("-u") "Shorthand for `--scope user'"] [(#:str dir #f) scope-dir () "Select package scope "]) + #:dry-run-flags + ([#:bool dry-run () ("Don't actually change package installation")]) #:job-flags ([#:bool no-setup () ("Don't `raco setup' after changing packages (usually a bad idea)")] [(#:num n #f) jobs ("-j") "Setup with parallel jobs"] diff --git a/racket/collects/pkg/private/git.rkt b/racket/collects/pkg/private/git.rkt index 1ee7c4d540..688046f5b9 100644 --- a/racket/collects/pkg/private/git.rkt +++ b/racket/collects/pkg/private/git.rkt @@ -14,6 +14,7 @@ (define (git #:status [status void] #:quiet-stderr? [quiet-stderr? #t] ; suppress stderr unless error #:fail-mode [fail-mode 'error] + #:dry-run? [dry-run? #f] . args) (define exe (force git-exe)) (unless exe @@ -28,7 +29,11 @@ (with-handlers ([values (lambda (exn) ;; re-raise after restoring stderr: (lambda () (raise exn)))]) - (define r (apply system* exe args)) + (define r + (cond + [dry-run? #t] + [else + (apply system* exe args)])) (lambda () r))))) (cond [r #t] diff --git a/racket/collects/pkg/private/install.rkt b/racket/collects/pkg/private/install.rkt index 2df70b180d..39c552f816 100644 --- a/racket/collects/pkg/private/install.rkt +++ b/racket/collects/pkg/private/install.rkt @@ -167,6 +167,7 @@ #:ai-cache ai-cache #:clone-info clone-info #:pull-behavior pull-behavior + #:dry-run? dry-run? descs) (define download-printf (if quiet? void printf/flush)) (define check-sums? (not ignore-checksums?)) @@ -240,8 +241,11 @@ ;; The `do-it` thunk: (lambda (fail-repos) (unless quiet? - (download-printf "Promoting ~a from auto-installed to explicitly installed\n" pkg-name)) - (update-pkg-db! pkg-name (update-auto existing-pkg-info #f))))] + (download-printf "Promoting ~a from auto-installed to explicitly installed~a\n" + pkg-name + (dry-run-explain dry-run?))) + (unless dry-run? + (update-pkg-db! pkg-name (update-auto existing-pkg-info #f)))))] [else ;; Fail --- already installed (clean!) @@ -453,7 +457,7 @@ (let () (define (continue conversation) (raise (vector #t infos pkg-name update-pkgs - (λ () (for-each (compose (remove-package #t quiet? use-trash?) pkg-desc-name) update-pkgs)) + (λ () (for-each (compose (remove-package #t quiet? use-trash? dry-run?) pkg-desc-name) update-pkgs)) conversation clone-info))) (match (if (andmap (lambda (dep) (set-member? implies (pkg-desc-name dep))) @@ -558,7 +562,7 @@ #:link-dirs? link-dirs?)]) (for ([pkg (in-list update-pkgs)]) (updater #:prefetch? #t pkg)) (append-map updater update-pkgs))]) - (λ () (for-each (compose (remove-package #t quiet? use-trash?) pkg-desc-name) to-update)))) + (λ () (for-each (compose (remove-package #t quiet? use-trash? dry-run?) pkg-desc-name) to-update)))) (match this-dep-behavior ['fail (clean!) @@ -591,17 +595,19 @@ ;; The "do-it" function (see `repos+do-its` below): (λ (fail-repos) (when updating? - (download-printf "Re-installing ~a\n" pkg-name)) + (download-printf "Re-installing ~a~a\n" pkg-name (dry-run-explain dry-run?))) (define final-pkg-dir (cond [clean? (define final-pkg-dir (or git-dir (select-package-directory - (build-path (pkg-installed-dir) pkg-name)))) - (unless git-dir - (make-parent-directory* final-pkg-dir) - (copy-directory/files pkg-dir final-pkg-dir #:keep-modify-seconds? #t)) - (clean!) + (build-path (pkg-installed-dir) pkg-name) + dry-run?))) + (unless dry-run? + (unless git-dir + (make-parent-directory* final-pkg-dir) + (copy-directory/files pkg-dir final-pkg-dir #:keep-modify-seconds? #t)) + (clean!)) final-pkg-dir] [else pkg-dir])) @@ -612,14 +618,15 @@ (if single-collect "single-collection " "") final-pkg-dir) (define scope (current-pkg-scope)) - (links final-pkg-dir - #:name single-collect - #:user? (not (or (eq? 'installation scope) - (path? scope))) - #:file (scope->links-file scope) - #:root? (not single-collect) - #:static-root? (and (pair? orig-pkg) - (eq? 'static-link (car orig-pkg)))) + (unless dry-run? + (links final-pkg-dir + #:name single-collect + #:user? (not (or (eq? 'installation scope) + (path? scope))) + #:file (scope->links-file scope) + #:root? (not single-collect) + #:static-root? (and (pair? orig-pkg) + (eq? 'static-link (car orig-pkg))))) (define alt-dir-name ;; If we had to pick an alternate dir name, then record it: (let-values ([(base name dir?) (split-path final-pkg-dir)]) @@ -635,7 +642,8 @@ (define this-pkg-info (make-pkg-info orig-pkg new-checksum auto? single-collect alt-dir-name)) (log-pkg-debug "updating db with ~e to ~e" pkg-name this-pkg-info) - (update-pkg-db! pkg-name this-pkg-info)))])) + (unless dry-run? + (update-pkg-db! pkg-name this-pkg-info))))])) (define metadata-ns (make-metadata-namespace)) (define infos (for/list ([v (in-list descs)]) @@ -694,8 +702,9 @@ (define fail-repos (for/fold ([fail-repos #hash()]) ([(git-dir checksums) (in-hash repos)]) (parameterize ([current-directory git-dir]) - (download-printf "Merging commits at ~a\n" - git-dir) + (download-printf "Merging commits at ~a~a\n" + git-dir + (dry-run-explain dry-run?)) (when ((length checksums) . > . 1) (download-printf (~a "Multiple packages in the of the clone\n" " " git-dir "\n" @@ -706,6 +715,7 @@ (define ok? (git #:status (lambda (s) (download-printf "~a\n" s)) #:fail-mode 'status + #:dry-run? dry-run? (if rebase? "rebase" "merge") (if rebase? "--onto" "--ff-only") checksum)) @@ -762,7 +772,8 @@ (cond [(or (null? repo+do-its) - (and (not updating-any?) (andmap is-promote? all-infos))) + (and (not updating-any?) (andmap is-promote? all-infos)) + dry-run?) ;; No actions, so no setup: 'skip] [else @@ -792,7 +803,7 @@ (loop new-check (set-union setup-pkgs new-check))]))) -(define (select-package-directory dir #:counter [counter 0]) +(define (select-package-directory dir dry-run? #:counter [counter 0]) (define full-dir (if (zero? counter) dir (let-values ([(base name dir?) (split-path dir)]) @@ -804,7 +815,8 @@ (build-path base new-name) new-name)))) (cond - [(directory-exists? full-dir) + [(and (directory-exists? full-dir) + (not dry-run?)) ;; If the directory exists, assume that we'd like to replace it. ;; Maybe the directory couldn't be deleted when a package was ;; uninstalled, and maybe it will work now (because some process @@ -813,7 +825,7 @@ (lambda (exn) (log-pkg-warning "error deleting old directory: ~a" (exn-message exn)) - (select-package-directory dir #:counter (add1 counter)))]) + (select-package-directory dir #f #:counter (add1 counter)))]) (delete-directory/files full-dir) ;; delete succeeded: full-dir)] @@ -856,7 +868,8 @@ (read-pkg-db) (if quiet? void printf/flush))] #:pull-behavior [pull-behavior 'ff-only] - #:convert-to-non-clone? [convert-to-non-clone? #f]) + #:convert-to-non-clone? [convert-to-non-clone? #f] + #:dry-run? [dry-run? #f]) (define download-printf (if quiet? void printf/flush)) (define descs @@ -929,6 +942,7 @@ #:multi-clone-behavior (vector-ref clone-info 0) #:repo-descs (vector-ref clone-info 1) #:pull-behavior pull-behavior + #:dry-run? dry-run? (for/list ([dep (in-list deps)]) (if (pkg-desc? dep) dep @@ -951,7 +965,7 @@ #:remote-checksum-cache remote-checksum-cache #:pre-succeed (λ () (for ([pkg-name (in-hash-keys extra-updating)]) - ((remove-package #t quiet? use-trash?) pkg-name)) + ((remove-package #t quiet? use-trash? dry-run?) pkg-name)) (pre-succeed)) #:updating? updating? #:extra-updating extra-updating @@ -967,6 +981,7 @@ #:clone-info (vector clone-behavior repo-descs) #:pull-behavior pull-behavior + #:dry-run? dry-run? new-descs) (unless (empty? summary-deps) (unless quiet? @@ -1243,7 +1258,8 @@ #:infer-clone-from-dir? [infer-clone-from-dir? #f] #:lookup-for-clone? [lookup-for-clone? #f] #:multi-clone-behavior [clone-behavior 'fail] - #:pull-behavior [pull-behavior 'ff-only]) + #:pull-behavior [pull-behavior 'ff-only] + #:dry-run? [dry-run? #f]) (define download-printf (if quiet? void printf/flush)) (define metadata-ns (make-metadata-namespace)) (define db (read-pkg-db)) @@ -1337,7 +1353,7 @@ (flush-output)) (pkg-install #:updating? #t - #:pre-succeed (λ () (for-each (compose (remove-package #t quiet? use-trash?) pkg-desc-name) to-update)) + #:pre-succeed (λ () (for-each (compose (remove-package #t quiet? use-trash? dry-run?) pkg-desc-name) to-update)) #:dep-behavior dep-behavior #:update-deps? update-deps? #:update-implies? update-implies? @@ -1362,6 +1378,7 @@ (andmap pkg-desc? in-pkgs) (not (ormap pkg-desc-extra-path in-pkgs))) #:pull-behavior pull-behavior + #:dry-run? dry-run? to-update)])))) ;; ---------------------------------------- diff --git a/racket/collects/pkg/private/migrate.rkt b/racket/collects/pkg/private/migrate.rkt index e26e06160b..e9befec449 100644 --- a/racket/collects/pkg/private/migrate.rkt +++ b/racket/collects/pkg/private/migrate.rkt @@ -23,7 +23,8 @@ #:use-cache? [use-cache? #t] #:dep-behavior [dep-behavior #f] #:strip [strip-mode #f] - #:force-strip? [force-strip? #f]) + #:force-strip? [force-strip? #f] + #:dry-run? [dry-run? #f]) (define from-db (parameterize ([current-pkg-scope-version from-version]) (installed-pkg-table #:scope 'user))) @@ -77,6 +78,7 @@ #:quiet? quiet? #:from-command-line? from-command-line? #:strip strip-mode - #:force-strip? force-strip?) + #:force-strip? force-strip? + #:dry-run? dry-run?) (unless quiet? (printf "Packages migrated\n"))))) diff --git a/racket/collects/pkg/private/print.rkt b/racket/collects/pkg/private/print.rkt index abb19dc94d..8bc340d6a3 100644 --- a/racket/collects/pkg/private/print.rkt +++ b/racket/collects/pkg/private/print.rkt @@ -67,3 +67,9 @@ (if default-yes? "" "nothing or ")) (eprintf " `a' or `A' for \"yes for all\", or `c' or `C' for \"cancel\".\n") (loop)]))) + + +(define (dry-run-explain dry-run?) + (if dry-run? + " (but not really)" + "")) diff --git a/racket/collects/pkg/private/remove.rkt b/racket/collects/pkg/private/remove.rkt index 1c6792ec67..18ad59db28 100644 --- a/racket/collects/pkg/private/remove.rkt +++ b/racket/collects/pkg/private/remove.rkt @@ -16,51 +16,57 @@ (provide remove-package pkg-remove) -(define (demote-packages quiet? pkg-names) +(define (demote-packages quiet? dry-run? pkg-names) (define db (read-pkg-db)) (for ([pkg-name (in-list pkg-names)]) (define pi (package-info pkg-name #:db db)) (unless (pkg-info-auto? pi) (unless quiet? - (printf/flush "Demoting ~a to auto-installed\n" pkg-name)) - (update-pkg-db! pkg-name (update-auto pi #t))))) + (printf/flush "Demoting ~a to auto-installed~a\n" + pkg-name + (dry-run-explain dry-run?))) + (unless dry-run? + (update-pkg-db! pkg-name (update-auto pi #t)))))) -(define ((remove-package for-install? quiet? use-trash?) pkg-name) +(define ((remove-package for-install? quiet? use-trash? dry-run?) pkg-name) (unless quiet? - (printf/flush "~a ~a\n" + (printf/flush "~a ~a~a\n" (if for-install? "Uninstalling to prepare re-install of" "Removing") - pkg-name)) + pkg-name + (dry-run-explain dry-run?))) (define db (read-pkg-db)) (define pi (package-info pkg-name #:db db)) (match-define (pkg-info orig-pkg checksum _) pi) (define pkg-dir (pkg-directory* pkg-name #:db db)) - (remove-from-pkg-db! pkg-name) + (unless dry-run? + (remove-from-pkg-db! pkg-name)) (define scope (current-pkg-scope)) (define user? (not (or (eq? scope 'installation) (path? scope)))) - (match orig-pkg - [`(,(or 'link 'static-link 'clone) ,_ . ,_) - (links pkg-dir - #:remove? #t - #:user? user? - #:file (scope->links-file scope) - #:root? (not (sc-pkg-info? pi)))] - [_ - (links pkg-dir - #:remove? #t - #:user? user? - #:file (scope->links-file scope) - #:root? (not (sc-pkg-info? pi))) - (cond - [(and use-trash? - (select-trash-dest pkg-name)) - => (lambda (trash-dest) - (printf/flush "Moving ~a to trash: ~a\n" pkg-name trash-dest) - (rename-file-or-directory pkg-dir trash-dest))] - [else - (delete-directory/files pkg-dir)])])) + (unless dry-run? + (match orig-pkg + [`(,(or 'link 'static-link 'clone) ,_ . ,_) + (links pkg-dir + #:remove? #t + #:user? user? + #:file (scope->links-file scope) + #:root? (not (sc-pkg-info? pi)))] + [_ + (links pkg-dir + #:remove? #t + #:user? user? + #:file (scope->links-file scope) + #:root? (not (sc-pkg-info? pi))) + (cond + [(and use-trash? + (select-trash-dest pkg-name)) + => (lambda (trash-dest) + (printf/flush "Moving ~a to trash: ~a\n" pkg-name trash-dest) + (rename-file-or-directory pkg-dir trash-dest))] + [else + (delete-directory/files pkg-dir)])]))) (define (pkg-remove given-pkgs @@ -69,6 +75,7 @@ #:auto? [auto? #f] #:quiet? [quiet? #f] #:use-trash? [use-trash? #f] + #:dry-run? [dry-run? #f] #:from-command-line? [from-command-line? #f]) (define db (read-pkg-db)) (define all-pkgs @@ -143,14 +150,15 @@ ;; Demote any package that is not going to be removed: (demote-packages quiet? + dry-run? (set->list (set-subtract (list->set in-pkgs) (list->set remove-pkgs))))) - (for-each (remove-package #f quiet? use-trash?) + (for-each (remove-package #f quiet? use-trash? dry-run?) remove-pkgs) (cond - [(or (null? remove-pkgs) demote?) + [(or (null? remove-pkgs) demote? dry-run?) ;; Did nothing, so no setup: 'skip] [else