diff --git a/collects/planet2/lib.rkt b/collects/planet2/lib.rkt index d9ab7b6c6e..025284dd46 100644 --- a/collects/planet2/lib.rkt +++ b/collects/planet2/lib.rkt @@ -23,6 +23,7 @@ setup/getinfo setup/dirs racket/format + version/utils "name.rkt" "util.rkt") @@ -153,12 +154,35 @@ (define (check-dependencies deps) (unless (and (list? deps) (for/and ([dep (in-list deps)]) - (and (string? dep) - (package-source->name dep)))) + (define (package-source? dep) + (and (string? dep) + (package-source->name dep))) + (define (version? s) + (and (string? s) + (valid-version? s))) + (or (package-source? dep) + (and (list? dep) + (= 2 (length dep)) + (package-source? (car dep)) + (version? (cadr dep)))))) (pkg-error (~a "invalid `deps' specification\n" " specification: ~e") deps))) +(define (dependency->name dep) + (package-source->name + (dependency->source dep))) + +(define (dependency->source dep) + (if (string? dep) + dep + (car dep))) + +(define (dependency->version dep) + (if (string? dep) + #f + (cadr dep))) + (define (with-package-lock* t) (make-directory* (pkg-dir)) (call-with-file-lock/timeout @@ -187,10 +211,11 @@ (define (package-index-lookup pkg) (or (for/or ([i (in-list (read-pkg-cfg/def "indexes"))]) + (define addr (combine-url/relative (string->url i) + (format "pkg/~a" pkg))) + (log-planet2-debug "resolving via ~a" (url->string addr)) (call/input-url+200 - (combine-url/relative - (string->url i) - (format "/pkg/~a" pkg)) + addr read)) (pkg-error (~a "cannot find package on indexes\n" " package: ~a") @@ -631,7 +656,8 @@ (when clean? (delete-directory/files pkg-dir))) (define simultaneous-installs - (list->set (map install-info-name infos))) + (for/hash ([i (in-list infos)]) + (values (install-info-name i) (install-info-directory i)))) (cond [(and (not updating?) (package-info pkg-name #f)) (clean!) @@ -683,32 +709,38 @@ 'deps (lambda () empty) #:checker check-dependencies)) (define unsatisfied-deps - (filter-not (λ (dep) - (or (set-member? simultaneous-installs - (package-source->name dep)) - (hash-has-key? db dep))) - deps)) + (map dependency->source + (filter-not (λ (dep) + (define name (dependency->name dep)) + (or (equal? name "racket") + (hash-ref simultaneous-installs name #f) + (hash-has-key? db name))) + deps))) (and (not (empty? unsatisfied-deps)) unsatisfied-deps))) => (λ (unsatisfied-deps) - (match + (match (or dep-behavior (if name? 'search-ask 'fail)) ['fail (clean!) - (pkg-error "missing dependencies\n missing packages:~a" (format-list unsatisfied-deps))] + (pkg-error (~a "missing dependencies\n" + " for package: ~a\n" + " missing packages:~a") + pkg + (format-list unsatisfied-deps))] ['search-auto (printf (string-append "The following packages are listed as dependencies, but are not currently installed,\n" - "so we will automatically install them:\n")) + "so they will be automatically installed:\n")) (printf "\t") (for ([p (in-list unsatisfied-deps)]) (printf "~a " p)) (printf "\n") - (raise (vector infos unsatisfied-deps))] + (raise (vector updating? infos unsatisfied-deps void))] ['search-ask (printf "The following packages are listed as dependencies, but are not currently installed:\n") (printf "\t") @@ -720,13 +752,110 @@ (flush-output) (match (read-line) [(or "y" "Y" "") - (raise (vector infos unsatisfied-deps))] + (raise (vector updating? infos unsatisfied-deps void))] [(or "n" "N") (clean!) (pkg-error "missing dependencies\n missing packages:~a" (format-list unsatisfied-deps))] [x (eprintf "Invalid input: ~e\n" x) (loop)]))]))] + [(and + (not (eq? dep-behavior 'force)) + (let () + (define deps (get-metadata metadata-ns pkg-dir + 'deps (lambda () empty) + #:checker check-dependencies)) + (define update-deps + (filter-map (λ (dep) + (define name (dependency->name dep)) + (define req-vers (dependency->version dep)) + (define-values (inst-vers* can-try-update?) + (cond + [(not req-vers) + (values #f #f)] + [(equal? name "racket") + (values (version) #f)] + [(hash-ref simultaneous-installs name #f) + => (lambda (dir) + (values + (get-metadata metadata-ns dir + 'version (lambda () "0.0")) + #f))] + [else + (values (get-metadata metadata-ns (package-directory name) + 'version (lambda () "0.0")) + #t)])) + (define inst-vers (if (and req-vers + (not (and (string? inst-vers*) + (valid-version? inst-vers*)))) + (begin + (log-planet2-error + "bad verson specification for ~a: ~e" + name + inst-vers*) + "0.0") + inst-vers*)) + (and req-vers + ((version->integer req-vers) + . > . + (version->integer inst-vers)) + (list name can-try-update? inst-vers req-vers))) + deps)) + (and (not (empty? update-deps)) + update-deps))) + => (lambda (update-deps) + (define (report-mismatch update-deps) + (define multi? (1 . < . (length update-deps))) + (pkg-error (~a "version mismatch for dependenc~a\n" + " for package: ~a\n" + " mismatch packages:~a") + (if multi? "ies" "y") + pkg + (format-deps update-deps))) + (define (format-deps update-deps) + (format-list (for/list ([ud (in-list update-deps)]) + (format "~a (have ~a, need ~a)" + (car ud) + (caddr ud) + (cadddr ud))))) + ;; If there's a mismatch that we can't attempt to update, complain. + (unless (andmap cadr update-deps) + (report-mismatch (filter (compose not cadr) update-deps))) + ;; Try updates: + (define update-pkgs (map car update-deps)) + (define (make-pre-succeed) + (let ([to-update (filter-map update-package update-pkgs)]) + (log-error "to update ~s" to-update) + (λ () (for-each (compose remove-package pkg-desc-name) to-update)))) + (match (or dep-behavior + (if name? + 'search-ask + 'fail)) + ['fail + (clean!) + (report-mismatch update-deps)] + ['search-auto + (printf (string-append + "The following packages are listed as dependencies, but are not at the required\n" + "version, so they will be automatically updated:~a\n") + (format-deps update-deps)) + (raise (vector #t infos update-pkgs (make-pre-succeed)))] + ['search-ask + (printf (~a "The following packages are listed as dependencies, but are not at the required\n" + "versions:~a\n") + (format-deps update-deps)) + (let loop () + (printf "Would you like to update them via your package indices? [Yn] ") + (flush-output) + (match (read-line) + [(or "y" "Y" "") + (raise (vector #t infos update-pkgs (make-pre-succeed)))] + [(or "n" "N") + (clean!) + (report-mismatch update-deps)] + [x + (eprintf "Invalid input: ~e\n" x) + (loop)]))]))] [else (λ () (define final-pkg-dir @@ -788,14 +917,14 @@ #:updating? [updating? #f]) (with-handlers* ([vector? (match-lambda - [(vector new-infos deps) + [(vector updating? new-infos deps more-pre-succeed) (install-cmd #:old-infos new-infos #:old-auto+pkgs (append old-descs descs) #:force? force #:ignore-checksums? ignore-checksums #:dep-behavior dep-behavior - #:pre-succeed pre-succeed + #:pre-succeed (lambda () (pre-succeed) (more-pre-succeed)) #:updating? updating? (for/list ([dep (in-list deps)]) (pkg-desc dep #f #f #t)))])]) @@ -841,7 +970,7 @@ (and new-checksum (not (equal? checksum new-checksum)) ;; FIXME: the type shouldn't be #f here; it should be - ;; preseved form instal time: + ;; preseved from install time: (pkg-desc orig-pkg-source #f pkg-name auto?))])) (define ((package-dependencies metadata-ns) pkg-name) diff --git a/collects/planet2/scribblings/planet2.scrbl b/collects/planet2/scribblings/planet2.scrbl index 3f303f541e..0397f64c2c 100644 --- a/collects/planet2/scribblings/planet2.scrbl +++ b/collects/planet2/scribblings/planet2.scrbl @@ -39,26 +39,38 @@ metadata}. @deftech{Package metadata} is: @itemlist[ - @item{a @deftech{package name} -- a string made of the characters @|package-name-chars|.} - @item{a list of dependencies -- a list of strings that name other packages that must be installed simultaneously.} - @item{a checksum -- a string that identifies different releases of a package.} + @item{a @deftech{package name} --- a string made of the characters @|package-name-chars|.} + @item{a @deftech{version} --- a string of the form @nonterm{maj}@litchar{.}@nonterm{min}, + @nonterm{maj}@litchar{.}@nonterm{min}@litchar{.}@nonterm{sub}, or + @nonterm{maj}@litchar{.}@nonterm{min}@litchar{.}@nonterm{sub}@litchar{.}@nonterm{rel}, + where @nonterm{maj}, @nonterm{min}, @nonterm{sub}, and @nonterm{rel} are + all canonical decimal representations of natural numbers, @nonterm{min} has no more + than two digits, and @nonterm{sub} and @nonterm{rel} has no more than + three digits. A version is intended to reflect available features of + a package, and should not be confused with different releases of + a package as indicated by the @tech{checksum}.} + @item{a list of dependencies --- a list of packages to be installed simultaneously, optionally + with a lower bound on each package's version.} + @item{a @deftech{checksum} --- a string that identifies different releases of a package. A + package can be updated when its @tech{checksum} changes + whether or not its @tech{version} changes.} ] A @tech{package} is typically represented by a directory with the same -name as the package. The checksum is typically left implicit. -If the package depends on other packages, the directory can -contain a file named @filepath{info.rkt} (see @secref["metadata"]). +name as the package. The @tech{checksum} is typically left implicit. +The package directory can contain a file named @filepath{info.rkt} +to declare other metadata (see @secref["metadata"]). A @deftech{package source} identifies a @tech{package} representation. Each package source type has a different way of -storing the checksum. The valid package source types are: +storing the @tech{checksum}. The valid package source types are: @itemlist[ @item{a local file path naming an archive -- The name of the package -is the basename of the archive file. The checksum for archive +is the basename of the archive file. The @tech{checksum} for archive @filepath{f.@nonterm{ext}} is given by the file @filepath{f.@nonterm{ext}.CHECKSUM}. For -example, @filepath{~/tic-tac-toe.zip}'s checksum would be inside +example, @filepath{~/tic-tac-toe.zip}'s @tech{checksum} would be inside @filepath{~/tic-tac-toe.zip.CHECKSUM}. The valid archive formats are (currently) @filepath{.zip}, @filepath{.tar}, @filepath{.tgz}, @filepath{.tar.gz}, and @@ -71,7 +83,7 @@ with alphabetic characters followed by @litchar{://}. The inferred package name is the filename without its suffix.} @item{a local directory -- The name of the package is the name of the -directory. The checksum is not present. For example, +directory. The @tech{checksum} is not present. For example, @filepath{~/tic-tac-toe/} is directory package source. A package source is inferred to refer @@ -81,10 +93,10 @@ with alphabetic characters followed by @litchar{://}. The inferred package name is the directory name.} @item{a remote URL naming an archive -- This type follows the same -rules as a local file path, but the archive and checksum files are +rules as a local file path, but the archive and @tech{checksum} files are accessed via HTTP(S). For example, @filepath{http://game.com/tic-tac-toe.zip} is a remote URL package -source whose checksum is found at +source whose @tech{checksum} is found at @filepath{http://game.com/tic-tac-toe.zip.CHECKSUM}. A package source is inferred to be a URL only when it @@ -99,9 +111,9 @@ contain a file named @filepath{MANIFEST} that lists all the contingent files. These are downloaded into a local directory and then the rules for local directory paths are followed. However, if the remote directory contains a file named @filepath{.CHECKSUM}, then it is used -to determine the checksum. For example, +to determine the @tech{checksum}. For example, @filepath{http://game.com/tic-tac-toe/} is a directory URL package -source whose checksum is found at +source whose @tech{checksum} is found at @filepath{http://game.com/tic-tac-toe/.CHECKSUM}. A package source is inferred to be a URL the same for a directory or @@ -120,7 +132,7 @@ is a GitHub package source. The @exec{zip}-formatted archive for the repository (generated by GitHub for every branch) is used as a remote URL archive path, except the -checksum is the hash identifying the branch. +@tech{checksum} is the hash identifying the branch. A package source is inferred to be a GitHub reference when it starts with @litchar{github://}; a package source that is otherwise @@ -130,7 +142,7 @@ is the last element of @nonterm{optional-subpath} if it is non-empty, otherwise the inferred name is @nonterm{repository}.} @item{a @tech{package name} -- A @tech{package name resolver} is -consulted to determine the source and checksum for the package. For +consulted to determine the source and @tech{checksum} for the package. For example, @exec{tic-tac-toe} is a package name that can be used as a package source. @@ -143,10 +155,10 @@ means that it has only the characters @|package-name-chars|.} A @deftech{package name resolver} (@deftech{PNR}) is a server that converts package names to other package sources. A PNR is identified by a string representing a URL, such that appending -@exec{/pkg/}@nonterm{package} forms a URL that refers to a -@racket[read]-able hash table with the keys: @racket['source] bound to -the source and @racket['checksum] bound to the checksum. Typically, -the source will be a remote URL string. +@exec{pkg/}@nonterm{package} forms a URL that refers to a +@racket[read]-able hash table with the keys: @racket['source] mapped to +the @tech{package source} string and @racket['checksum] mapped to the +@tech{checksum} value. Typically, the source will be a remote URL. PLT supports two @tech{package name resolvers} that are enabled by default: @url{https://plt-etc.byu.edu:9004} for new @@ -175,8 +187,11 @@ the purposes of conflicts, a module is a file that ends in @filepath{.rkt} or @filepath{.ss}. Package A is a @deftech{package update} of Package B if (1) B is -installed, (2) A and B have the same name, and (3) A's checksum is -different than B's. +installed, (2) A and B have the same name, and (3) A's @tech{checksum} is +different than B's. Note that a package @tech{version} is not taken +into account when determining a @tech{package update}, although a change +in a package's @tech{version} (in either direction) should normally +imply a change in the @tech{checksum}. @; ---------------------------------------- @@ -212,20 +227,20 @@ sub-sub-commands: environment variable @envvar{PLT_PLANET2_NOSETUP} is set to any non-empty value.} @item{@DFlag{installation} or @Flag{i} --- Install system-wide, rather than user-local.} - @item{@DFlag{shared} or @Flag{s} --- Install for all versions, rather than user-local and version-specific.} + @item{@DFlag{shared} or @Flag{s} --- Install for all Racket versions, rather than user-local and version-specific.} @item{@DFlag{deps} @nonterm{behavior} --- Selects the behavior for dependencies, where @nonterm{behavior} is one of @itemlist[ - @item{@exec{fail} --- Cancels the installation if dependencies are unmet (default for most packages)} - @item{@exec{force} --- Installs the package(s) despite missing dependencies (unsafe)} - @item{@exec{search-ask} --- Looks for the dependencies via the configured @tech{package name resolvers} - (default if the dependency is an indexed name) but asks if you would like it installed.} - @item{@exec{search-auto} --- Like @exec{search-ask}, but does not ask for permission to install.} + @item{@exec{fail} --- Cancels the installation if dependencies are version requirements are unmet (default for most packages)} + @item{@exec{force} --- Installs the package(s) despite missing dependencies or version requirements (unsafe)} + @item{@exec{search-ask} --- Looks for the dependencies or updates via the configured @tech{package name resolvers} + (default if the dependency is an indexed name) but asks if you would like it installed or updated.} + @item{@exec{search-auto} --- Like @exec{search-ask}, but does not ask for permission to install or update.} ]} @item{@DFlag{force} --- Ignores conflicts (unsafe)} - @item{@DFlag{ignore-checksums} --- Ignores errors verifying package checksums (unsafe.)} + @item{@DFlag{ignore-checksums} --- Ignores errors verifying package @tech{checksums} (unsafe).} @item{@DFlag{link} --- Implies @exec{--type dir} (and overrides any specified type), and links the existing directory as an installed package.} @@ -259,13 +274,14 @@ listed, this command fails atomically. It accepts the following @nonterm{option} @item{@DFlag{installation} or @Flag{i} --- Same as for @exec{install}.} @item{@DFlag{shared} or @Flag{s} --- Same as for @exec{install}.} @item{@DFlag{force} --- Ignore dependencies when removing packages.} - @item{@DFlag{auto} --- Remove packages that were installed by the @exec{search-auto} and @exec{search-ask} dependency behavior that are no longer required.} + @item{@DFlag{auto} --- Remove packages that were installed by the @exec{search-auto} or @exec{search-ask} + dependency behavior and are no longer required.} ] } @item{@exec{show} @nonterm{option} ... --- Print information about currently installed packages. By default, packages are shown for all installation modes (installation-wide, - user- and version-specific, and user-specific all-version). + user- and Racket-version-specific, and user-specific all-version). The command accepts the following @nonterm{option}s: @itemlist[ @@ -398,7 +414,7 @@ first: @commandline{raco pkg create @nonterm{package}} -And then upload the archive and its checksum to your site: +And then upload the archive and its @tech{checksum} to your site: @commandline{scp @nonterm{package}.zip @nonterm{package}.zip.CHECKSUM your-host:public_html/} @@ -406,8 +422,8 @@ Now, publish your package source as: @inset{@exec{http://your-host/~@nonterm{user}/@nonterm{package}.zip}} -Whenever you want to release a new version, recreate and reupload the -package archive (and checksum). Your changes will automatically be +Whenever you want to provide a new release of a package, recreate and reupload the +package archive (and @tech{checksum}). Your changes will automatically be discovered by those who used your package source when they use @exec{raco pkg update}. @@ -459,6 +475,16 @@ when backwards incompatible changes are necessary. For example, present interfaces to external, versioned things, such as @pkgname{sqlite3} or @pkgname{libgtk2}.} +@item{A @tech{version} declaration for a package is used only by other +package implementors to effectively declare dependencies on provided +features. Such declarations allow @exec{raco pkg install} and +@exec{raco pkg update} to help check dependencies. Declaring and +changing a version is optional, and @tech{package name resolvers} +ignore version declarations; in particular, a package is a candidate +for updating when its @tech{checksum} changes, independent of whether +the package's version changes or in which direction the version +changes.} + @item{Packages should not include large sets of utilities libraries that are likely to cause conflicts. For example, packages should not contain many extensions to the @filepath{racket} collection, like @@ -491,11 +517,26 @@ The following fields are used by the package manager: @itemlist[ - @item{@racketidfont{deps} --- a list of @tech{package source} strings. - Each string determines a dependency on the @tech{package} whose name - is inferred from the @tech{package source} (i.e., dependencies are - on package names, not package sources), while the @tech{package source} indicates - where to get the package if needed to satisfy the dependency.} + @item{@racketidfont{version} --- a @tech{version} string. The default + @tech{version} of a package is @racket["0.0"].} + + @item{@racketidfont{deps} --- a list of dependencies, where each + dependency is either a @tech{package source} strings or a list + containing a @tech{package source} string and a + @tech{version} string. + + Each elements of the @racketidfont{deps} list determines a + dependency on the @tech{package} whose name is inferred from + the @tech{package source} (i.e., dependencies are on package + names, not package sources), while the @tech{package source} + indicates where to get the package if needed to satisfy the + dependency. + + When provided, a @tech{version} string specifies a lower bound + on an acceptable @tech{version} of the package. + + Use the package name @racket["racket"] to specify a dependency + on the version of the Racket installation.} @item{@racketidfont{setup-collects} --- a list of path strings and/or lists of path strings, which are used as collection names to @@ -511,6 +552,7 @@ For example, a basic @filepath{info.rkt} file might be @codeblock{ #lang setup/infotab +(define version "1.0") (define deps (list _package-source-string ...)) } @@ -578,7 +620,7 @@ requires reinstallation of all packages every version change.) @subsection{Where and how are packages installed?} -User-local and version-specific packages are in @racket[(build-path +User-local and Racket-version-specific packages are in @racket[(build-path (find-system-path 'addon-dir) (version) "pkgs")], user-local and all-version packages are in @racket[(build-path (find-system-path 'addon-dir) "pkgs")], and installation-wide packages are in @@ -597,18 +639,20 @@ conflict checks for user-specific packages. Similarly, new user-specific but all-version packages can invalidate previous user-specific conflict checks for a different Racket version. -@subsection{If packages have no version numbers, how can I update -packages with error fixes, etc?} +@subsection{Do I need to change a package's version when I update a package with error fixes, @|etc|?} -If you have a new version of the code for a package, then it will have -a new checksum. When package updates are searched for, the checksum of -the installed package is compared with the checksum of the source, if -they are different, then the source is re-installed. This allows code -changes to be distributed. +If you have new code for a package, then it should have a new +@tech{checksum}. When package updates are searched for, the checksum +of the installed package is compared with the checksum of the source, +if they are different, then the source is re-installed. This allows +code changes to be distributed. You do not need to declare an update a +version number, except to allow other package implementors to indicate +a dependency on particular features (where a bug fix might be +considered a feature, but it is not usually necessary to consider it +that way). -@subsection{If packages have no version numbers, how can I specify -which version of a package I depend on if its interface has changed -and I need an old version?} +@subsection{How can I specify which version of a package I depend on +if its interface has changed and I need an @emph{old} version?} In such a situation, the author of the package has released a backwards incompatible edition of a package. The package manager provides @@ -646,9 +690,7 @@ flexible---so that code can migrate in and out of the core, packages can easily be split up, combined, or taken over by other authors, etc. This change is bad because it makes the meaning of your program -dependent on the state of the system. (This is already true of Racket -code in general, because there's no way to make the required core -version explicit, but the problem will be exacerbated by the package manager.) +dependent on the state of the system. The second major difference is that @|Planet1| is committed to guaranteeing that packages that never conflict with one another, so diff --git a/collects/tests/planet2/test-pkgs/pkg-v-one/info.rkt b/collects/tests/planet2/test-pkgs/pkg-v-one/info.rkt new file mode 100644 index 0000000000..421a5d9c42 --- /dev/null +++ b/collects/tests/planet2/test-pkgs/pkg-v-one/info.rkt @@ -0,0 +1,3 @@ +#lang setup/infotab + +(define version "1.0") diff --git a/collects/tests/planet2/test-pkgs/pkg-v-two/info.rkt b/collects/tests/planet2/test-pkgs/pkg-v-two/info.rkt new file mode 100644 index 0000000000..1ccf1245a2 --- /dev/null +++ b/collects/tests/planet2/test-pkgs/pkg-v-two/info.rkt @@ -0,0 +1,3 @@ +#lang setup/infotab + +(define version "2.1") diff --git a/collects/tests/planet2/test-pkgs/pkg-w-one/info.rkt b/collects/tests/planet2/test-pkgs/pkg-w-one/info.rkt new file mode 100644 index 0000000000..26320f8054 --- /dev/null +++ b/collects/tests/planet2/test-pkgs/pkg-w-one/info.rkt @@ -0,0 +1,4 @@ +#lang setup/infotab + +(define deps '(("pkg-v" "2.0") + ("racket" "5.3.1.10"))) diff --git a/collects/tests/planet2/test.rkt b/collects/tests/planet2/test.rkt index 051771626f..fa3d3c1ce8 100644 --- a/collects/tests/planet2/test.rkt +++ b/collects/tests/planet2/test.rkt @@ -39,5 +39,6 @@ "planet" "update-deps" "update-auto" + "versions" "raco" "main-server") diff --git a/collects/tests/planet2/tests-versions.rkt b/collects/tests/planet2/tests-versions.rkt new file mode 100644 index 0000000000..e534271ed3 --- /dev/null +++ b/collects/tests/planet2/tests-versions.rkt @@ -0,0 +1,65 @@ +#lang racket/base +(require rackunit + racket/system + unstable/debug + racket/match + (for-syntax racket/base + syntax/parse) + racket/file + racket/runtime-path + racket/path + racket/list + planet2/util + "shelly.rkt" + "util.rkt") + +(pkg-tests + (shelly-begin + (initialize-indexes) + + (shelly-case + "create packages" + $ "raco pkg create --format zip test-pkgs/pkg-v-one" + $ "raco pkg create --format zip test-pkgs/pkg-v-two" + $ "raco pkg create --format zip test-pkgs/pkg-w-one") + + (hash-set! *index-ht-1* "pkg-v" + (hasheq 'checksum + (file->string "test-pkgs/pkg-v-one.zip.CHECKSUM") + 'source + "http://localhost:9999/pkg-v-one.zip")) + + (hash-set! *index-ht-1* "pkg-w" + (hasheq 'checksum + (file->string "test-pkgs/pkg-w-one.zip.CHECKSUM") + 'source + "http://localhost:9999/pkg-w-one.zip")) + + $ "raco pkg config --set indexes http://localhost:9990" + + (shelly-case + "update" + (shelly-begin "install pkg-v version 1.0" + $ "raco pkg install pkg-v") + (shelly-begin "fail on install pkg-w, bad version for pkg-v" + $ "raco pkg install --deps fail pkg-w" + =exit> 1 + =stderr> #rx".*version mismatch for dependency.*for package: pkg-w.*pkg-v [(]have 1[.]0, need 2[.]0[)]") + (shelly-begin "auto-update still fails" + $ "raco pkg install --deps search-auto pkg-w" + =exit> 1 + =stderr> #rx".*version mismatch for dependency.*for package: pkg-w.*pkg-v [(]have 1[.]0, need 2[.]0[)]")) + + (hash-set! *index-ht-1* "pkg-v" + (hasheq 'checksum + (file->string "test-pkgs/pkg-v-two.zip.CHECKSUM") + 'source + "http://localhost:9999/pkg-v-two.zip")) + + (shelly-case + "update" + (shelly-begin "auto-update now succeeds (installs and version matches)" + $ "raco pkg install --deps search-auto pkg-w")) + + (initialize-indexes))) +